Gin参数验证-Validator
在进行api开发的时候,参数验证是极其频繁的一步,选择一个验证轮子很重要。validator是一个开源的验证器包,可以快速校验输入信息是否符合自定规则。
访问地址:https://github.com/go-playground/validator
一:安装到我们项目中:
go get github.com/go-playground/validator/v10然后在合适的位置导入包:
import "github.com/go-playground/validator/v10"
1:单个变量验证
package main
import (
"fmt"
"github.com/go-playground/validator/v10" //导入包
)
func main() {
validate := validator.New() //创建一个实例
email := "" //验证这个字段
err := validate.Var(email, "required") // required 判断是否为空
if err != nil {
fmt.Println(err.Error()) //如果为空,返回错误提示
}
}这里返回:Key: '' Error:Field validation for '' failed on the 'required' tag
下面增加判断条件,在不为空的基础上,增加一个是否是合法的email
email := "addd#eeee" //验证这个字段 err := validate.Var(email, "required,email") // required 判断是否为空,email判断是否是一个合法的email返回:Key: '' Error:Field validation for '' failed on the 'email' tag
细心的同学可能会发现一个问题,返回的提示信息是哪里来的?其实这个信息是validator默认的,而且这个提示很不友好,那么能不能修改一下?如果单个验证的话,可以直接把err.Error()换成你需要的提示语即可。还有个方法,单个变量可以通过注册规则和替换来实现,然后通过翻译来实现,这里有翻译包,一会儿再介绍
2:结构体验证
package main
import (
"fmt"
"github.com/go-playground/validator/v10" //导入包
)
func main() {
validate := validator.New() //创建一个实例
// 结构体定义验证规则
type User struct {
ID int64 `json:"id" validate:"gt=0"` //大于 0
Name string `json:"name" validate:"required"` // 不能为空
Gender string `json:"gender" validate:"required,oneof=man woman"` // 不能为空,只能为man woman
Age uint8 `json:"age" validate:"required,gte=0,lte=130"` // 大于等于0,小于等于130
Email string `json:"email" validate:"required,email"` // 不能为空,检测邮箱是否合法
}
user := &User{
ID: 12,
Name: "jecken",
Gender: "boy",
Age: 190,
Email: "abc@88.com",
}
err := validate.Struct(user)
if err != nil {
fmt.Println(err.Error()) //如果为空,返回错误提示
}
}
返回:Key: 'User.Gender' Error:Field validation for 'Gender' failed on the 'oneof' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'lte' tag
这个返回的还是不太好看,我们美化一下。
我们先下载2个东西:
go get github.com/go-playground/universal-translator //翻译器 go get github.com/go-playground/locales //本地化多语言库
然后代码是这样的:
package main
import (
"fmt"
"reflect"
"strings"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
"github.com/go-playground/validator/v10"
ut "github.com/go-playground/universal-translator"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
func main() {
// 创建一个新的翻译器和翻译映射表
enTranslator := en.New()
zhTranslator := zh.New()
trans := ut.New(enTranslator, zhTranslator)
// 设置翻译语言
uniTranslator, _ := trans.GetTranslator("zh")
validate := validator.New() //创建一个验证实例
//验证器注册翻译器
errs := zh_translations.RegisterDefaultTranslations(validate, uniTranslator)
if errs != nil {
fmt.Println(errs)
}
//提取结构体中的tag标签 lable
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := fld.Tag.Get("label")
return name
})
// 结构体定义验证规则
type User struct {
ID int64 `json:"id" validate:"gt=0" label:"id"` //大于 0
Name string `json:"name" validate:"required" label:"姓名"` // 不能为空
Gender string `json:"gender" validate:"required,oneof=man woman" label:"性别"` // 不能为空,只能为man woman
Age uint8 `json:"age" validate:"required,gte=0,lte=130" label:"年龄"` // 大于等于0,小于等于130
Email string `json:"email" validate:"required,email" label:"邮箱"` // 不能为空,检测邮箱是否合法
}
user := &User{
ID: 12,
Name: "",
Gender: "boy",
Age: 190,
Email: "abc@88.com",
}
err := validate.Struct(user)
if err != nil {
errs, ok := err.(validator.ValidationErrors)
if !ok {
fmt.Println(err.Error()) //非validator.ValidationErrors类型错误直接返回
}
fmt.Println(removeTopStruct(errs.Translate(uniTranslator))) // validator.ValidationErrors类型错误则进行翻译
}
}
//移除其 key 中包含“.”的键,并将其余部分作为新的键
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
}
返回:map[姓名:姓名为必填字段 年龄:年龄必须小于或等于130 性别:性别必须是[man woman]中的一个]3:我们把这玩意儿弄到GIN中去,搞一个模块单独调用,这里新建一个validator.go
package validator
import (
"fmt"
"reflect"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
"github.com/go-playground/validator/v10"
ut "github.com/go-playground/universal-translator"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
var Trans ut.Translator //全局翻译器
// 设置,init方法在golang会自动执行
func init() {
uni := ut.New(en.New(), zh.New())
// 设置翻译语言
t, _ := uni.GetTranslator("zh")
Trans = t //赋给全局变量
//获取gin的校验器,这里就不用上面那个 validator.New()了,用GIN框架的,它继承了
validate := binding.Validator.Engine().(*validator.Validate)
//注册翻译器
errs := zh_translations.RegisterDefaultTranslations(validate, Trans)
if errs != nil {
fmt.Println(errs)
}
//提取结构体中的tag标签 lable
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := fld.Tag.Get("label")
return name
})
}
//返回提示
func Fails(err error) string {
errors := err.(validator.ValidationErrors)
for _, err := range errors {
return err.Translate(Trans) //返回第一个就行了
}
return ""
}
然后在我们所谓的控制器中调用,搞两个参数,page和category,GET form方式绑定这两个参数验证
搞一个结构体绑定
type CateRequest struct {
//注意这里是form,单独使用用validate,这里用gin的binding
Category string `form:"category" binding:"required" label:"类别"`
Page string `form:"page" binding:"required" label:"分页"`
}
把我们上面这个validator.go import进来func Cate(ctx *gin.Context) {
var c CateRequest
err := ctx.BindQuery(&c)
if err != nil {
ctx.JSON(200, gin.H{"message": validator.Fails(err)})
return
}
}
执行一下:{"message":"类别为必填字段"}如果要返回全部提示,我们可以validator.go再加一个方法:
//返回全部提示
func FailsAll(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
} 执行:{"message":{"分页":["分页为必填字段"],"类别":["类别为必填字段"]}}