cosmos主网即将上线,对文档做了大量更新。特地翻译了一下,方便小伙伴们阅览, 之后会持续更新
第三章教程:
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"
"github.com/cosmos/cosmos-sdk/client/rest"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/sdk-application-tutorial/x/nameservice"
"github.com/gorilla/mux"
)
const (
restName = "name"
)
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")
}
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)
}
}
上述代码要注意:
- 请注意,我们使用相同的
cliCtx.QueryWithData
函数来获取数据 - 这些函数与相应的CLI功能几乎相同
Tx Handlers
现在定义buyName
和setName
交易路径:
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
}
rest.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc)
}
}
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
}
rest.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc)
}
}
注意上述代码:
BaseReq
包含用于进行交易的基本必填字段(使用哪个密钥,如何解码,使用哪条链等等)并且如所示被设计成嵌入形式。baseReq.ValidateBasic
和utils.CompleteAndBroadcastTxREST
为你设置响应代码,因此你需担心在使用这些函数时处理错误或成功。