Gin除了模型绑定还提供了模型验证功能。你可以给字段指定特定的规则标签,如果一个字段用binding:”required”标签修饰,在绑定时该字段的值为空,那么将返回一个错误。开发web api的时候大部分参数都是需要验证的,比如email参数要验证是否是邮箱格式、phone参数要验证是否是手机号格式等等,使用模型验证方法可以将验证过程隔离在业务之外。
内置的验证标签 Gin通过集成go-playground/validator提供模型验证功能,并提供了很多常用验证规则可以满足我们大部分的开发需求。我们通过一个例子看一下怎么使用这些验证标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type AddUserRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` Nickname string `json:"nickname" binding:"required"` Mobile string `json:"mobile"` Email string `json:"email" binding:"required,email"` } func AddUser (c *gin.Context) { req := sysUser.AddUserRequest{} if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error" : err.Error()}) return } ... c.String(http.StatusOK, "" ) }
在Username字段上增加了binding:”required”标签,代表gin会验证参数为必填项,如果没有为Username字段提供值ShouldBind返回的err中会包含相应的错误内容。Email字段增加了binding:”required,email”标签,这是一个组合验证,代表Email是必填项的同时还要是一个正确的邮箱格式的字符串。
下面例子可以看到在未通过模型验证时,接口返回的错误信息。
请求:
1 2 3 4 5 6 7 8 { "username" : "" , "password" : "123qwe" , "nickname" : "昵称" , "mobile" : "13322323232" , "email" : "" , "qq" : "234123412312" }
响应:
1 2 3 { "error" : "Key: 'AddUserRequest.Username' Error:Field validation for 'Username' failed on the 'required' tag\nKey: 'AddUserRequest.Email' Error:Field validation for 'Email' failed on the 'required' tag" }
自定义验证 有时候内置的验证规则可能不能满足业务需求,这样就需要自定义验证规则。大致两个步骤,1.定义一个验证方法。2.把这个方法注册为验证规则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 type Booking struct { CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` } var bookableDate validator.Func = func (fl validator.FieldLevel) bool { date, ok := fl.Field().Interface().(time.Time) if ok { today := time.Now() if today.After(date) { return false } } return true } func main () { route := gin.Default() if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterValidation("bookabledate" , bookableDate) } ... }
这里添加了一个叫做bookabledate的验证规则,验证一下参数是否大于今天。使用RegisterValidation(“bookabledate”, bookableDate)方法注册为验证规则,并且在CheckIn字段上增加了binding:”required,bookabledate”标签。如果验证失败会返回错误信息:
1 Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"
自定义错误消息 目前错误消息还是英文的,对于国内用户很不友好,接口报错的时候基本不可能把这种错误消息返回给用户看。go-playground/validator提供了错误信息的翻译,至少先解决英文错误的问题。在项目下新增validator/init.go文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package validatorimport ( "github.com/gin-gonic/gin/binding" "github.com/go-playground/locales/zh" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" zh_translations "github.com/go-playground/validator/v10/translations/zh" ) var ( uni *ut.UniversalTranslator validate *validator.Validate trans ut.Translator ) func init () { translator := zh.New() uni = ut.New(translator, translator) trans, _ = uni.GetTranslator("zh" ) validate := binding.Validator.Engine().(*validator.Validate) _ = zh_translations.RegisterDefaultTranslations(validate, trans) } func Translate (err error ) string { var result string errors := err.(validator.ValidationErrors) for _, err := range errors { result += err.Translate(trans) + ";" } return result }
在handler中调用validator.Translate方法获取错误消息的中文翻译。
1 2 3 4 5 6 7 8 9 func AddUser(c *gin.Context) { req := sysUser.AddUserRequest{} if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": validator.Translate(err)}) return } ... c.String(http.StatusOK, "") }
这样访问接口时会看到中文的错误信息:
1 2 3 { "error" : "Username为必填字段;" }