前言
bindingvalidate
本人翻译了一份gin官网中文文档。关注公众号[Golang梦工厂](扫描下方二维码),后盾回复:gin,即可获取。
疾速装置
validator
# 第一次装置应用如下命令$ go get github.com/go-playground/validator/v10# 我的项目中引入包import "github.com/go-playground/validator/v10"
简略示例
装置还是很简略的,上面我先来一个官网样例,看看是怎么应用的,而后开展剖析。
package mainimport ( "fmt" "net/http" "github.com/gin-gonic/gin")type RegisterRequest struct { Username string `json:"username" binding:"required"` Nickname string `json:"nickname" binding:"required"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` Age uint8 `json:"age" binding:"gte=1,lte=120"`}func main() { router := gin.Default() router.POST("register", Register) router.Run(":9999")}func Register(c *gin.Context) { var r RegisterRequest err := c.ShouldBindJSON(&r) if err != nil { fmt.Println("register failed") c.JSON(http.StatusOK, gin.H{"msg": err.Error()}) return } //验证 存储操作省略..... fmt.Println("register success") c.JSON(http.StatusOK, "successful")}
- 测试
curl --location --request POST 'http://localhost:9999/register' \--header 'Content-Type: application/json' \--data-raw '{ "username": "asong", "nickname": "golang梦工厂", "email": "7418.com", "password": "123", "age": 140}'
- 返回后果
{ "msg": "Key: 'RegisterRequest.Email' Error:Field validation for 'Email' failed on the 'email' tag\nKey: 'RegisterRequest.Age' Error:Field validation for 'Age' failed on the 'lte' tag"}
validator
validator库
gin框架是应用validator.v10这个库来进行参数验证的,所以咱们先来看看这个库的应用。
先装置这个库:
$ go get github.com/go-playground/validator/v10
而后先写一个简略的示例:
package mainimport ( "fmt" "github.com/go-playground/validator/v10")type User struct { Username string `validate:"min=6,max=10"` Age uint8 `validate:"gte=1,lte=10"` Sex string `validate:"oneof=female male"`}func main() { validate := validator.New() user1 := User{Username: "asong", Age: 11, Sex: "null"} err := validate.Struct(user1) if err != nil { fmt.Println(err) } user2 := User{Username: "asong111", Age: 8, Sex: "male"} err = validate.Struct(user2) if err != nil { fmt.Println(err) }}
validator.New()Struct()
下面的例子,咱们在User构造体中,有三个字段:
- Name:通过min和max来进行束缚,Name的字符串长度为[6,10]之间。
- Age:通过gte和lte对年老的范畴进行束缚,age的大小大于1,小于10。
- Sex:通过oneof对值进行束缚,只能是所列举的值,oneof列举出性别为男士????和女士????(不是硬性规定奥,可能还有别的性别)。
user1
Key: 'User.Name' Error:Field validation for 'Name' failed on the 'min' tagKey: 'User.Age' Error:Field validation for 'Age' failed on the 'lte' tagKey: 'User.Sex' Error:Field validation for 'Sex' failed on the 'oneof' tag
各个字段违反了什么束缚,一眼咱们便能从错误信息中看进去。看完了简略示例,上面我就来看一看都有哪些tag,咱们都能够怎么应用。本文不介绍所有的tag,更多应用办法,请到官网文档自行学习。
字符串束缚
excludesallexcludesall=abexcludesruneexcludesrune=asongstartswithstartswith=hiendswithendswith=byecontains=contains=emailcontainsanycontainsany=abcontainsruneexcludesexcludes=email
范畴束缚
范畴束缚的字段类型分为三种:
- 对于数值,咱们则能够束缚其值
- 对于切片、数组和map,咱们则能够束缚其长度
- 对于字符串,咱们则能够束缚其长度
罕用tag介绍:
nene=5gtgt=5gtegte=50ltlt=50ltelte=50oneofoneof=male femaleeqleneqleneq=10lenlen=10maxmax=10minmin=10
Fields束缚
eqfieldeqfield=Passwordeqcsfieldeqfiel=ConfirmPasswordnefieldnefield=Color1necsfield
罕用束缚
uniqueunique=fieldemailemaileamilomitempty-|rgb|rgbarequired
好啦,就介绍这些罕用的束缚,更多束缚学习请到文档自行学习吧,都有example供你学习,很快的。
gin中的参数校验
validatorvalidatebinding
然而有些时候,并不是所有的参数校验都能满足咱们的需要,所以咱们能够定义本人的束缚。自定义束缚反对自定义构造体校验、自定义字段校验等。这里来介绍一下自定义构造体校验。
自定义构造体校验
当波及到一些简单的校验规定,这些已有的校验规定就不能满足咱们的需要了。例如当初有一个需要,存在db的用户信息中创立工夫与更新工夫都要大于某一时间,假如是从前端传来的(当然不可能,哈哈)。当初咱们来写一个简略示例,学习一下怎么对这个参数进行校验。
package mainimport ( "fmt" "net/http" "time" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10")type Info struct { CreateTime time.Time `form:"create_time" binding:"required,timing" time_format:"2006-01-02"` UpdateTime time.Time `form:"update_time" binding:"required,timing" time_format:"2006-01-02"`}// 自定义验证规定断言func timing(fl validator.FieldLevel) bool { if date, ok := fl.Field().Interface().(time.Time); 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 { err := v.RegisterValidation("timing", timing) if err != nil { fmt.Println("success") } } route.GET("/time", getTime) route.Run(":8080")}func getTime(c *gin.Context) { var b Info // 数据模型绑定查问字符串验证 if err := c.ShouldBindWith(&b, binding.Query); err == nil { c.JSON(http.StatusOK, gin.H{"message": "time are valid!"}) } else { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) }}
写好了,上面我就来测试验证一下:
$ curl "localhost:8080/time?create_time=2020-10-11&update_time=2020-10-11"# 后果{"message":"time are valid!"}%$ curl "localhost:8080/time?create_time=1997-10-11&update_time=1997-10-11"# 后果{"error":"Key: 'Info.CreateTime' Error:Field validation for 'CreateTime' failed on the 'timing' tag\nKey: 'Info.UpdateTime' Error:Field validation for 'UpdateTime' failed on the 'timing' tag"}%
这里咱们看到尽管参数验证胜利了,然而这里返回的谬误显示的也太全了,在我的项目开发中不能够给前端返回这么具体的信息的,所以咱们须要革新一下:
func getTime(c *gin.Context) { var b Info // 数据模型绑定查问字符串验证 if err := c.ShouldBindWith(&b, binding.Query); err == nil { c.JSON(http.StatusOK, gin.H{"message": "time are valid!"}) } else { _, ok := err.(validator.ValidationErrors) if !ok { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 1000, "msg": "param is error"}) }}
这里在呈现谬误时返回固定谬误即可。这里你也能够应用一个办法封装一下,对谬误进行解决在进行返回,更多应用办法等你察觉哟。
小彩蛋
validator
package mainimport ( "fmt" "log" "net/http" "github.com/gin-gonic/gin" "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" enTranslations "github.com/go-playground/validator/v10/translations/en" chTranslations "github.com/go-playground/validator/v10/translations/zh")var trans ut.Translator// loca 通常取决于 http 申请头的 'Accept-Language'func transInit(local string) (err error) { if v, ok := binding.Validator.Engine().(*validator.Validate); ok { zhT := zh.New() //chinese enT := en.New() //english uni := ut.New(enT, zhT, enT) var o bool trans, o = uni.GetTranslator(local) if !o { return fmt.Errorf("uni.GetTranslator(%s) failed", local) } //register translate // 注册翻译器 switch local { case "en": err = enTranslations.RegisterDefaultTranslations(v, trans) case "zh": err = chTranslations.RegisterDefaultTranslations(v, trans) default: err = enTranslations.RegisterDefaultTranslations(v, trans) } return } return}type loginRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required,max=16,min=6"`}func main() { if err := transInit("zh"); err != nil { fmt.Printf("init trans failed, err:%v\n", err) return } router := gin.Default() router.POST("/user/login", login) err := router.Run(":8888") if err != nil { log.Println("failed") }}func login(c *gin.Context) { var req loginRequest if err := c.ShouldBindJSON(&req); err != nil { // 获取validator.ValidationErrors类型的errors errs, ok := err.(validator.ValidationErrors) if !ok { // 非validator.ValidationErrors类型谬误间接返回 c.JSON(http.StatusOK, gin.H{ "msg": err.Error(), }) return } // validator.ValidationErrors类型谬误则进行翻译 c.JSON(http.StatusOK, gin.H{ "msg": errs.Translate(trans), }) return } //login 操作省略 c.JSON(http.StatusOK, gin.H{ "code": 0, "msg": "success", })}
我这里申请参数中限度明码的长度,来验证一下吧。
curl --location --request POST 'http://localhost:8888/user/login' \--header 'Content-Type: application/json' \--data-raw '{ "username": "asong", "password": "11122222222222222222"}'# 返回{ "msg": { "loginRequest.Password": "Password长度不能超过16个字符" }}
看,间接显示中文了,是不是很棒,咱们能够在测试的时候应用这个,上线我的项目不倡议应用呦!!!
总结
好啦,这一篇文章到这里完结啦。这一篇干货还是满满的。学会这些知识点,进步咱们的开发效率,省去了一些没必要写的代码。能用的轮子咱们还是不要错过滴。
我是asong,一名普普通通的程序猿,让我一起缓缓变强吧。欢送各位的关注,咱们下期见~~~ 举荐往期文章:
- 据说你还不会jwt和swagger-饭我都不吃了带着实际我的项目我就来了
- 把握这些Go语言个性,你的程度将进步N个品位(二)
- go实现多人聊天室,在这里你想聊什么都能够的啦!!!
- grpc实际-学会grpc就是这么简略
- go规范库rpc实际
- 2020最新Gin框架中文文档 asong又捡起来了英语,用心翻译
- 基于gin的几种热加载形式