介绍

Validator 是一个 Go 的第三方库,用于对数据进行校验,返回一个 validator.ValidationErrors error 的切片,在 Tag 中配置各自字段的约束进行校验。

依赖

# 最基础的依赖
go get -u github.com/go-playground/validator/v10

# 国际化
go get -u github.com/go-playground/locales
go get -u github.com/go-playground/universal-translator
# 中文语言包
go get -u github.com/go-playground/locales/zh
# 中文校验器工具 用于注册
go get -u github.com/go-playground/validator/v10/translations/zh

简单测试

下面这个是最基础的配置

func Test_Validator(t *testing.T) {
    type User struct {
        UserName string `json:"user_name" validate:"required"`
        Password string `json:"password" validate:"required,min=6,max=20"`
    }
    u := &User{
        Password: "123",
    }
    validate := validator.New()

    if errs := validate.Struct(u); errs != nil {
        for _, err := range errs.(validator.ValidationErrors) {
            fmt.Println(err)
        }
    }
}
Key: 'User.UserName' Error:Field validation for 'UserName' failed on the 'required' tag
Key: 'User.Password' Error:Field validation for 'Password' failed on the 'min' tag

国际化

不是很明白这种模式,逻辑有点混乱,需要一个 RegisterDefaultTranslations 方法连接校验器和翻译器吗?

自己尝试画了一个图,不是很对,用于自己理解

image-20230802221354107

注册时需要注意给包命名

import (
    "fmt"
    "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"
    "testing"
)
func Test_Validator_Local(t *testing.T) {
    type User struct {
        UserName string `json:"user_name" validate:"required"`
        Password string `json:"password" validate:"required,min=6,max=20"`
    }
    u := &User{
        Password: "123",
    }
    uni := ut.New(zh.New())
    zhTranslator, _ := uni.GetTranslator("zh")
    //if !found {
    //  fmt.Println("zhTranslator not found.", found)
    //  return
    //}
    validate := validator.New()
    if err := zh_translations.RegisterDefaultTranslations(validate, zhTranslator); err != nil {
        fmt.Println(err)
    }
    if errs := validate.Struct(u); errs != nil {
        for _, err := range errs.(validator.ValidationErrors) {
            fmt.Println(err.Translate(zhTranslator))
        }
    }
}
UserName为必填字段
Password长度必须至少为6个字符

校验规则

更多的参考官方的:Baked-in Validations

Tag 说明 示例
required 必填 validate:"required"
min 最小值 validate:"required,min=1"
ip IP 格式 validate:"ip"
gt 大于 validate:"gt=3"
gte 大于等于 validate:"gte=3"
eqfield 等于某个字段 validate:"eqfiled=Password"

gin 中使用

gin 中内置 validator,但是使用的 Tag 名是 binding

package main

import (
    "errors"
    "fmt"
    "github.com/gin-gonic/gin"
    "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"
    "reflect"
    "strings"
)

type User struct {
    Username string `json:"username" binding:"required,gte=3,lte=8"`
    Password string `json:"password" binding:"required,gte=8,lte=16"`
    Sex      uint8  `json:"sex" binding:"required"`
    Email    string `json:"email" binding:"required,email"`
    Age      int    `json:"age"`
}

var trans ut.Translator

func main() {
    r := gin.Default()
    if err := InitTrans(); err != nil {
        fmt.Println(err)
        return
    }
    r.POST("/signup.do", func(ctx *gin.Context) {
        u := new(User)
        if err := ctx.ShouldBindJSON(u); err != nil {
            errs, ok := err.(validator.ValidationErrors)
            var msg string = "param is error"
            if ok {
                msg = ""
                for _, validateErr := range errs {
                    msg = fmt.Sprintf("%s,%s", msg, validateErr.Translate(trans))
                }
            }
            ctx.JSON(200, msg)
        } else {
            ctx.JSON(200, "signup success")
        }
    })

    r.Run(":9999")
}

func InitTrans() error {
    if validate, ok := binding.Validator.Engine().(*validator.Validate); ok {
        uni := ut.New(zh.New())
        trans, _ = uni.GetTranslator("zh")
        zh_translations.RegisterDefaultTranslations(validate, trans)

        validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
            name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
            if name == "-" {
                return ""
            }
            return name
        })

        return nil
    } else {
        return errors.New("get gin Validator failed")
    }
}

请求测试

  • 第一组

    {
        "username":"xxcheng",
        "password":"12345678",
        "email":"www@qq"
    }
    ",sex为必填字段,email必须是一个有效的邮箱"
  • 第二组

    {
        "username":"xxcheng",
        "password":"123456",
        "sex":2
    }
    ",password长度必须至少为8个字符,email为必填字段"

参考链接

文章目录