preface

This is my fourth day in the Gwen Challenge. Hi, I am a composer. My last post talked about Zap log management and route initialization. In this chapter we will discuss how to verify the data sent from the front end, the Chinese translation of the verifier, and write a custom verifier

1. Introduction

Field verification is an important link in the back end. Although the front end form is verified, we must do field verification for security and avoid dirty data in the back end Although we can write a function to encapsulate their own verification logic, but the structure of the tag has some basic verification function, plus can customize the validator, code elegant efficiency is very high! To make it easier for you to understand the flow of requests, draw a data flow diagram to understand the overall flow of data

2. Quickly implement basic field verification

(1). Define the check structure

In the forms/user. Go to write

package forms

type PasswordLoginForm struct {
	// Password binding:"required" is a mandatory field. The length is greater than 3 and less than 20
	PassWord  string `form:"password" json:"password" binding:"required,min=3,max=20"`
	/ / user name
	Username string `form:"name" json:"name" binding:"required"`       
}
Copy the code

The tag in the structure comes with some verification parameters, but they are relatively basic. If you do some strict verification, you need to make a custom validator, which we will talk about later.

(2). Writing the controller

The controller/user. Go to write

package controller

import (
	"github.com/fatih/color"
	"github.com/gin-gonic/gin"
	"go_gin/forms"
	"net/http"
)

/ / PasswordLogin login
func PasswordLogin(c *gin.Context) {
	PasswordLoginForm := forms.PasswordLoginForm{

	}
	iferr := c.ShouldBind(&PasswordLoginForm); err ! =nil {
		color.Blue(err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{
			"err": err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"msg": "sussess"})},Copy the code

(3). Add a router route

Add a route to router/user.go

    UserRouter.POST("login", controller.PasswordLogin)
Copy the code

(4) verify the validity of the verification

Correct verification of parameters:

Parameter error verification:The name field is not entered

Ps: If you can verify to this point, it means that the above steps are successful, but there is a hole here, that is, the error return field is English, we need to add Chinese translation converter to the validator next

3. Add the Chinese converter for field verification

1). Install validator

// Validator go get github.com/go-playground/validator/v10 // translator go get github.com/go-playground/universal-translator // Chinese package go get github.com/go-playground/locales/zh // English package go get github.com/go-playground/locales/enCopy the code

(2). Define global translator instance variables

Add variables to global/globalVar

Trans ut.Translator
Copy the code

(3). Writing translators

In the initialize/validator

package initialize

import (
	"fmt"
	"github.com/fatih/color"
	"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"
	"go_gin/global"

	"reflect"
	"strings"
)

// InitTrans Validator message translation
func InitTrans(locale string) (err error) {
	color.Red("test111111111111")
	// 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)
		global.Trans, ok = uni.GetTranslator(locale)
		if! ok {return fmt.Errorf("uni.GetTranslator(%s)", locale)
		}

		switch locale {
		case "en":
			_ = en_translations.RegisterDefaultTranslations(v, global.Trans)
		case "zh":
			_ = zh_translations.RegisterDefaultTranslations(v, global.Trans)
		default:
			_ = en_translations.RegisterDefaultTranslations(v, global.Trans)
		}

		return
	}
	return
}
Copy the code

Ps: The general process is to pass an argument to InitTrans, determine what language package is loaded, and then get the language package and assign it to the global translator

(3). Write field check exception function

Encapsulating a unified function for handling field exceptions is written in utils/ Validator

package utils

import (
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go_gin/global"
	"net/http"
	"strings"
)

// HandleValidatorError handles field validation exceptions
func HandleValidatorError(c *gin.Context, err error) {
	// How do I return error messages
	errs, ok := err.(validator.ValidationErrors)
	if! ok { c.JSON(http.StatusOK, gin.H{"msg": err.Error(),
		})
	}
	c.JSON(http.StatusBadRequest, gin.H{
		"error": removeTopStruct(errs.Translate(global.Trans)),
	})
	return
}
// removeTopStruct defines a custom method to remove the prefix of the structure name:
func removeTopStruct(fileds map[string]string) map[string]string {
	rsp := map[string]string{}
	for field, err := range fileds {
		"PasswordLoginForm. Mobile ": "mobile is required"
		rsp[field[strings.Index(field, ".") +1:]] = err
	}
	return rsp
}

Copy the code

Ps :removeTopStruct is mainly used to split strings. It should be translated into Chinese. The first half of the returned key is still in English

(4). Use HandleValidatorError in controller

package controller

import (

	"github.com/gin-gonic/gin"
	"go_gin/utils"

	"go_gin/forms"

	"net/http"

)



/ / PasswordLogin login
func PasswordLogin(c *gin.Context) {
	PasswordLoginForm := forms.PasswordLoginForm{

	}
	iferr := c.ShouldBind(&PasswordLoginForm); err ! =nil {
                // Handle exceptions uniformly
		utils.HandleValidatorError(c, err)
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"msg": "sussess"})},Copy the code

If PasswordLoginForm () is passed in, we should check the tag of the struct

(5). Use the initialization field validator in main.go

In main.go

//4. Initialize the translation
if err := initialize.InitTrans("zh"); err ! =nil {
    panic(err)
}

Copy the code

(6). Test the above steps

A simple field validator is complete, and we now verify \

<1>. Parameter error:

<1>. If the parameter is correct:

The Chinese error check is displayed. If you do this and the test passes, you can proceed

3. Customize the verification

The tag verification function of the structure is relatively basic, but there are some complex verification requirements, we have to customize the validator to do this. Now suppose that the user name passed by the front-end must be in the format of the mobile phone number:

  1. Consecutive digits are 11 digits
  2. Start with the digit 1

Given the requirements, we know that the checksum function is easier to do with regular matching

(1). Define the check function

Written in utils/ Valicator

// ValidateMobile Validates the mobile phone number
func ValidateMobile(fl validator.FieldLevel) bool {
	// Use reflection to get the struct tag containing the mobile key field
	mobile := fl.Field().String()
	// Use regular expressions to check whether it is valid
	ok, _ := regexp.MatchString(`^1([38][0-9]|14[579]|5[^4]|16[6]|7[1-35-8]|9[189])\d{8}$`, mobile)
	if! ok{return false
	}
	return true
}
Copy the code

(2). Define and register custom check tag functions

Add in initialize/ Validator

// Func myvalidator.ValidateMobile
type Func func(fl validator.FieldLevel) bool
// RegisterValidatorFunc registers custom validatorTags
func RegisterValidatorFunc(v *validator.Validate, tag string, msgStr string, fn Func) {
	// Register tag custom validation
	_ = v.RegisterValidation(tag, validator.Func(fn))
	// Customize the error content
	_ = v.RegisterTranslation(tag, global.Trans, func(ut ut.Translator) error {
		return ut.Add(tag, "{0}"+msgStr, true) // see universal-translator for details
	}, func(ut ut.Translator, fe validator.FieldError) string {
		t, _ := ut.T(tag, fe.Field())
		return t
	})

	return
}
Copy the code

(3). Add it to InitTrans

We’ve written a checksum function and a registered custom checksum tag function,

Add them to InitTrans in initialize/ Validator

And finally — testing

(1). PasswordLoginForm add a mobile tag to the form /user.go file

(2). The test

An error is reported when the name field does not conform to the validation logic of the custom field

By the time you have reached this stage, you have mastered the use of custom validators

The next chapter introduces unified response return processing

If you found any of these articles useful, please give them a thumbs up and leave a comment