zoukankan      html  css  js  c++  java
  • Go语言:validator库对请求参数校验

    validator库参数校验

    1.介绍

    • validator 库做参数校验是否实用,包括错误翻译等提示

    • 下载

      go get github.com/go-playground/validator/v10
      

    2.gin内置校验

    • 先看一下gin内置validator做校验
    package main
    
    import (
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    
    type SignUpParam struct {
    	Age        uint8  `json:"age" binding:"gte=1,lte=130"`
    	Name       string `json:"name" binding:"required"`
    	Email      string `json:"email" binding:"required,email"`
    	Password   string `json:"password" binding:"required"`
    	RePassword string `json:"re_password" binding:"required,eqfield=Password"`
    }
    func main() {
    	r := gin.Default()
    	r.POST("/signup", func(c *gin.Context) {
    		var u SignUpParam
    		if err := c.ShouldBind(&u);err!=nil{
    			c.JSON(http.StatusOK, gin.H{
    				"msg":err.Error(),
    			})
    			return
    		}
    		// 执行逻辑,保存db
    		c.JSON(http.StatusOK,"success")
    	})
    	_ = r.Run(":28282")
    }
    
    • 我们用postman 发送请求
    {
        "age":28,
        "name":"liSir",
        "email":"300202@qq.com"
    }
    // 会返回来错误信息
    {
        "msg": "Key: 'SignUpParam.Password' Error:Field validation for 'Password' failed on the 'required' tag
    Key: 'SignUpParam.RePassword' Error:Field validation for 'RePassword' failed on the 'required' tag"
    }
    // 可以看到validator检验不通过。但都是英文。
    

    3.如何将校验错误信息翻译成中文

    • validator库支持国际化,借助相应语言包可以实现校验错误提示信息自动翻译。
    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"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"
    	"net/http"
    )
    
    
    type SignUpParam struct {
    	Age        uint8  `json:"age" binding:"gte=1,lte=130"`
    	Name       string `json:"name" binding:"required"`
    	Email      string `json:"email" binding:"required,email"`
    	Password   string `json:"password" binding:"required"`
    	RePassword string `json:"re_password" binding:"required,eqfield=Password"`
    }
    // 全局翻译器T
    var trans ut.Translator
    
    func InitTrans(locale string) (err error){
    	//修改gin框架中Validator引擎属性,实现自定制
    	if v,ok := binding.Validator.Engine().(*validator.Validate);ok{
    		zhT := zh.New()//中文翻译器
    		enT := en.New()//英文翻译器
    		// 第一个参数是备用(fallback)语言环境
    		// 后面参数是应该支持语言环境(可支持多个)
    		uni := ut.New(enT,zhT,enT)
    		// locale通常取决于http请求'Accept-language'
    		var ok bool
    		trans,ok = uni.GetTranslator(locale)
    		if !ok{
    			return fmt.Errorf("uni.GetTranslator(%s) failed", locale)
    		}
    		// 注册翻译器
    		switch locale{
    		case "en":
    			err = enTranslations.RegisterDefaultTranslations(v, trans)
    		case "zh":
    			err = zhTranslations.RegisterDefaultTranslations(v, trans)
    		default:
    			err = enTranslations.RegisterDefaultTranslations(v, trans)
    		}
    		return
    	}
    	return
    }
    func main() {
    	// 注册中文翻译器
    	if err:= InitTrans("zh");err != nil{
    		fmt.Printf("init trans failed,err %v
    ",err)
    		return
    	}
    
    
    	r := gin.Default()
    	r.POST("/signup", func(c *gin.Context) {
    		var u SignUpParam
    		if err := c.ShouldBind(&u);err!=nil{
    			//获取validator.ValidationErrors类型的errors
    			errs,ok := err.(validator.ValidationErrors)
    			// 非validator类型错误直接返回
    			if !ok{
    				c.JSON(http.StatusOK,gin.H{
    					"message":err.Error(),
    				})
    				return
    			}
    			// valodator.ValidationErrors类型错误进行翻译
    			c.JSON(http.StatusOK,gin.H{
    				"message":errs.Translate(trans),
    			})
    		}
    		// 执行逻辑,保存db
    		c.JSON(http.StatusOK,"success")
    	})
    	_ = r.Run(":28282")
    }
    
    • 返回结果
    {
        "message": {
            "SignUpParam.Password": "Password为必填字段",
            "SignUpParam.RePassword": "RePassword为必填字段"
        }
    }"success"
    

    4.自定义错误提示信息字段名

    • 错误信息提示中的字段用自定义的名称,例如json tag指定的值呢?
    if v,ok := binding.Validator.Engine().(*validator.Validate);ok{
    
    		// 注册一个获取json tag的自定义方法
    		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
    			name := strings.SplitN(fld.Tag.Get("json"),",",2)[0]
    			if name == "-"{
    				return ""
    			}
    			return name
    		})
      ....
    
    • 再次发送请求
    {
        "message": {
            "SignUpParam.password": "password为必填字段",
            "SignUpParam.re_password": "re_password为必填字段"
        }
    }"success"
    
    • 可以看到错误提示信息使用就是我们结构体中json tag设置的名称。但是前段提示信息不需要提示SignUpParam,这里可以去掉结构体名称前缀,
    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
    }
    
    • 我们在代码中使用上述翻译后
    if err := c.ShouldBind(&u); err != nil {
    	// 获取validator.ValidationErrors类型的errors
    	errs, ok := err.(validator.ValidationErrors)
    	if !ok {
    		// 非validator.ValidationErrors类型错误直接返回
    		c.JSON(http.StatusOK, gin.H{
    			"msg": err.Error(),
    		})
    		return
    	}
    	// validator.ValidationErrors类型错误则进行翻译
    	// 并使用removeTopStruct函数去除字段名中的结构体名称标识
    	c.JSON(http.StatusOK, gin.H{
    		"msg": removeTopStruct(errs.Translate(trans)),
    	})
    }
    
    • 最终显示效果
    {
        "message": {
            "password": "password为必填字段",
            "re_password": "re_password为必填字段"
        }
    }"success"
    
    • 如果我们想将上面Password字段也改为json
    // SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数
    func SignUpParamStructLevelValidation(sl validator.StructLevel) {
    	su := sl.Current().Interface().(SignUpParam)
    
    	if su.Password != su.RePassword {
    		// 输出错误提示信息,最后一个参数就是传递的param
    		sl.ReportError(su.RePassword, "re_password", "RePassword", "eqfield", "password")
    	}
    }
    
    • 在初始化校验器的函数中注册自定义校验方法即可
    func InitTrans(locale string) (err error) {
    	// 修改gin框架中的Validator引擎属性,实现自定制
    	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    
    		// ... liwenzhou.com ...
        
    		// 为SignUpParam注册自定义校验方法
    		v.RegisterStructValidation(SignUpParamStructLevelValidation, SignUpParam{})
    
    		zhT := zh.New() // 中文翻译器
    		enT := en.New() // 英文翻译器
    
    		// ... liwenzhou.com ...
    }
    

    5.自定义校验方法

    type SignUpParam struct{
      ...
      Date       string `json:"date" binding:"required,datetime=2006-01-02,checkDate"
    }
    

    其中datetime=2006-01-02是内置的用于校验日期类参数是否满足指定格式要求的tag。 如果传入的date参数不满足2006-01-02这种格式就会提示如下错误:

    // customFunc 自定义字段级别校验方法
    func customFunc(fl validator.FieldLevel) bool {
    	date, err := time.Parse("2006-01-02", fl.Field().String())
    	if err != nil {
    		return false
    	}
    	if date.Before(time.Now()) {
    		return false
    	}
    	return true
    }
    
    
    // 在校验器注册自定义的校验方法
    if err := v.RegisterValidation("checkDate", customFunc); err != nil {
    	return err
    }
    

    6.自定义翻译方法

    // registerTranslator 为自定义字段添加翻译功能
    func registerTranslator(tag string, msg string) validator.RegisterTranslationsFunc {
    	return func(trans ut.Translator) error {
    		if err := trans.Add(tag, msg, false); err != nil {
    			return err
    		}
    		return nil
    	}
    }
    
    // translate 自定义字段的翻译方法
    func translate(trans ut.Translator, fe validator.FieldError) string {
    	msg, err := trans.T(fe.Tag(), fe.Field())
    	if err != nil {
    		panic(fe.(error).Error())
    	}
    	return msg
    }
    
    • 定义好相关翻译方法,再初始化
    func InitTrans(locale string) (err error) {
    	// ...liwenzhou.com...
    	
    		// 注册翻译器
    		switch locale {
    		case "en":
    			err = enTranslations.RegisterDefaultTranslations(v, trans)
    		case "zh":
    			err = zhTranslations.RegisterDefaultTranslations(v, trans)
    		default:
    			err = enTranslations.RegisterDefaultTranslations(v, trans)
    		}
    		if err != nil {
    			return err
    		}
    		// 注意!因为这里会使用到trans实例
    		// 所以这一步注册要放到trans初始化的后面
    		if err := v.RegisterTranslation(
    			"checkDate",
    			trans,
    			registerTranslator("checkDate", "{0}必须要晚于当前日期"),
    			translate,
    		); err != nil {
    			return err
    		}
    		return
    	}
    	return
    }
    
  • 相关阅读:
    hud 3336 count the string (KMP)
    JSOI2008星球大战(并查集)
    HAOI2006受欢迎的牛
    十二月个人考核
    十二月个人考核
    CentOS配置Tomcat监听80端口,虚拟主机
    CentOS配置Tomcat监听80端口,虚拟主机
    如何调试一个无法重现的错误?
    如何调试一个无法重现的错误?
    Highcharts的自适应DOM或者DIV,JS方法实现
  • 原文地址:https://www.cnblogs.com/xujunkai/p/13418781.html
Copyright © 2011-2022 走看看