当前位置: 首页 > 工具软件 > go-validator > 使用案例 >

字段参数验证库-validator及其翻译器的使用

酆耀
2023-12-01

安装

  • go get:go get github.com/go-playground/validator/v10
  • import:import “github.com/go-playground/validator/v10”

一般使用方式

  1. 给结构体表上validate标签,并指明限制
  2. 新建验证器
  3. 验证字段
  4. 遍历输出错误信息

1、验证

标记之间特殊符号说明
  • 逗号( ,):把多个验证标记隔开。注意:隔开逗号之间不能有空格, validate:“lt=0,gt=100”,逗号那里不能有空格,否则panic
  • 横线( - ):跳过该字段不验证
  • 竖线( | ):使用多个验证标记,但是只需满足其中一个即可
  • required:表示该字段值必输设置,且不能为默认值
  • omitempty:如果字段未设置,则忽略它
范围比较验证

范围验证: 切片、数组和map、字符串,验证其长度;数值,验证大小范围

  • lte:小于等于参数值,validate:“lte=3” (小于等于3)
  • gte:大于等于参数值,validate:“lte=120,gte=0” (大于等于0小于等于120)
  • lt:小于参数值,validate:“lt=3” (小于3)
  • gt:大于参数值,validate:“lt=120,gt=0” (大于0小于120)
  • len:等于参数值,validate:“len=2”
  • max:最大值,小于等于参数值,validate:“max=20” (小于等于20)
  • min:最小值,大于等于参数值,validate:“min=2,max=20” (大于等于2小于等于20)
  • ne:不等于,validate:“ne=2” (不等于2)
  • oneof:只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,validate:“oneof=red green”
字符串验证
  • contains:包含参数子串,validate:“contains=tom” (字段的字符串值包含tom)
  • excludes:包含参数子串,validate:“excludes=tom” (字段的字符串值不包含tom)
  • startswith:以参数子串为前缀,validate:“startswith=golang”
  • endswith:以参数子串为后缀,validate:“startswith=world”
字段验证
  • eqcsfield:跨不同结构体字段验证,比如说 Struct1 Filed1,与结构体Struct2 Field2相等,
    validate:eqcsfield=Struct2.Field2
  • necsfield:跨不同结构体字段不相等
  • eqfield:同一结构体字段验证相等,最常见的就是输入2次密码验证
  • nefield:同一结构体字段验证不相等
  • gtefield:大于等于同一结构体字段,validate:“gtefiled=Field2”
  • ltefield:小于等于同一结构体字段
网络验证
  • ip:字段值是否包含有效的IP地址,validate:“ip”
  • ipv4:字段值是否包含有效的ipv4地址,validate:“ipv4”
  • ipv6:字段值是否包含有效的ipv6地址,validate:“ipv6”
  • uri:字段值是否包含有效的uri,validate:“uri”
  • url:字段值是否包含有效的uri,validate:“url”
Format
  • base64:字段值是否包含有效的base64值
自定义验证函数
  • 通过字段tag自定义函数
package main

import (
	"fmt"

	"github.com/go-playground/validator/v10"
)

type User struct {
	Name string `form:"name" json:"name" validate:"required,CustomerValidation"` //注意:required和CustomerValidation之间不能有空格,否则panic。CustomerValidation:自定义tag-函数标签
	Age  uint8  ` form:"age" json:"age" validate:"gte=0,lte=80"`                 //注意:gte=0和lte=80之间不能有空格,否则panic
}

var validate *validator.Validate

func main() {
	validate = validator.New()
	validate.RegisterValidation("CustomerValidation", CustomerValidationFunc) //注册自定义函数,前一个参数是struct里tag自定义,后一个参数是自定义的函数

	user := &User{
		Name: "jimmy",
		Age:  86,
	}

	fmt.Println("first value: ", user)
	err := validate.Struct(user)
	if err != nil {
		fmt.Printf("Err(s):\n%+v\n", err)
	}

	user.Name = "tom"
	user.Age = 29
	fmt.Println("second value: ", user)
	err = validate.Struct(user)
	if err != nil {
		fmt.Printf("Err(s):\n%+v\n", err)
	}
}

