Go-zero框架修改模版进行handler统一响应封装

使用go-zero快速生成接口的时候,发现还是有一些情况不太好处理,比如说,想要自定义响应封装等等。

最开始第一版写api文件的时候,写法是这样的。

python 复制代码
type LoginRequest {
  UserName string `json:"userName"`
  Password string `json:"password"`
}

type Response {
  Code int    `json:"code"`
  Data string `json:"data"`
  Msg  string `json:"msg"`
}

type UserInfo {
  UserName string `json:"userName"`
  Addr     string `json:"addr"`
  Id       uint   `json:"id"`
}

type UserInfoResponse {
  Code int      `json:"code"`
  Data UserInfo `json:"data"`
  Msg  string   `json:"msg"`
}

service users {
  @handler login
  post /api/users/login (LoginRequest) returns (Response)
  
  @handler userInfo
  get /api/users/info returns (UserInfoResponse)
}

// goctl api go -api v1.api -dir .

后面发现可以不把code、data、msg这三个重要信息写在api里边,而是通过统一封装,在统一响应中去加上code、data、msg,使之成为我们一个公共的库供我们使用。

首先我们封装好response文件:

go 复制代码
package response

import (
	"github.com/zeromicro/go-zero/rest/httpx"
	"net/http"
)

type Body struct {
	Code uint32      `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

// Response http返回
func Response(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
	if err == nil {
		//成功返回
		r := &Body{
			Code: 0,
			Msg:  "成功",
			Data: resp,
		}
		httpx.WriteJson(w, http.StatusOK, r)
		return
	}
	//错误返回
	errCode := uint32(7)
	// 可以根据错误码,返回具体错误信息
	//errMsg := "服务器错误"

	httpx.WriteJson(w, http.StatusOK, &Body{
		Code: errCode,
		Msg:  err.Error(),
		Data: nil,
	})

}

此时还需要考虑问题,假设我们只是需要这个项目使用统一的封装,不希望后面的go-zero项目受到影响,那么我们可以通过直接在本地生成模版文件去给当前这个项目使用。

然后我看了go-zero的官方文档,发现确实可以这么操作,ok,尝试一波。

首先,通过在本地(项目路径下)生成模版文件:

go 复制代码
goctl template init --home template

然后可以看到提示成功了,并且生成了下面的模版。

下面是原api中的hanlder.tpl文件的代码:

go 复制代码
package {{.PkgName}}

import (
	"net/http"

	"github.com/zeromicro/go-zero/rest/httpx"
	{{.ImportPackages}}
)

{{if .HasDoc}}{{.Doc}}{{end}}
func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		{{if .HasRequest}}var req types.{{.RequestType}}
		if err := httpx.Parse(r, &req); err != nil {
			httpx.ErrorCtx(r.Context(), w, err)
			return
		}

		{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
		{{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
		if err != nil {
			httpx.ErrorCtx(r.Context(), w, err)
		} else {
			{{if .HasResp}}httpx.OkJsonCtx(r.Context(), w, resp){{else}}httpx.Ok(w){{end}}
		}
	}
}

修改后的tpl文件如下:

go 复制代码
package handler

import (
    "net/http"
    "fim/common/response"
    {{.ImportPackages}}
    {{if .HasRequest}}
    "github.com/zeromicro/go-zero/rest/httpx"
    {{end}}
)

func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        {{if .HasRequest}}var req types.{{.RequestType}}
        if err := httpx.Parse(r, &req); err != nil {
             response.Response(r, w, nil, err)
            return
        }{{end}}

        l := logic.New{{.LogicType}}(r.Context(), svcCtx)
        {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
        {{if .HasResp}}response.Response(r, w, resp, err){{else}}response.Response(w, nil, err){{end}}

    }
}

简单来分析下这个代码逻辑,就是使用了模板引擎的语法(如 {``{.HandlerName}}{``{if .HasRequest}}),这些模板变量和条件判断在代码生成时会被具体的值替换。

首先,"fim/common/response":项目中自定义的模块,提供了统一的响应处理方法。

然后是:{``{.ImportPackages}}:模板变量,表示在代码生成时会根据需要动态导入其他必要的包。

go 复制代码
    {{.ImportPackages}}
    {{if .HasRequest}}
    "github.com/zeromicro/go-zero/rest/httpx"
    {{end}}

func {``{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc{``{.HandlerName}} 是模板变量,表示具体的处理函数名称。例如,它可能会被替换为 LoginHandler

svcCtx *svc.ServiceContext:服务上下文,包含了依赖注入的配置和数据库连接等信息。然后返回handlerFunc


如果当前接口需要解析请求体({``{if .HasRequest}}),则会执行以下操作:定义一个变量 req,类型为 types.{``{.RequestType}}{``{.RequestType}} 是模板变量,表示请求体的类型,例如 LoginRequest

go 复制代码
{{if .HasRequest}}var req types.{{.RequestType}}
        if err := httpx.Parse(r, &req); err != nil {
             response.Response(r, w, nil, err)
            return
        }{{end}}

使用 httpx.Parse(r, &req) 解析 HTTP 请求体到 req 中。如果解析失败(err != nil),调用 response.Response 方法返回错误响应,并结束处理。


然后是进行逻辑处理:创建一个业务逻辑对象 l,类型为 logic.New{``{.LogicType}}{``{.LogicType}} 是模板变量,表示具体的业务逻辑类型,例如 LoginLogic。

调用业务逻辑对象的 {``{.Call}} 方法({{.Call}} 是模板变量,表示具体的业务逻辑方法名,例如 Login)。

go 复制代码
       l := logic.New{{.LogicType}}(r.Context(), svcCtx)
        {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
        {{if .HasResp}}response.Response(r, w, resp, err){{else}}response.Response(w, nil, err){{end}}

如果接口有返回值({``{if .HasResp}}),则将返回值存储到 resp 中;否则,仅处理错误。

如果接口有返回值({``{if .HasResp}}),调用 response.Response(r, w, resp, err),将业务逻辑的返回值作为响应数据返回。

如果接口没有返回值,仅调用 response.Response(w, nil, err),返回错误信息或成功状态。


然后把原来的api生成的handler和logic删除,然后重新生成一下。注意选对好对应目录下的template(这里我是在auth_api路径下去运行这个命令,也就是跟api文件同级目录下去运行)。

go 复制代码
goctl api go -api auth_api.api -dir . --home ../../template

运行成功,看看效果。

这个时候发现生成的接口函数那些就没问题了。

相关推荐
RationalDysaniaer2 小时前
gorm基础:自定义数据类型
golang
web守墓人2 小时前
【gpt生成-其一】以go语言为例,详细描述一下 :语法规范BNF/EBNF形式化描述
前端·gpt·golang
yoke菜籽2 小时前
k8s报错kubelet.go:2461] “Error getting node“ err=“node \“k8s-master\“ not found“
golang·kubernetes·kubelet
Villiam_AY5 小时前
go语言对http协议的支持
开发语言·http·golang
虽千万人 吾往矣6 小时前
golang context源码
android·开发语言·golang
Delphi菜鸟15 小时前
go环境安装mac
开发语言·后端·golang
Delphi菜鸟17 小时前
go+mysql+cocos实现游戏搭建
mysql·游戏·golang·gin·cocos2d
why15118 小时前
字节头条golang二面
开发语言·后端·golang