★结构体验证
用gin框架的数据验证,可以不用解析数据,减少if else,会简洁许多
package main import ( "fmt" "github.com/gin-gonic/gin" "time" ) type Person struct { //不能为空并且大于10 Age int `form:"age" binding:"required,gt=10"` Name string `form:"name" binding:"required"` Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` } func main() { r := gin.Default() r.GET("/5lmh", func(c *gin.Context) { var person Person if err := c.ShouldBind(&person); err != nil { c.String(500, fmt.Sprint(err)) return } c.String(200, fmt.Sprintf("%#v", person)) }) r.Run() }
★自定义验证
示例一:
package main import ( "net/http" "reflect" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "gopkg.in/go-playground/validator.v8" ) /* 对绑定解析到结构体上的参数,自定义验证功能 比如我们要对 name 字段做校验,要不能为空,并且不等于 admin ,类似这种需求,就无法 binding 现成的方法 需要我们自己验证方法才能实现 官网示例(https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Custom_Functions) 这里需要下载引入下 gopkg.in/go-playground/validator.v8 */ type Person struct { Age int `form:"age" binding:"required,gt=10"` // 2、在参数 binding 上使用自定义的校验方法函数注册时候的名称 Name string `form:"name" binding:"NotNullAndAdmin"` Address string `form:"address" binding:"required"` } // 1、自定义的校验方法 func nameNotNullAndAdmin(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if value, ok := field.Interface().(string); ok { // 字段不能为空,并且不等于 admin return value != "" && !("5lmh" == value) } return true } func main() { r := gin.Default() // 3、将我们自定义的校验方法注册到 validator中 if v, ok := binding.Validator.Engine().(*validator.Validate); ok { // 这里的 key 和 fn 可以不一样最终在 struct 使用的是 key v.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin) } /* curl -X GET "http://127.0.0.1:8080/testing?name=&age=12&address=beijing" curl -X GET "http://127.0.0.1:8080/testing?name=lmh&age=12&address=beijing" curl -X GET "http://127.0.0.1:8080/testing?name=adz&age=12&address=beijing" */ r.GET("/5lmh", func(c *gin.Context) { var person Person if e := c.ShouldBind(&person); e == nil { c.String(http.StatusOK, "%v", person) } else { c.String(http.StatusOK, "person bind err:%v", e.Error()) } }) r.Run() }
示例二:
package main import ( "net/http" "reflect" "time" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "gopkg.in/go-playground/validator.v8" ) // Booking contains binded and validated data. type Booking struct { //定义一个预约的时间大于今天的时间 CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` //gtfield=CheckIn退出的时间大于预约的时间 CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` } func bookableDate( v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, ) bool { //field.Interface().(time.Time)获取参数值并且转换为时间格式 if date, ok := field.Interface().(time.Time); ok { today := time.Now() if today.Unix() > date.Unix() { return false } } return true } func main() { route := gin.Default() //注册验证 if v, ok := binding.Validator.Engine().(*validator.Validate); ok { //绑定第一个参数是验证的函数第二个参数是自定义的验证函数 v.RegisterValidation("bookabledate", bookableDate) } route.GET("/5lmh", getBookable) route.Run() } func getBookable(c *gin.Context) { var b Booking if err := c.ShouldBindWith(&b, binding.Query); err == nil { c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) } else { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }
★多语言翻译验证
当业务系统对验证信息有特殊需求时,例如:返回信息需要自定义,手机端返回的信息需要是中文而pc端发挥返回的信息需要时英文,如何做到请求一个接口满足上述三种情况。
package main import ( "fmt" "github.com/gin-gonic/gin" "github.com/go-playground/locales/en" "github.com/go-playground/locales/zh" "github.com/go-playground/locales/zh_Hant_TW" ut "github.com/go-playground/universal-translator" "gopkg.in/go-playground/validator.v9" en_translations "gopkg.in/go-playground/validator.v9/translations/en" zh_translations "gopkg.in/go-playground/validator.v9/translations/zh" zh_tw_translations "gopkg.in/go-playground/validator.v9/translations/zh_tw" ) var ( Uni *ut.UniversalTranslator Validate *validator.Validate ) type User struct { Username string `form:"user_name" validate:"required"` Tagline string `form:"tag_line" validate:"required,lt=10"` Tagline2 string `form:"tag_line2" validate:"required,gt=1"` } func main() { en := en.New() zh := zh.New() zh_tw := zh_Hant_TW.New() Uni = ut.New(en, zh, zh_tw) Validate = validator.New() route := gin.Default() route.GET("/5lmh", startPage) route.POST("/5lmh", startPage) route.Run(":8080") } func startPage(c *gin.Context) { //这部分应放到中间件中 locale := c.DefaultQuery("locale", "zh") trans, _ := Uni.GetTranslator(locale) switch locale { case "zh": zh_translations.RegisterDefaultTranslations(Validate, trans) break case "en": en_translations.RegisterDefaultTranslations(Validate, trans) break case "zh_tw": zh_tw_translations.RegisterDefaultTranslations(Validate, trans) break default: zh_translations.RegisterDefaultTranslations(Validate, trans) break } //自定义错误内容 Validate.RegisterTranslation("required", trans, func(ut ut.Translator) error { return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details }, func(ut ut.Translator, fe validator.FieldError) string { t, _ := ut.T("required", fe.Field()) return t }) //这块应该放到公共验证方法中 user := User{} c.ShouldBind(&user) fmt.Println(user) err := Validate.Struct(user) if err != nil { errs := err.(validator.ValidationErrors) sliceErrs := []string{} for _, e := range errs { sliceErrs = append(sliceErrs, e.Translate(trans)) } c.String(200, fmt.Sprintf("%#v", sliceErrs)) } c.String(200, fmt.Sprintf("%#v", "user")) }