深色模式
go-zero定制handler返回数据
go-zero的goctl工具,对生成的xxhandler.go
的代码进行定制。
默认行为
默认模板,生成的handler,返回的json是类似这样的:
json
{
"result": 100
}
我希望它是这样的:
json
{
"code": 0,
"msg": "success",
"result": 100
}
解决办法,就是修改goctl工具的模板。
准备工作
定义错误码
在logic
目录中,新建errors.go
:
go
package logic
import "fmt"
// 错误分为以下几类
// - 成功
// - 不规范错误
// - 规范错误
// - 未使用的错误
const (
ErrOk = iota + 0
)
const (
ErrUnspecified = -1 // 不规范错误,未分类的错误
)
const (
ErrInvalidToken = iota + 100
ErrInvalidParam
ErrNoPermission
)
const (
ErrInternalError = iota + 500
)
// 此类型代表是规范错误
type GyError struct {
Code int64 `json:"code"`
Msg string `json:"msg"`
}
func (e GyError) Error() string {
return e.Msg
}
// 使用默认message
func NewGyError(code int64) GyError {
return GyError{
Code: code,
Msg: defaultMessage(code),
}
}
// 指定message
func NewGyErrorReplaceMsg(code int64, message string) GyError {
return GyError{
Code: code,
Msg: message,
}
}
// 追加message
func NewGyErrorAppendMsg(code int64, message string) GyError {
return GyError{
Code: code,
Msg: fmt.Sprintf("%s >> %s", defaultMessage(code), message),
}
}
// 默认的->错误码说明信息
func defaultMessage(code int64) string {
var msg string
switch code {
case ErrOk:
msg = "success"
case ErrUnspecified:
msg = "[unspecified error]"
case ErrInvalidToken:
msg = "token不合法"
case ErrInvalidParam:
msg = "参数不合法"
case ErrNoPermission:
msg = "无权限"
case ErrInternalError:
msg = "内部错误"
default:
msg = "未使用"
}
return msg
}
编写响应函数
在internal
中新建一个文件夹hresp
(取这个名字是因为我希望它和handler
挨在一起),在hresp
中新建hresp.go
文件,文件内容如下:
go
// Response
// 如果没有错误,err传空
// 如果有错误:err非空
// - 非规范错误:未指定错误码
// - 规范错误:根据错误类型,指定错误码
func Response(w http.ResponseWriter, resp any, err error) {
var body Body
var gyError logic.GyError
if err == nil {
// 1. [没有错误]
gyError = logic.NewGyError(logic.ErrOk)
} else {
var target logic.GyError
if !errors.As(err, &target) {
// 2.1 [不规范错误]
gyError = logic.NewGyErrorAppendMsg(logic.ErrUnspecified, err.Error())
} else {
// 2.2 [规范错误]
gyError = target
}
}
body = Body{
Code: gyError.Code,
Msg: gyError.Msg,
Result: resp,
}
httpx.OkJson(w, body)
}
调用函数
调用上面的Response()
函数,即可返回我们想要的json结构。
所以,我们通过修改goctl的模板文件,让goctl生成的代码去调用Response()
函数,而不必我们手写调用。
定制
修改模板
执行命令:
sh
goctl template init
会在~/.goctl
中初始化默认的模板文件。
找到api/handler.tpl
文件,它的内容是:
go
package {{.PkgName}}
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
{{.ImportPackages}}
)
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}}
}
}
}
将其修改为:
go
package {{.PkgName}}
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
{{.ImportPackages}}
)
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 {
hresp.Response(w, nil, err)
return
}
{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
{{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
{{if .HasResp}}hresp.Response(w, resp, err){{else}}hresp.Response(w, nil, err){{end}}
}
}
生成代码
使用goctl api go
即可生成想要的代码。
sh
goctl api go --api app/api.api --dir app --style go_zero
handler的代码,从默认的:
go
func AppHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.Request
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := logic.NewAppLogic(r.Context(), svcCtx)
resp, err := l.App(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
变为:
go
func AppHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.Request
if err := httpx.Parse(r, &req); err != nil {
hresp.Response(w, nil, err)
return
}
l := logic.NewAppLogic(r.Context(), svcCtx)
resp, err := l.App(&req)
hresp.Response(w, resp, err)
}
}