# NameService 模块的 REST 接口

你的模块还可以公开 REST 接口,提供程序访问模块的功能。。首先创建一个文件来保存HTTP的handler:

  • ./x/nameservice/client/rest/rest.go

引入模块和定义常量:

你的应用程序需要导入你刚编写的代码。这里导入路径设置为此存储库(github.com/cosmos/sdk-application-tutorial/nameservice/x/nameservice)。如果您是在自己的仓库中进行的前面的操作,则需要更改导入路径(github.com/{.Username}/{.Project.Repo}/x/nameservice)。

Copy 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-tutorials/nameservice/x/nameservice" "github.com/gorilla/mux" ) const ( restName = "name" )

# RegisterRoutes

首先在RegisterRoutes函数中为模块定义REST客户端接口。路由都以模块名称开头,以防止命名空间与其他模块的路径冲突:

Copy // 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") }

# Query Handlers

接下来,是时候定义上面提到的处理程序了。这些与前面定义的CLI方法非常相似。首先开始whoisresolve查询:

Copy 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) } }

上述代码要注意:

  • 请注意,我们使用相同的cliCtx.QueryWithData函数来获取数据
  • 这些函数与相应的CLI功能几乎相同

# Tx Handlers

现在定义 buyName 和 setName 交易路由。 请注意,这些实际上并不能将交易发送进行买入和设置名称。 这需要跟交易请求一起发送发送密码,将是一个安全问题。 相反,这些端点构建并返回每个特定交易,然后可以以安全的方式对其进行签名,然后使用标准端点(如/ txs)将其广播到网络。

Copy 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}) } }

注意上述代码:

  • BaseReq包含用于进行交易的基本必填字段(使用哪个密钥,如何解码,使用哪条链等等)并且如所示被设计成嵌入形式。
  • baseReq.ValidateBasicutils.CompleteAndBroadcastTxREST为你设置响应代码,因此你需担心在使用这些函数时处理错误或成功。

# 现在你的模块已经具备了全部功能,需要与 Cosmos SDK 应用合并