1. Why data verification

  • Data verification is similar to the function of middleware, so that the data transmitted from the front end can reach the controller more legally, so as to reduce the data verification in the control layer one by one.
  • If you do not use data verification, you can manually check data one by one in the controller
  • ingoThere is no equivalent in languagejavaThe same annotation is used to validate the data, but you can use the validation method in the structure (I don’t particularly like this method, but for this language).

Second, use data verification in GO language

  • 1. Official website address

    https://github.com/go-playground/validator
    Copy the code
  • 2. Install dependency packages

    go get github.com/go-playground/validator
    Copy the code
  • 3. Refer to the official website for basic use

3. Custom validator

  • 1. Define the structure for receiving parameters

    type UserInfo struct {
    	Id   string `validate:"uuid" json:"id"`           / / the type UUID
    	Name string `validate:"checkName,required" json:"name"`     // Custom verification
    	Age  uint8  `validate:"min=0,max=130,required" json:"age"` / / age
    }
    Copy the code
  • 2. Custom methods

    func checkNameFunc(f validator.FieldLevel) bool {
    	count := utf8.RuneCountInString(f.Field().String())
    	if count >= 2 && count <= 12 {
    		return true
    	} else {
    		return false}}Copy the code
  • 3. Register the method in init

    var valildate *validator.Validate
    
    func init(a) {
    	valildate = validator.New()
    	valildate.RegisterValidation("checkName", checkNameFunc)
    }
    Copy the code
  • 4. Use in the main function

    func main(a) {
    	router := gin.Default()
    	router.POST("/register".func(c *gin.Context) {
    		user := UserInfo{}
    		// var user = UserInfo
    		err := c.Bind(&user)
    		iferr ! =nil {
    			c.JSON(http.StatusOK, gin.H{
    				"code":    1."message": "Request parameter error",})return
    		}
    		err = valildate.Struct(user)
    		iferr ! =nil {
    			/ / output calibration error. (the validator. ValidationErrors) is asserted
    			for _, e := range err.(validator.ValidationErrors) {
    				fmt.Println("Error field :", e.Field())
    				fmt.Println("Error value :", e.Value())
    				fmt.Println("Wrong tag:", e.Tag())
    			}
    			c.JSON(http.StatusOK, gin.H{
    				"code":    1."message": "Data verification error",})return
    		}
    		fmt.Println(user, "Received data")
    		c.JSON(http.StatusOK, gin.H{
    			"code":    0."message": "Request successful",
    		})
    	})
    	router.Run(": 8000")}Copy the code

Fourth, the verification of multi-layer nested data

  • 1. Use Dive for data validation of multilevel structures

  • 2. The structure of the input parameter

    type UserInfo struct {
    	Id   string `validate:"uuid" json:"id"`           / / the type UUID
    	Name string `validate:"checkName,required" json:"name"`     // Custom verification
    	Age  uint8  `validate:"min=0,max=130,required" json:"age"` / / age
    	Address []Address `validate:"dive" json:"address"` // Shipping address
    }
    
    type Address struct {
    	Province string `json:"province" validate:"required"` / / provinces
    	City string `json:"city" validate:"required"` / / the city
    	County string `json:"county" validate:"required"` / / county
    	Mobile string `json:"mobile" validate:"numeric,len=11"` // Mobile phone number
    }
    Copy the code
  • 3. Submit data using Postman

    {
        "name": "admin",
        "age": 20.
        "id": "3f7a783f-f9dc-4db8-9c6b-afe1d3f9b3f7",
        "address": [
            {
                "province": Guangdong Province,
                "city":"Shenzhen ",
                "county": Bao 'an District,
                "mobile": "18112345678"
            }
        ]
    }
    Copy the code
  • 4. Others have the same structure as above

