Gin Validator 错误信息翻译与自定义校验规则详解
前言
在使用 Go 语言的 Gin 框架开发 Web 应用时,数据校验是保障接口数据完整性和安全性的关键环节。Gin 集成了 go-playground/validator/v10 作为其默认的校验引擎,提供了强大且灵活的结构体字段验证能力。然而,其默认的英文错误信息对中文用户不够友好,且内置规则无法覆盖所有业务场景。
本文将深入探讨 Gin Validator 的两大核心功能:错误信息的翻译(国际化) 和 自定义校验规则的实现,帮助你构建更健壮、更用户友好的 API 接口。
一、前置准备
确保项目中已安装必要的依赖:
go get -u github.com/gin-gonic/gin
go get -u github.com/go-playground/validator/v10
go get -u github.com/go-playground/validator/v10/translations/zh
说明:zh 包用于支持中文翻译。
二、基础校验回顾
Gin 使用结构体标签(binding)进行数据校验。例如:
type LoginReq struct {Username string `json:"username" binding:"required,min=3,max=11"`Password string `json:"password" binding:"required,min=6"`
}
当数据不满足规则时,Gin 会返回类似 "Key: 'LoginReq.Username' Error:Field validation for 'Username' failed on the 'required' tag"
的英文错误信息。我们需要将其转换为中文,并支持自定义规则。
三、错误信息翻译(国际化)
为了让错误提示更友好,我们需要将默认的英文错误信息翻译成中文。
1. 初始化翻译器
首先,我们需要创建一个翻译器实例,并注册中文翻译支持。
import ("errors""gin/global""github.com/gin-gonic/gin/binding""github.com/go-playground/locales/en""github.com/go-playground/locales/zh"ut "github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"en_translations "github.com/go-playground/validator/v10/translations/en"zh_translations "github.com/go-playground/validator/v10/translations/zh""reflect""strings"
)func InitTrans(locale string) (err error) {if v, ok := binding.Validator.Engine().(*validator.Validate); ok {zh := zh.New()en := en.New()uni := ut.New(zh, en)// 获取翻译器global.Trans, ok = uni.GetTranslator(locale)if !ok {return errors.New("不支持的语言:" + locale)}switch locale {case "zh":err = zh_translations.RegisterDefaultTranslations(v, global.Trans)case "en":err = en_translations.RegisterDefaultTranslations(v, global.Trans)default:err = zh_translations.RegisterDefaultTranslations(v, global.Trans)}if err != nil {return err}// 注册自定义标签名函数, 用于获取结构体标签中的json字段名v.RegisterTagNameFunc(func(fld reflect.StructField) string {name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]if name == "-" {return ""}return name})return nil
}
2. 使用翻译后的错误信息
在处理请求时,捕获校验错误并翻译:
// removeTopStruct 移除字段名中的结构体名称
func removeTopStruct(fields map[string]string) map[string]string {res := map[string]string{}for field, err := range fields {res[field[strings.Index(field, ".")+1:]] = err}return res
}
func handleResponseValidateErr(c *gin.Context, err error) {// 断言错误类型if errs, ok := err.(validator.ValidationErrors); ok {c.JSON(http.StatusBadRequest, gin.H{"message": removeTopStruct(errs.Translate(global.Trans))})return}c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
}func Login(c *gin.Context) {var req LoginReqerr := c.ShouldBindJSON(&req)if err != nil {// c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})handleResponseValidateErr(c, err)return}c.JSON(http.StatusOK, gin.H{"message": "success","code": 0,"token": "Abcd123456FGH",})
}
此时,错误信息将显示为中文,如 “Username 为必填字段” 或 “Password 长度必须至少为 6 个字符”。
四、自定义校验规则
虽然 validator 提供了丰富的内置规则,但在实际开发中,我们常需要自定义校验逻辑,如校验手机号、身份证、用户名格式等。
1. 定义自定义校验函数
以校验是否包含敏感词为例:
// 自定义验证函数
func validateSensitiveWord(fl validator.FieldLevel) bool {sensitiveWords := []string{"fuck", "shit", "fucking"}value := fl.Field().String()for _, word := range sensitiveWords {if strings.Contains(value, word) {return false}}return true
}
2. 注册自定义规则
在应用启动时,将自定义函数注册到 validator 引擎中:
func init() {if v, ok := binding.Validator.Engine().(*validator.Validate); ok {// 注册自定义规则 sensitiveif err := v.RegisterValidation("sensitive", sensitiveWord); err != nil {return err}// 注册中文翻译if err := v.RegisterTranslation("sensitive", global.Trans,func(ut ut.Translator) error {return ut.Add("sensitive", "{0}包含敏感字符", true)},func(ut ut.Translator, fe validator.FieldError) string {t, _ := ut.T("sensitive", fe.Field())return t},); err != nil {return err}}
}
3. 在结构体中使用
type RegisterReq struct {UserName string `json:"name" binding:"required,sensitive" label:"姓名"`Password string `json:"password" binding:"required,min=6,max=15" label:"密码"`
}
五、其他
-
多语言支持
通过 universal-translator 可轻松支持多语言。根据请求头 Accept-Language 动态选择翻译器。 -
全局错误处理中间件
将错误翻译逻辑封装为中间件,避免重复代码:
func ErrorHandler() gin.HandlerFunc {return func(c *gin.Context) {c.Next()if len(c.Errors) > 0 {err := c.Errors[0].Errif errs, ok := err.(validator.ValidationErrors); ok {var msgs []stringfor _, e := range errs {msgs = append(msgs, e.Translate(trans))}c.JSON(400, gin.H{"errors": msgs})return}c.JSON(400, gin.H{"error": err.Error()})}}
}
示例代码
gitee