參數校驗錯誤信息中文處理

在上一節咱們介紹到,gin能夠使用ShouldBind方法把參數綁定到結構體,可是沒有介紹到參數校驗的方式,這節咱們來介紹參數校驗和校驗失敗後轉換成中文返回前端。前端

1.數據校驗

下面咱們開始一個簡單的例子:git

  1. 在根目錄的requests目錄下新建一個test_request.go
package requests

//測試請求結構體 該結構體定義了請求的參數和校驗規則
type TestRequest struct {
    Username string `form:"username" binding:"required"`
}
  1. 在根目錄的api目錄下新建一個test.go的控制器,定義test控制器
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})
}
  1. 在根目錄的routers下定義/test路由,分別新建init.go和test.go文件初始化路由.

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)
}
  1. 在main.go中註冊路由
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 上啓動服務
}
  1. 運行並訪問localhost:8081/test,不帶參數會報錯
{
    "error": "Key: 'TestRequest.Username' Error:Field validation for 'Username' failed on the 'required' tag"
}
  1. 運行並訪問localhost:8081/test?username=sockstack,則返回響應的參數。
{
    "params": {
        "Username": "sockstack"
    }
}

上面的例子已經能夠實現參數校驗和接收參數了,可是校驗不經過的時候返回的提示是英文的,下面咱們介紹一下怎麼把錯誤轉成中文返回。api

2.校驗參數失敗提示自動翻譯

經過查看代碼咱們發現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

  1. 在requests目錄下新建init.go文件
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
}
  1. 修改控制器
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})
}
  1. 運行並訪問localhost:8081/test,不帶參數會報錯,可是報錯信息已經翻譯成中文了
{
    "error": {
        "Username": [
            "Username爲必填字段"
        ]
    }
}
出處 gin從入門到實踐更多精彩文章,請關注個人博客 SOCKSTACK,分享個人工做經驗。
相關文章
相關標籤/搜索