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

运行成功,看看效果。

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

相关推荐
资深web全栈开发1 小时前
并查集(Union-Find)套路详解
leetcode·golang·并查集·unionfind
moxiaoran57533 小时前
Go语言的递归函数
开发语言·后端·golang
朝花不迟暮3 小时前
Go基础-闭包
android·开发语言·golang
西京刀客5 小时前
go语言-切片排序之sort.Slice 和 sort.SliceStable 的区别(数据库分页、内存分页场景注意点)
后端·golang·sort·数据库分页·内存分页
黄昏单车6 小时前
golang语言基础到进阶学习笔记
笔记·golang·go
moxiaoran575316 小时前
Go语言结构体
开发语言·后端·golang
Tony Bai1 天前
Cloudflare 2025 年度报告发布——Go 语言再次“屠榜”API 领域,AI 流量激增!
开发语言·人工智能·后端·golang
小徐Chao努力1 天前
Go语言核心知识点底层原理教程【变量、类型与常量】
开发语言·后端·golang
锥锋骚年1 天前
go语言异常处理方案
开发语言·后端·golang
moxiaoran57531 天前
Go语言的map
开发语言·后端·golang