在上一節咱們介紹到,gin能夠使用ShouldBind方法把參數綁定到結構體,可是沒有介紹到參數校驗的方式,這節咱們來介紹參數校驗和校驗失敗後轉換成中文返回前端。前端
下面咱們開始一個簡單的例子:git
package requests //測試請求結構體 該結構體定義了請求的參數和校驗規則 type TestRequest struct { Username string `form:"username" binding:"required"` }
package api import ( "cn.sockstack/gin_demo/requests" "github.com/gin-gonic/gin" "net/http" ) func Test(c *gin.Context) { //實例化一個TestRequest結構體,用於接收參數 testStruct := requests.TestRequest{} //接收請求參數 err := c.ShouldBind(&testStruct) //判斷參數校驗是否經過,若是不經過,把錯誤返回給前端 if err != nil { c.JSON(http.StatusOK, gin.H{"error": err.Error()}) return } //校驗經過,返回請求參數 c.JSON(http.StatusOK, gin.H{"params": testStruct}) }
test.gogithub
package routers import ( "cn.sockstack/gin_demo/api" "github.com/gin-gonic/gin" ) func test(r *gin.Engine) { //定義/test路由 r.GET("/test", api.Test) }
init.gojson
package routers import "github.com/gin-gonic/gin" func Init(r *gin.Engine) { //註冊test路由 test(r) }
package main // 導入gin包 import ( "cn.sockstack/gin_demo/pkg/config" "cn.sockstack/gin_demo/routers" "fmt" "github.com/gin-gonic/gin" ) // 入口函數 func main() { // 初始化一個http服務對象 r := gin.Default() //註冊路由 routers.Init(r) r.Run(fmt.Sprintf("%s:%d", config.Server.Address, config.Server.Port)) // 監聽並在 0.0.0.0:8081 上啓動服務 }
{ "error": "Key: 'TestRequest.Username' Error:Field validation for 'Username' failed on the 'required' tag" }
{ "params": { "Username": "sockstack" } }
上面的例子已經能夠實現參數校驗和接收參數了,可是校驗不經過的時候返回的提示是英文的,下面咱們介紹一下怎麼把錯誤轉成中文返回。api
經過查看代碼咱們發現gin默認的校驗器使用的是validator包,而且查看文檔發現validator是能夠把英文錯誤翻譯成中文的。app
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)) } } }
那麼咱們改造gin的校驗提示。ide
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() { //註冊翻譯器 zh := zh.New() uni = ut.New(zh, zh) trans, _ = uni.GetTranslator("zh") //獲取gin的校驗器 validate := binding.Validator.Engine().(*validator.Validate) //註冊翻譯器 zh_translations.RegisterDefaultTranslations(validate, trans) } //Translate 翻譯錯誤信息 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 }
package api import ( "cn.sockstack/gin_demo/requests" "github.com/gin-gonic/gin" "net/http" ) func Test(c *gin.Context) { //實例化一個TestRequest結構體,用於接收參數 testStruct := requests.TestRequest{} //接收請求參數 err := c.ShouldBind(&testStruct) //判斷參數校驗是否經過,若是不經過,把錯誤返回給前端 if err != nil { c.JSON(http.StatusOK, gin.H{"error": requests.Translate(err)}) return } //校驗經過,返回請求參數 c.JSON(http.StatusOK, gin.H{"params": testStruct}) }
{ "error": { "Username": [ "Username爲必填字段" ] } }
出處 gin從入門到實踐更多精彩文章,請關注個人博客 SOCKSTACK,分享個人工做經驗。