As we explained in the previous section, gin can use the ShouldBind method to bind parameters to a structure, but we don't cover how parameter checking works. In this section, we describe how parameter checking and checking fail and then convert to Chinese to return to the front end.
1. Data Validation
Let's start with a simple example:
- Create a new test_under the requests directory in the root directoryRequest.go
package requests //Test Request Structure This structure defines the parameters and validation rules for the request type TestRequest struct { Username string `form:"username" binding:"required"` }
- Create a new one in the root api directoryTest.goController, define test controller
package api import ( "cn.sockstack/gin_demo/requests" "github.com/gin-gonic/gin" "net/http" ) func Test(c *gin.Context) { //Instantiate a TestRequest structure for receiving parameters testStruct := requests.TestRequest{} //Receive Request Parameters err := c.ShouldBind(&testStruct) //Determines whether the parameter check passes or not, and if it does not, returns the error to the front end if err != nil { c.JSON(http.StatusOK, gin.H{"error": err.Error()}) return } //Check passes, returning request parameters c.JSON(http.StatusOK, gin.H{"params": testStruct}) }
- Define/test routes under the root directory routers, create new onesInit.goandTest.goFile initialization routing.
test.go
package routers import ( "cn.sockstack/gin_demo/api" "github.com/gin-gonic/gin" ) func test(r *gin.Engine) { //Define/test routing r.GET("/test", api.Test) }
init.go
package routers import "github.com/gin-gonic/gin" func Init(r *gin.Engine) { //Register test route test(r) }
- stayMain.goRegister Routes in
package main // Import gin package import ( "cn.sockstack/gin_demo/pkg/config" "cn.sockstack/gin_demo/routers" "fmt" "github.com/gin-gonic/gin" ) // Entry function func main() { // Initialize an http service object r := gin.Default() //Register Routes routers.Init(r) r.Run(fmt.Sprintf("%s:%d", config.Server.Address, config.Server.Port)) // Listen and start the service at 0.0.0.0:8081 }
- Run and accessLocalhost:8081/test, error occurs without parameters
{ "error": "Key: 'TestRequest.Username' Error:Field validation for 'Username' failed on the 'required' tag" }
- Run and accessLocalhost:8081/test?Username=sockstackReturns the parameters of the response.
{ "params": { "Username": "sockstack" } }
The example above already implements parameter checking and accepts parameters, but the hint returned when the check fails is in English. Let's show how to convert the error into Chinese.
2. Check parameter failures prompt automatic translation
Looking at the code, we found that gin's default validator uses the validator package, and looking at the documentation found that validator can translate English errors into Chinese.
package main import ( "fmt" "github.com/go-playground/locales/en" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" en_translations "github.com/go-playground/validator/v10/translations/en" ) // User contains user information type User struct { FirstName string `validate:"required"` LastName string `validate:"required"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla' Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... } // Address houses a users address information type Address struct { Street string `validate:"required"` City string `validate:"required"` Planet string `validate:"required"` Phone string `validate:"required"` } // use a single instance , it caches struct info var ( uni *ut.UniversalTranslator validate *validator.Validate ) func main() { // NOTE: ommitting allot of error checking for brevity en := en.New() uni = ut.New(en, en) // this is usually know or extracted from http 'Accept-Language' header // also see uni.FindTranslator(...) trans, _ := uni.GetTranslator("en") validate = validator.New() en_translations.RegisterDefaultTranslations(validate, trans) translateAll(trans) translateIndividual(trans) translateOverride(trans) // yep you can specify your own in whatever locale you want! } func translateAll(trans ut.Translator) { type User struct { Username string `validate:"required"` Tagline string `validate:"required,lt=10"` Tagline2 string `validate:"required,gt=1"` } user := User{ Username: "Joeybloggs", Tagline: "This tagline is way too long.", Tagline2: "1", } err := validate.Struct(user) if err != nil { // translate all error at once errs := err.(validator.ValidationErrors) // returns a map with key = namespace & value = translated error // NOTICE: 2 errors are returned and you'll see something surprising // translations are i18n aware!!!! // eg. '10 characters' vs '1 character' fmt.Println(errs.Translate(trans)) } } func translateIndividual(trans ut.Translator) { type User struct { Username string `validate:"required"` } var user User err := validate.Struct(user) if err != nil { errs := err.(validator.ValidationErrors) for _, e := range errs { // can translate each error one at a time. fmt.Println(e.Translate(trans)) } } } func translateOverride(trans ut.Translator) { 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 }) type User struct { Username string `validate:"required"` } var user User err := validate.Struct(user) if err != nil { errs := err.(validator.ValidationErrors) for _, e := range errs { // can translate each error one at a time. fmt.Println(e.Translate(trans)) } } }
So let's transform gin's verification tips.
- New under requests directoryInit.gofile
package requests import ( "github.com/gin-gonic/gin/binding" "github.com/go-playground/locales/zh" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" zh_translations "github.com/go-playground/validator/v10/translations/zh" ) var ( uni *ut.UniversalTranslator validate *validator.Validate trans ut.Translator ) func init() { //Register Translator zh := zh.New() uni = ut.New(zh, zh) trans, _ = uni.GetTranslator("zh") //Get gin's verifier validate := binding.Validator.Engine().(*validator.Validate) //Register Translator zh_translations.RegisterDefaultTranslations(validate, trans) } //Translate Translation Error Information func Translate(err error) map[string][]string { var result = make(map[string][]string) errors := err.(validator.ValidationErrors) for _, err := range errors{ result[err.Field()] = append(result[err.Field()], err.Translate(trans)) } return result }
- Modify Controller
package api import ( "cn.sockstack/gin_demo/requests" "github.com/gin-gonic/gin" "net/http" ) func Test(c *gin.Context) { //Instantiate a TestRequest structure for receiving parameters testStruct := requests.TestRequest{} //Receive Request Parameters err := c.ShouldBind(&testStruct) //Determines whether the parameter check passes or not, and if it does not, returns the error to the front end if err != nil { c.JSON(http.StatusOK, gin.H{"error": requests.Translate(err)}) return } //Check passes, returning request parameters c.JSON(http.StatusOK, gin.H{"params": testStruct}) }
- Run and accessLocalhost:8081/test, error without parameters, but error message has been translated into Chinese
{ "error": { "Username": [ "Username Is a required field" ] } }
Source gin from entry to practice More great articles, please follow my blog SOCKSTACK To share my work experience.