校验错误翻译与自定义校验器

一句话结论:Gin 默认用 go-playground/validator 做结构体校验(binding:"required,min=..."),但原始错误是英文、字段名取结构体字段名,直接抛给用户不可读。两条路:① 翻译——用官方 translations/zh 按 tag 注册翻译器,并用 RegisterTagNameFunc 让错误字段名取 json tag;② 自定义校验器——通过 binding.Validator.Engine() 拿到底层 *validator.Validate,RegisterValidation 注册业务规则(手机号、枚举、时间区间)。所有校验失败统一收敛到一个“参数错误”响应里(配合错误码设计),handler 里不散落翻译逻辑。

字段名与翻译

v, _ := binding.Validator.Engine().(*validator.Validate)

// 1. 错误里显示 json tag 而非字段名
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
    return fld.Tag.Get("json")
})

// 2. 注册中文翻译(官方翻译包:go-playground/validator/translations/zh)
zhT := zh.New()
trans, _ := ut.New(zhT, zhT).GetTranslator("zh")
for tag, fn := range map[string]func(ut.Translator) error{
    "required": zhT.RegisterTranslation, // 示意:每个 tag 都要注册
    "min":      zhT.RegisterTranslation,
} {
    v.RegisterTranslation(tag, trans, registrationFunc(tag), translateFunc(tag))
}

说明:官方 zh 包提供翻译函数,但需要为用到的每个 tag(required/min/max/email…)逐个 RegisterTranslation,没有“注册一次全部生效”的开关。

自定义校验器

var mobileRe = regexp.MustCompile(`^1[3-9]\d{9}$`)

func init() {
    v, _ := binding.Validator.Engine().(*validator.Validate)
    _ = v.RegisterValidation("mobile", func(fl validator.FieldLevel) bool {
        return mobileRe.MatchString(fl.Field().String())
    })
}

type Req struct {
    Phone string `json:"phone" binding:"required,mobile"`
}

更复杂的跨字段/结构体规则用 RegisterStructValidation 或 validator 的 div/gtfield 等内置关联规则。

统一翻译入口

func BindAndCheck(c *gin.Context, obj any) bool {
    if err := c.ShouldBindJSON(obj); err != nil {
        var verrs validator.ValidationErrors
        if errors.As(err, &verrs) {
            // verrs.Translate(trans) 得到可读中文;统一 400 响应
        }
        return false
    }
    return true
}

追问记忆点

  • 追问:为什么错误显示的是结构体字段名而不是 JSON 字段?——validator 默认用 Field.Name,需要 RegisterTagNameFunc 读 json tag。
  • 追问:自定义校验器注册时机?——main/init 阶段一次即可,注册后全局生效;validator 会缓存 struct 规则,别在请求路径里注册。
  • 追问:Required 对零值(0/"")的语义?——required 拒绝零值;要允许零值用指针字段 + omitempty 组合设计。
  • 记忆点:翻译=zh 包按 tag 注册 + json tag 字段名;扩展=RegisterValidation/RegisterStructValidation;错误统一转 400 参数错误码。
笔记加载中…