Error message translated into Chinese

  • 1. Define a simple registry structure

    type RegisterForm struct {
    	UserName   string `json:"username" binding:"required,min=3,max=10"`
    	Password   string `json:"password" binding:"required,min=3"`
    	RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
    }
    Copy the code
  • 2, the normal use of the error

    {
      "error": "Key: 'RegisterForm.RePassword' Error:Field validation for 'RePassword' failed on the 'eqfield' tag"
    }
    Copy the code
  • 3, use translation into Chinese, more readable, define a translator method

    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"
    	en_translations "github.com/go-playground/validator/v10/translations/en"
    	zh_translations "github.com/go-playground/validator/v10/translations/zh"
    )
    
    var trans ut.Translator
    
    // Define the translation method
    func InitTrans(locale string) (err error) {
    	// Modify the Properties of the Validator engine in the GIN framework to implement customization
    	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    		zhT := zh.New() // Chinese translator
    		enT := en.New() // English translator
    		// The first argument is the alternate locale, and the next argument is the locale that should be supported
    		uni := ut.New(enT, zhT, enT)
    		trans, ok = uni.GetTranslator(locale)
    		if! ok {return fmt.Errorf("uni.GetTranslator(%s)", locale)
    		}
    		switch locale {
    		case "en":
    			en_translations.RegisterDefaultTranslations(v, trans)
    		case "zh":
    			zh_translations.RegisterDefaultTranslations(v, trans)
    		default:
    			en_translations.RegisterDefaultTranslations(v, trans)
    		}
    		return
    	}
    	return
    }
    Copy the code
  • 4. Use a translator

    func main(a) {
    	if err := InitTrans("zh"); err ! =nil {
    		fmt.Println("Error initializing translator")
    		return
    	}
    	router := gin.Default()
    	router.POST("/register".func(c *gin.Context) {
    		var registerForm RegisterForm
    		iferr := c.ShouldBind(&registerForm); err ! =nil {
    			fmt.Println(err.Error())
    			// Translation error
    			errs, ok := err.(validator.ValidationErrors)
    			if! ok { c.JSON(http.StatusOK, gin.H{"message": err.Error(),
    				})
    			}
          // Return the error message to the front end
    			c.JSON(http.StatusOK, gin.H{
    				"error": errs.Translate(trans),
    			})
    			return
    		}
    		fmt.Println("Parameters for registration :", registerForm)
    		c.JSON(http.StatusOK, gin.H{
    			"code":    1."message": "Success"."data": gin.H{
    				"username": registerForm.UserName,
    				"password": registerForm.Password,
    			},
    		})
    	})
    	router.Run(": 8080")}Copy the code
    {
      "error": {
        "RegisterForm.RePassword": "RePassword must equal Password",
        "RegisterForm.UserName": "UserName must be at least 3 characters long"
      }
    }
    Copy the code
  • 5. Remove unnecessary labels to make the tips more user-friendly

    func removeTopStruct(fileds map[string]string) map[string]string {
    	rsp := map[string]string{}
    	for field, err := range fileds {
    		rsp[field[strings.Index(field, ".") +1:]] = err
    	}
    	return rsp
    }
    Copy the code
    iferr := c.ShouldBind(&registerForm); err ! =nil {
      fmt.Println(err.Error())
      // Translation error
      errs, ok := err.(validator.ValidationErrors)
      if! ok { c.JSON(http.StatusOK, gin.H{"message": err.Error(),
        })
      }
      c.JSON(http.StatusOK, gin.H{
        "error": removeTopStruct(errs.Translate(trans)),
      })
      return
    }
    Copy the code
  • 6. Complete code for translation into Chinese

    package main
    
    import (
    	"fmt"
    	"net/http"
    	"reflect"
    	"strings"
    
    	"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"
    	en_translations "github.com/go-playground/validator/v10/translations/en"
    	zh_translations "github.com/go-playground/validator/v10/translations/zh"
    )
    var trans ut.Translator
    type RegisterForm struct {
    	UserName   string `json:"username" binding:"required,min=3,max=10"`
    	Password   string `json:"password" binding:"required,min=3"`
    	RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
    }
    func removeTopStruct(fileds map[string]string) map[string]string {
    	rsp := map[string]string{}
    	for field, err := range fileds {
    		rsp[field[strings.Index(field, ".") +1:]] = err
    	}
    	return rsp
    }
    // Define the translation method
    func InitTrans(locale string) (err error) {
    	// Modify the Properties of the Validator engine in the GIN framework to implement customization
    	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    		// Register a custom method to get a 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 argument is the alternate locale, and the next argument is the locale that should be supported
    		uni := ut.New(enT, zhT, enT)
    		trans, ok = uni.GetTranslator(locale)
    		if! ok {return fmt.Errorf("uni.GetTranslator(%s)", locale)
    		}
    		switch locale {
    		case "en":
    			en_translations.RegisterDefaultTranslations(v, trans)
    		case "zh":
    			zh_translations.RegisterDefaultTranslations(v, trans)
    		default:
    			en_translations.RegisterDefaultTranslations(v, trans)
    		}
    		return
    	}
    	return
    }
    
    func main(a) {
    	if err := InitTrans("zh"); err ! =nil {
    		fmt.Println("Error initializing translator")
    		return
    	}
    	router := gin.Default()
    	router.POST("/register".func(c *gin.Context) {
    		var registerForm RegisterForm
    		iferr := c.ShouldBind(&registerForm); err ! =nil {
    			fmt.Println(err.Error())
    			// Translation error
    			errs, ok := err.(validator.ValidationErrors)
    			if! ok { c.JSON(http.StatusOK, gin.H{"message": err.Error(),
    				})
    			}
    			c.JSON(http.StatusOK, gin.H{
    				"error": removeTopStruct(errs.Translate(trans)),
    			})
    			return
    		}
    		fmt.Println("Parameters for registration :", registerForm)
    		c.JSON(http.StatusOK, gin.H{
    			"code":    1."message": "Success"."data": gin.H{
    				"username": registerForm.UserName,
    				"password": registerForm.Password,
    			},
    		})
    	})
    
    	router.Run(": 8080")}Copy the code
  • You can encapsulate the above block as a public method for use elsewhere

    package utils
    
    import (
    	"fmt"
    	"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"
    	"reflect"
    	"strings"
    )
    
    var Trans ut.Translator
    
    func init(a) {
    	if err := InitTrans("zh"); err ! =nil {
    		fmt.Println("Error initializing translator")
    		panic("Error initializing translator")}}// RemoveTopStruct removes unwanted tags
    func RemoveTopStruct(fileds map[string]string) map[string]string {
    	rsp := map[string]string{}
    	for field, err := range fileds {
    		rsp[field[strings.Index(field, ".") +1:]] = err
    	}
    	return rsp
    }
    
    // InitTrans defines the translation method
    func InitTrans(locale string) (err error) {
    	// Modify the Properties of the Validator engine in the GIN framework to implement customization
    	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    		// Register a custom method to get a 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 argument is the alternate locale, and the next argument is the locale that should be supported
    		uni := ut.New(enT, zhT, enT)
    		Trans, ok = uni.GetTranslator(locale)
    		if! ok {return fmt.Errorf("uni.GetTranslator(%s)", locale)
    		}
    		switch locale {
    		case "en":
    			enTranslations.RegisterDefaultTranslations(v, Trans)
    		case "zh":
    			zhTranslations.RegisterDefaultTranslations(v, Trans)
    		default:
    			enTranslations.RegisterDefaultTranslations(v, Trans)
    		}
    		return
    	}
    	return
    }
    Copy the code
  • 8. External use

    package main
    
    import (
    	"fmt"
    	"gin_stuty/01/utils"
    	"github.com/gin-gonic/gin"
    	"github.com/go-playground/validator/v10"
    	"net/http"
    )
    
    type RegisterForm struct {
    	UserName   string `json:"username" binding:"required,min=3,max=10"`
    	Password   string `json:"password" binding:"required,min=3"`
    	RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
    }
    
    func main(a) {
    	router := gin.Default()
    	router.POST("/register".func(ctx *gin.Context) {
    		var registerForm RegisterForm
    		iferr := ctx.ShouldBind(&registerForm); err ! =nil {
    			fmt.Println(err.Error())
    			// Translation error
    			errs, ok := err.(validator.ValidationErrors)
    			if! ok { ctx.JSON(http.StatusOK, gin.H{"message": err.Error(),
    				})
    			}
    			// Use the encapsulated translation method
    			ctx.JSON(http.StatusOK, gin.H{
    				"error": utils.RemoveTopStruct(errs.Translate(utils.Trans)),
    			})
    			return
    		}
    		ctx.JSON(http.StatusOK, gin.H{
    			"code":    0."message": "Success",
    		})
    	})
    	fmt.Println(fmt.Sprintf("Service started :localhost:9000"))
    	router.Run(": 9000")}Copy the code

