1. Description of special symbols before marking
Comma (,): separate multiple verification tags. Note that there cannot be spaces between commas Horizontal dash (-): This field is not verified and skipped Vertical bar (|): Use multiple verification tags, but only one of them needs to be satisfied. required: indicates that the field must be set and cannot be used as a default value omitempty: If a field is not set, ignore it omitempty,xxx=xxx # Optional, if it exists, continue to verify the rule xxx=xxx backwards. If it does not exist, xxx=xxx will not take effect, but if there is a verification rule before omitempty, the previous verification rule will still be Effective, such as gte=-1,omitempty,len=3, the gte=-1 rule will always take effect, and len=3 will only take effect when the value is not 0.
2. Verification scope
1: Verification range: slices, arrays, maps, and strings, verify their length; numerical values, verify the size range lte: less than or equal to the parameter value, validate: "lte=3" (less than or equal to 3) gte: greater than or equal to the parameter value, validate: "lte=120,gte=0" (greater than or equal to 0 and less than or equal to 120) lt: less than the parameter value, validate: "lt=3" (less than 3) gt: greater than the parameter value, validate: "lt=120,gt=0" (greater than 0 and less than 120) len: equal to parameter value, validate: "len=2" max: maximum value, less than or equal to the parameter value, validate: "max=20" (less than or equal to 20) min: minimum value, greater than or equal to the parameter value, validate: "min=2,max=20" (greater than or equal to 2 and less than or equal to 20) ne: not equal to, validate: "ne=2" (not equal to 2) oneof: can only be one of the listed values. These values must be numeric values or strings, separated by spaces. If there are spaces in the string, surround the string with single quotes, validate: "oneof=red green"
2: String verification
contains: Contains parameter substring, validate: "contains=tom" (the string value of the field contains tom) excludes: Contains parameter substrings, validate: "excludes=tom" (the string value of the field does not include tom) startswith: prefixed with parameter substring, validate: "startswith=golang" endswith: suffixed with parameter substring, validate: "startswith=world" max=10 # The maximum length is 10 min=10 # The minimum length is 10 gt=10 # The length is greater than 10 lt=10 # The length is less than 10 gte=10 # The length is greater than or equal to 10 let=10 # The length is less than or equal to 10 eq=aaa #The value is aaa ne=aaa # The value cannot be aaa oneof=a b c # Enumeration, can only be a, b or c len=3 #The character length is 3
3: Field verification
eqcsfield: Verification across different structure fields, for example, struct filed1 and struct filed2 are equal necsfield: fields are not equal across different structures eqfield: The fields of the same structure are verified to be equal. The most common one is to enter the password twice for verification. 【 Password string `json:"password" binding:"required"` RePassword string `json:"re_password" binding:"required,eqfield=Password"` 】 nefield: Field verification of the same structure is not equal gtefield: greater than or equal to the same structure field, validate: "gtefiled=Field2" ltefield: less than or equal to the same structure field A level internal check qfield=AAA #Equal to field AAA value nefield=AAA # is not equal to the field AAA value gtfield=AAA # Value greater than field AAA gtefield=AAA # Greater than or equal to the value of field AAA ltfield=AAA # Value less than field AAA ltefield=AAA # Less than or equal to the value of the AAA field
Array check dive Domains []string `binding:"gt=0,dive,required,min=1,max=100"` Check content: [] string length must be greater than 0, and the string length of the element in the array must be between 1-100 Dive Map Verification ReqHeaders map[string]string `binding:"dive,keys,min=1,max=100,endkeys,required,min=1,max=100"` Keys and endkeys are used here to mark the verification range of key values, starting from keys and ending with endkeys Verification content: The whole verification is not done. The key value length must be between 1-100, and the value value length must also be between 1-100. structonly When a structure defines validation rules, but in some places, these validation rules are not required to take effect, you can use the structonly tag, and the validation rules inside the structure with this tag will no longer take effect. as follows: type Timeout struct { Connect int `json:"connect" binding:"required"` Read int `json:"read" binding:"required"` Send int `json:"send" binding:"required"` } type MyStruct struct { Name string `json:"name" binding:"required"` Timeout Timeout `json:"timeout" binding:"structonly"` } Each field of the Timeout structure defines verification content, but I used the structonly tag in the Timeout field of MyStruct, so the verification content defined in Timeout will no longer take effect.
4: Network verification
ip: Whether the field value contains a valid IP address, validate: "ip" ipv4: Whether the field value contains a valid ipv4 address, validate: "ipv4" ipv6: Whether the field value contains a valid ipv6 address, validate: "ipv6" uri: Whether the field value contains a valid uri, validate: "uri" url: Whether the field value contains a valid uri, validate: "url"
5: Date verification
Date string `json:"date" binding:"required,datetime=2006-01-02,checkDate"` Among them, datetime=2006-01-02 is a built-in tag used to verify whether date parameters meet the specified format requirements. If the date parameter passed in does not meet the format of 2006-01-02, the following error will be prompted: example type SignUpParam struct { Age uint8 `json:"age" binding:"gte=1,lte=130"` Name string `json:"name" binding:"required"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required,max=13,min=1"` RePassword string `json:"re_password" binding:"required,eqfield=Password"` }
Three: Translation verification error message
The validator library itself supports internationalization, and the automatic translation of verification error prompts can be achieved with the help of the corresponding language pack. The examples below are translated into Chinese.
package main import ( "fmt" "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" zhTranslations "github.com/go-playground/validator/v10/translations/zh" ) //Define a global translator T var transut.Translator // InitTrans initializes the translator func InitTrans(locale string) (err error) { // Modify the Validator engine properties in the gin framework to achieve customization if v, ok := binding.Validator.Engine().(*validator.Validate); ok { zhT := zh.New() // Chinese translator enT := en.New() // English translator // The first parameter is the fallback locale //The following parameters are the locale that should be supported (multiple supported) // uni := ut.New(zhT, zhT) is also possible uni := ut.New(enT, zhT, enT) // locale usually depends on the 'Accept-Language' http request header var OK bool // You can also use uni.FindTranslator(...) to pass in multiple locales for search trans, ok = uni.GetTranslator(locale) if !ok { return fmt.Errorf("uni.GetTranslator(%s) failed", locale) } //Register translator switch locale { case "en": err = enTranslations.RegisterDefaultTranslations(v, trans) case "zh": err = zhTranslations.RegisterDefaultTranslations(v, trans) default: err = enTranslations.RegisterDefaultTranslations(v, trans) } return } return } type SignUpParam struct { Age uint8 `json:"age" binding:"gte=1,lte=130"` Name string `json:"name" binding:"required"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` RePassword string `json:"re_password" binding:"required,eqfield=Password"` } func main() { if err := InitTrans("zh"); err != nil { fmt.Printf("init trans failed, err:%v\ ", err) return } r := gin.Default() r.POST("/signup", func(c *gin.Context) { var uSignUpParam if err := c.ShouldBind( & amp;u); err != nil { // Get errors of type validator.ValidationErrors errs, ok := err.(validator.ValidationErrors) if !ok { // Non-validator.ValidationErrors type errors are returned directly c.JSON(http.StatusOK, gin.H{ "msg": err.Error(), }) return } // Validator.ValidationErrors type errors are translated c.JSON(http.StatusOK, gin.H{ "msg":errs.Translate(trans), }) return } // Save specific business logic code such as warehousing... c.JSON(http.StatusOK, "success") }) _ = r.Run(":8999") }
Customize the field name of the error message
The above error message seems to be OK, but it still makes little sense. First of all, the field in the error message is not the field used in the request. For example: RePassword is the field name in the structure defined by our backend, and the field used in the request is the re_password field. How to use a custom name for the field in the error message, such as the value specified by jsontag?
Just add a custom method to get the json tag when initializing the translator as shown below.
//InitTrans initializes the translator func InitTrans(locale string) (err error) { // Modify the Validator engine properties in the gin framework to achieve customization if v, ok := binding.Validator.Engine().(*validator.Validate); ok { //Register a custom method to get json tag v.RegisterTagNameFunc(func(fld reflect.StructField) string { name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] if name == "-" { return "" } return name }) zhT := zh.New() // Chinese translator enT := en.New() // English translator // The first parameter is the fallback locale //The following parameters are the locale that should be supported (multiple supported) // uni := ut.New(zhT, zhT) is also possible uni := ut.New(enT, zhT, enT) //... }
{"msg":{"SignUpParam.email":"email must be a valid email address", "SignUpParam.password":"password is a required field", "SignUpParam.re_password":"re_password is a required field"} }
You can see that the error message now uses the name of the jsontag setting in our structure.
But there is still a slight flaw, that is, the final error message center still has the structure name defined by our backend – SignUpParam. This name actually does not need to be returned to the front end with the error message, and the front end does not need this value. We need to find a way to get rid of it.
Here, refer to the method provided by https://github.com/go-playground/validator/issues/633#issuecomment-654382345 to define a custom method that removes the prefix of the structure name.
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 }
We can use the above function in the code to process the translated errors:
if err := c.ShouldBind( & amp;u); err != nil { // Get errors of type validator.ValidationErrors errs, ok := err.(validator.ValidationErrors) if !ok { // Non-validator.ValidationErrors type errors are returned directly c.JSON(http.StatusOK, gin.H{ "msg": err.Error(), }) return } // Validator.ValidationErrors type errors are translated // And use the removeTopStruct function to remove the structure name identifier in the field name c.JSON(http.StatusOK, gin.H{ "msg": removeTopStruct(errs.Translate(trans)), }) return }
Take a look at the final effect:
{"msg":{"email":"email must be a valid email address", "password":"password is a required field","re_password":"re_password is a required field"}}