// 自定义函数
func CustomerValidationFunc(f1 validator.FieldLevel) bool {
    // f1 包含了字段相关信息
    // f1.Field() 获取当前字段信息
    // f1.Param() 获取tag对应的参数
    // f1.FieldName() 获取字段名称
    
	return f1.Field().String() == "jimmy"
}
  • 自定义函数-直接注册函数1
package main

import (
	"fmt"

	"github.com/go-playground/validator/v10"
)

type User struct {
	FirstName      string `json:firstname`
	LastName       string `json:lastname`
	Age            uint8  `validate:"gte=0,lte=130"`
	Email          string `validate:"required,email"`
	FavouriteColor string `validate:"hexcolor|rgb|rgba"`
}

var validate *validator.Validate

func main() {
	validate = validator.New()

	validate.RegisterStructValidation(UserStructLevelValidation, User{})

	user := &User{
		FirstName:      "",
		LastName:       "",
		Age:            30,
		Email:          "TestFunc@126.com",
		FavouriteColor: "#000",
	}

	err := validate.Struct(user)
	if err != nil {
		fmt.Println(err)
	}
}

func UserStructLevelValidation(sl validator.StructLevel) {
	user := sl.Current().Interface().(User)

	if len(user.FirstName) == 0 && len(user.LastName) == 0 {
		sl.ReportError(user.FirstName, "FirstName", "firstname", "firstname", "")
		sl.ReportError(user.LastName, "LastName", "lastname", "lastname", "")
	}
}
  • 自定义函数-直接注册函数2
package main

import (
	"database/sql"
	"database/sql/driver"
	"fmt"
	"reflect"

	"github.com/go-playground/validator/v10"
)

type DbBackedUser struct {
	Name sql.NullString `validate:"required"`
	Age  sql.NullInt64  `validate:"required"`
}

var validate *validator.Validate

func main() {
	validate = validator.New()

	validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})

	// build object for validation
	x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}

	err := validate.Struct(x)
	if err != nil {
		fmt.Printf("Err(s):\n%+v\n", err)
	}
}

func ValidateValuer(field reflect.Value) interface{} {
	if valuer, ok := field.Interface().(driver.Valuer); ok {
		val, err := valuer.Value()
		if err == nil {
			return val
		}
		// handle the error how you want
	}
	return nil
}

2、新建验证器

validate := validator.New()

3、验证字段

验证单个字段
  • func (v *Validate) Var(field interface{}, tag string) error
  • 实例
var emailTest string = "test@126.com" 
err = validate.Var(emailTest, "email")
验证结构体
  • func (v *Validate) Struct(s interface{}) error
  • 实例
user := &User{
		FirstName: "Badger",
		LastName:  "Smith",
		Age:       135,
		Email:     "Badger.Smith@gmail.com",
		Addresses: []*Address{address},
	}
err := validate.Struct(user)
验证slice
  • 和验证单个字段使用同一个函数:func (v *Validate) Var(field interface{}, tag string) error
  • 不同的是,需要使用一个tag关键字:dive
  • 实例1
sliceone := []string{"123", "onetwothree", "myslicetest", "four", "five"} 
err := validate.Var(sliceone, "max=15,dive,min=4")

说明:如实例1,dive前面的是验证 len(sliceone) 最大长度为15,后面的时候验证sliceone内的每一个参数 长度最小为4

  • 实例2:二维切片
slicethree := [][]string{}
validate.Var(slicethree, "min=2,dive,len=2,dive,required")
validate.Var(slicethree, "min=2,dive,dive,required")
验证map
  • 用 dive 关键字区分 是对map进行验证,还是对map内key,value进行验证,对 key 的验证,在写在 keys 和 endkeys 两个tag内;剩下的才是对 value值的验证
  • 实例1:
