道者编程

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":{"分页":["分页为必填字段"],"类别":["类别为必填字段"]}}


 

       
        

最新评论:
我要评论:

看不清楚