文章目录
- 问题背景
- 解决方案:自定义模板
-
- [1. 初始化模板](#1. 初始化模板)
- [2. 定位 Handler 模板](#2. 定位 Handler 模板)
- [3. 修改模板内容](#3. 修改模板内容)
- 总结
在使用 go-zero 框架开发 API 服务时,我们经常需要在每个 handler 中添加参数验证逻辑。本文将介绍如何通过自定义模板实现自动化代码生成,避免重复的手动修改。
问题背景
在标准的 goctl 代码生成过程中,生成的 handler 文件通常只包含基本的请求解析和业务逻辑调用:
生成的命令:goctl api go -api project.api -dir . --style go_zero
go
func GetOpeningBankHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetOpeningBankRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := settlement.NewGetOpeningBankLogic(r.Context(), svcCtx)
resp, err := l.GetOpeningBank(&req)
// ...
}
}
但实际项目中,我们往往需要添加统一的参数验证逻辑:
go
// 验证请求参数
if err := middleware.ValidateRequest(&req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
参数验证的方法:
go
// internal/middleware/validator_middleware.go
package middleware
// 处理验证错误
func handleValidationErrors(err validator.ValidationErrors) string {
var errorMessages []string
for _, e := range err {
errorMessages = append(errorMessages, e.Translate(trans))
}
return strings.Join(errorMessages, ", ")
}
// ValidateRequest 验证请求参数
func ValidateRequest(req interface{}) error {
if err := validate.Struct(req); err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
logx.Errorf("Invalid validation error: %v", err)
return types.NewMyError(constants.CodeParamsValidationFail.Code, constants.CodeParamsValidationFail.Msg+": 验证器配置错误")
}
errMessages := handleValidationErrors(err.(validator.ValidationErrors))
return types.NewMyError(constants.CodeParamsValidationFail.Code, errMessages)
}
return nil
}
解决方案:自定义模板
1. 初始化模板
首先初始化 go-zero 模板:
goctl template init
2. 定位 Handler 模板
找到 handler.tpl 模板文件,通常位于 ~/.goctl/ 目录下,不同的go-zero框架的模板文件位置不同。我的是1.8.4
版本,执行goctl template init
命令后,打印内容如下:
进入到对应的目录,可以看到这里面有很多go-zero定义好的模板文件。这里,要修改的是handler.tpl
,记得提前备份一下,防止改错。
3. 修改模板内容
在模板中添加参数验证逻辑,别忘了 import
部分:
go
package {{.PkgName}}
import (
"project/internal/middleware"
"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
}
// 验证请求参数
if err := middleware.ValidateRequest(&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}}
}
}
}
这样,生成的 handler 文件就会自动加上我们配置好的内容啦:
go-zero的自动生成的其它文件,应该同理,可以自行扩展。
总结
通过自定义 goctl 模板,我们可以实现:
✅ 自动化代码生成
✅ 统一的参数验证逻辑
✅ 避免手动重复修改
✅ 保持代码风格一致性
这种方法特别适用于需要在所有 handler 中添加通用逻辑的场景,大大提高了开发效率和代码质量。