demo

gin框架使用github.com/go-playground/validator来进行参数校验,到目前为止已经支持github.com/go-playground/validator/v10,在定义结构体时可以使用 binding tag标识相关校验规则.

-----------------models.verify---------------------
// 结构体中tag传入binding:"required",gin如果使用ShouldBind会自动校验参数,如果必须的字段没传入,就会返回错误
type Verify struct {
    Name       string `json:"name" binding:"required"`
    Password   string `json:"password" binding:"required"`
    Token      int64  `json:"token" binding:"required"`
    RepassWord string `json:"repass-word" binding:"required,eqfield=Password"` // 注意写法
}
----------------controlers.validatorTranslate------------
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"
)
// 定义一个全局翻译器变量trans
var trans ut.Translator

// InitTrans 初始化
func InitTrans(local string) (err error) {
    // 修改gin框架中的Validator引擎属性,实现自定制
    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {

        // 注册一个获取json tag的自定义方法,这样注册后返回的错误信息,字段名就不是结构体的字段名字了而是json的
        v.RegisterTagNameFunc(func(fld reflect.StructField) string {
            name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
            if name == "-" {
                return ""
            }
            return name
        })
        zhT := zh.New() // 中文翻译器
        // 不需要英文的话可以不用传入
        enT := en.New() // 英文翻译器

        // 第一个参数是备用(fallback)的语言环境
        // 后面的参数是应该支持的语言环境(支持多个)
        // uni := ut.New(zhT, zhT) 也是可以的
        uni := ut.New(enT, zhT, enT)

        // local 通常取决于请求头的 'Accept-Language'
        var ok bool
        // 也可以使用 uni.FindTranslator(...) 传入多个local进行查找
        trans, ok = uni.GetTranslator(local)
        if !ok {
            return fmt.Errorf("uni.GetTranslator(%s) failed", local)
        }

        // 注册翻译器
        switch local {
        case "en":
            err = enTranslations.RegisterDefaultTranslations(v, trans)
        case "zh":
            err = zhTranslations.RegisterDefaultTranslations(v, trans)
        default:
            err = enTranslations.RegisterDefaultTranslations(v, trans)
        }
        return
    }
    return
}
--------------controlers.users----------------
// 下面是路由路径函数,r.GET("/signup", controlers.Signify)
func Signify(c *gin.Context) {
    //var p models.Verify
    p := new(models.Verify)
    if err := c.ShouldBindJSON(&p); err != nil {
        // 使用翻译,==先做类型判断==
        if errs, ok := err.(validator.ValidationErrors); !ok {
            fmt.Println("users: failed to translate:", err)
            zap.L().Error("users: failed to translate:")
            return
        } else {
            // trans 是在main中定义的全局变量,翻译器
            // removeTopStruct 去除返回的错误信息中的结构体名称
            c.JSON(http.StatusOK, gin.H{
                "msg": "query string invalid",
                "err": removeTopStruct(errs.Translate(trans)),
            })
        }
        // 下面是未使用翻译
        //data := gin.H{
        //  "msg": "query string invalid",
        //  "err": err.Error(),
        //}
        //c.JSON(http.StatusOK, data)
        return
    }

    // 能走到下面说明起码参数是合法的,再做一些非空的判断
    logic.SetUp(p)
    // 返回响应
    c.JSON(http.StatusOK, gin.H{
        "msg":         "success",
        "valid_token": time.Now().UnixNano(), // 返回时间戳
    })
    return
}

// removeTopStruct 去除返回的错误信息中的结构体名称
func removeTopStruct(fields map[string]string) map[string]string {
    res := map[string]string{}
    for field, err := range fields {
        res[field[strings.Index(field, ".")+1:]] = err
    }
    return res
}
-----------------main.go--------------------
// 初始化gin框架使用的校验器翻译器
    if err := controlers.InitTrans("zh"); err != nil {
        fmt.Println("main: init translate function failed:", err.Error())
        zap.L().Error("main: init translate function failed:", zap.Error(err))
        return
    }

基本使用

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type Verify struct {
    Name       string `json:"name" binding:"required"`
    Password   string `json:"password" binding:"required"`
    Token      int64  `json:"token" binding:"required"`
    RepassWord string `json:"repass-word" binding:"required,eqfield=Password"` // 注意写法
}

func main() {
    r := gin.Default()

    r.POST("/signup", func(c *gin.Context) {
        var p Verify
        if err := c.ShouldBind(&u); err != nil {
            c.JSON(http.StatusOK, gin.H{
                "msg": err.Error(),
            })
            return
        }
        // 保存入库等业务
        // ...........................
        c.JSON(http.StatusOK, "success")
    })

    _ = r.Run(":8081")
}

code view

// 获取json tag的自定义方法,这样错误信息返回的就不是结构体名称,而是json里tag名
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
    name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
    if name == "-" {
        return ""
    }
    return name
})


可以看到返回的错误信息里, Verify.repass-word,包含结构体名称,可以去掉,剽窃的文档issue里的方法
https://github.com/go-playground/validator/issues/633#issuecomment-654382345提供的方法

// 去除返回的错误信息中的结构体名称
func removeTopStruct(fields map[string]string) map[string]string {
    res := map[string]string{}
    for field, err := range fields {
        res[field[strings.Index(field, ".")+1:]] = err
    }
    return res
}
--------------------------------------------
// 调用方法
c.JSON(http.StatusOK, gin.H{
    "msg": "query string invalid",
    "err": removeTopStruct(errs.Translate(trans)),
})
分类: go

站点统计

  • 文章总数:309 篇
  • 分类总数:19 个
  • 标签总数:191 个
  • 运行天数:1009 天
  • 访问总数:128801 人次

浙公网安备33011302000604

辽ICP备20003309号