6. Translate custom errors into Chinese

  • 1. Review the custom translator above

    type RegisterForm1 struct {
    	// There is a custom validator for mobile phone number format. Mobile is our custom validator
    	Mobile string `form:"mobile" json:"mobile" binding:"required,mobile"`
    }
    
    // ValidateMobile Specifies a custom validator
    func ValidateMobile(f validator.FieldLevel) bool {
    	// Get the value of the field
    	mobile := f.Field().String()
    	// Use the re to verify
    	if ok, _ := regexp.MatchString(` ^ 1,5,7,8 [3] \ d {9} $`, mobile); ok {
    		return true
    	} else {
    		return false}}Copy the code
  • 2. Register custom validators to verify

    func main(a) {
    	// Register the validator
    	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    		_ = v.RegisterValidation("mobile", ValidateMobile)
    		_ = v.RegisterTranslation("mobile", utils.Trans, func(ut ut.Translator) error {
    			return ut.Add("mobile"."{0} Mobile phone number does not match the rule".true)},func(ut ut.Translator, fe validator.FieldError) string {
    			t, _ := ut.T("mobile", fe.Field())
    			return t
    		})
    	}
    	router := gin.Default()
    	router.POST("/register".func(ctx *gin.Context) {
    		var registerForm RegisterForm1
    		iferr := ctx.ShouldBindJSON(&registerForm); err ! =nil {
    			fmt.Println(err.Error())
    			// Translation error
    			errs, ok := err.(validator.ValidationErrors)
    			if! ok { ctx.JSON(http.StatusOK, gin.H{"message": err.Error(),
    				})
    			}
    			// Use the encapsulated translation method
    			ctx.JSON(http.StatusOK, gin.H{
    				"error": utils.RemoveTopStruct(errs.Translate(utils.Trans)),
    			})
    			return
    		}
    		ctx.JSON(http.StatusOK, gin.H{
    			"code":    0."message": "Success",
    		})
    	})
    	router.Run(": 9000")}Copy the code
  • 3. Generally, custom validators are stored in the project’s Validator folder, so they are not extracted here