mapone = map[string]string{"one": "jimmmy", "two": "", "1234": "123"}
err := validate.Var(mapone, "gte=3,dive,keys,min=4,endkeys,required"

说明:gte=3是对len(mapone)的验证;min=4是对mapone中的key进行验证;required是对value进行验证

  • 实例2:嵌套map验证
maptwo := map[[3]string]string{[3]string{"123","345","567"}:"123"}
validate.Var(maptwo, "gte=3,dive,keys,dive,eq=1|eq=3,endkeys,required")

3、输出错误信息

  • 简单输出:
if err != nil {
		fmt.Println(err)
}
  • 遍历输出:
for _, err := range errs.(validator.ValidationErrors) {
		fmt.Println(err.Error())
}

validator翻译器

  • get:go get github.com/go-playground/universal-translator
    • 中文:go get github.com/go-playground/validator/v10/translations/zh
    • 英文:go get github.com/go-playground/validator/v10/translations/en

使用

  • 步骤:

    • 1.新建需要的语言
    • 2.将语言配置到翻译器中
    • 3.获取需要的语言
    • 4.把翻译器注册到validator中
    • 5.翻译错误信息
  • 实例:

package main

import (
	"fmt"
	"strings"

	"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"

	zhtrans "github.com/go-playground/validator/v10/translations/zh"
	// entrans "github.com/go-playground/validator/v10/translations/en"
)

type Student struct {
	Name  string `validate:required`
	Email string `validate:"email"`
	Age   int    `validate:"max=30,min=12"`
}

func main() {
	// 1.新建需要的语言
	en := en.New() //英文翻译器
	zh := zh.New() //中文翻译器

	// 2.将语言配置到翻译器中
	// 第一个参数是必填,如果没有其他的语言设置,就用这第一个
	// 后面的参数是支持多语言环境(
	// uni := ut.New(en, en) 也是可以的
	// uni := ut.New(en, zh, tw)
	uni := ut.New(en, zh)
	// 3.获取需要的语言
	trans, _ := uni.GetTranslator("zh")

	student := Student{
		Name:  "tom",
		Email: "testemal",
		Age:   40,
	}
	validate := validator.New()
	// 4.把翻译器注册到validate中
	zhtrans.RegisterDefaultTranslations(validate, trans)

	err := validate.Struct(student)
	if err != nil {
		// fmt.Println(err)

		errs := err.(validator.ValidationErrors)
		// 5.翻译错误信息
		fmt.Println(removeStructName(errs.Translate(trans)))
		fmt.Println(errs.Translate(trans))
	}
}

func removeStructName(fields map[string]string) map[string]string {
	result := map[string]string{}

	for field, err := range fields {
		result[field[strings.Index(field, ".")+1:]] = err
	}
	return result
}

Gin框架使用Validator

  1. 在对应结构体使用 binding 标签,指明限制
  2. 使用Gin框架的ShouldBind* 方法接受参数,这些方法会自动通过 binding 标签校验参数

Gin框架结合翻译器使用Validator

  1. 新建需要的语言
  2. 将语言配置到翻译器中
  3. 获取需要的语言
  4. 获取validator
  5. 把翻译器注册到validator中
  6. 翻译错误信息
package main

import (
	"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"
	"net/http"
)

type Person struct {
	Name string `form:"name" binding:"max=30,ne=Admin"`
	Age  int    `form:"age" binding:"max=200,min=0"`
	Sex  string `form:"sex" binding:"oneof=woman man"`
}

func main() {
	r := gin.Default()
	r.Use(translation())
	//_ = binding.Validator.Engine().(validator.Validate)
	r.POST("/addPerson", func(ctx *gin.Context) {
		trans, _ := ctx.Get("trans")
		var person Person
		err := ctx.ShouldBind(&person)
		if err != nil {
			errs := err.(validator.ValidationErrors)
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"msg": "failed",
				"err": errs.Translate(trans.(ut.Translator)),
			})
			return
		}
		fmt.Println(person)
		name := ctx.PostForm("name")
		fmt.Println(name)
		ctx.JSON(http.StatusOK, gin.H{"msg": "success"})
	})
	r.Run()
}

func translation() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		//1. 新建需要的语言
		zh := zh.New()
		//2. 将语言配置到翻译器中
		uni := ut.New(zh)
		//3. 获取需要的语言
		trans, _ := uni.GetTranslator("zh")
		//4. 获取validator
		v := binding.Validator.Engine().(*validator.Validate)
		_ = zh_translations.RegisterDefaultTranslations(v, trans)
		ctx.Set("trans", trans)
		ctx.Next()
	}
}
 类似资料: