NameService 模块的 REST 接口
你的模块还可以公开 REST 接口,提供程序访问模块的功能。。首先创建一个文件来保存HTTP的handler:
./x/nameservice/client/rest/rest.go
引入模块和定义常量:
你的应用程序需要导入你刚编写的代码。这里导入路径设置为此存储库(github.com/cosmos/sdk-application-tutorial/x/nameservice)。如果您是在自己的仓库中进行的前面的操作,则需要更改导入路径(github.com/{.Username}/{.Project.Repo}/x/nameservice)。
package rest
import (
"fmt"
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
clientrest "github.com/cosmos/cosmos-sdk/client/rest"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/sdk-application-tutorial/x/nameservice"
"github.com/gorilla/mux"
)
const (
restName = "name"
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
RegisterRoutes
首先在RegisterRoutes
函数中为模块定义REST客户端接口。路由都以模块名称开头,以防止命名空间与其他模块的路径冲突:
// RegisterRoutes - Central function to define routes that get registered by the main application
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) {
r.HandleFunc(fmt.Sprintf("/%s/names", storeName), buyNameHandler(cdc, cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/%s/names", storeName), setNameHandler(cdc, cliCtx)).Methods("PUT")
r.HandleFunc(fmt.Sprintf("/%s/names/{%s}", storeName, restName), resolveNameHandler(cdc, cliCtx, storeName)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/%s/names/{%s}/whois", storeName, restName), whoIsHandler(cdc, cliCtx, storeName)).Methods("GET")
}
1
2
3
4
5
6
7
2
3
4
5
6
7
Query Handlers
接下来,是时候定义上面提到的处理程序了。这些与前面定义的CLI方法非常相似。首先开始whois
和resolve
查询:
func resolveNameHandler(cdc *codec.Codec, cliCtx context.CLIContext, storeName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
paramType := vars[restName]
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/resolve/%s", storeName, paramType), nil)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}
func whoIsHandler(cdc *codec.Codec, cliCtx context.CLIContext, storeName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
paramType := vars[restName]
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/whois/%s", storeName, paramType), nil)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}
func namesHandler(cdc *codec.Codec, cliCtx context.CLIContext, storeName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/names", storeName), nil)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
上述代码要注意:
- 请注意,我们使用相同的
cliCtx.QueryWithData
函数来获取数据 - 这些函数与相应的CLI功能几乎相同
Tx Handlers
现在定义 buyName 和 setName 交易路由。 请注意,这些实际上并不能将交易发送进行买入和设置名称。 这需要跟交易请求一起发送发送密码,将是一个安全问题。 相反,这些端点构建并返回每个特定交易,然后可以以安全的方式对其进行签名,然后使用标准端点(如/ txs)将其广播到网络。
type buyNameReq struct {
BaseReq rest.BaseReq `json:"base_req"`
Name string `json:"name"`
Amount string `json:"amount"`
Buyer string `json:"buyer"`
}
func buyNameHandler(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req buyNameReq
if !rest.ReadRESTReq(w, r, cdc, &req) {
rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
return
}
baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) {
return
}
addr, err := sdk.AccAddressFromBech32(req.Buyer)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
coins, err := sdk.ParseCoins(req.Amount)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// create the message
msg := nameservice.NewMsgBuyName(req.Name, coins, addr)
err = msg.ValidateBasic()
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, baseReq, []sdk.Msg{msg})
}
}
type setNameReq struct {
BaseReq rest.BaseReq `json:"base_req"`
Name string `json:"name"`
Value string `json:"value"`
Owner string `json:"owner"`
}
func setNameHandler(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req setNameReq
if !rest.ReadRESTReq(w, r, cdc, &req) {
rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
return
}
baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) {
return
}
addr, err := sdk.AccAddressFromBech32(req.Owner)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// create the message
msg := nameservice.NewMsgSetName(req.Name, req.Value, addr)
err = msg.ValidateBasic()
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, baseReq, []sdk.Msg{msg})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
注意上述代码:
BaseReq
包含用于进行交易的基本必填字段(使用哪个密钥,如何解码,使用哪条链等等)并且如所示被设计成嵌入形式。baseReq.ValidateBasic
和utils.CompleteAndBroadcastTxREST
为你设置响应代码,因此你需担心在使用这些函数时处理错误或成功。