gin 参数验证

5 篇文章 0 订阅

前言

gin的参数验证是集成了 https://github.com/go-playground

参数验证

使用 gin.Context中ShouldBind(obj)方法
obj 通过tag设置验证规则

接收参数字段设置

//json:“json_name” 代表json格式收取json_name参数值到Name
//form:"form_name" 代表表单格式收取form_name参数值到Name
//xml:"xml_name" 代表xml格式收取xml_name参数到Name
type Register struct {
	Name string `json:"json_name" form:"form_name" xml:"xml_name"`
}

参数条件限制
更多tag 请查看官方文档(https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-Using_Validator_Tags)

//binding:后为条件限制
//required:必填
//min:最小长度
//max:最大长度

type Register struct {
	Name string `json:"json_name" form:"form_name" xml:"xml_name" binding:"required,min=3,max=10"`
}

输出错误信息

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

type Register struct {
	Name string `json:"json_name" form:"form_name" xml:"xml_name" binding:"required,min=3,max=10"`
}

func main() {
	r := gin.Default()

	r.POST("/register", func(c *gin.Context) {
		r := &Register{}
		err := c.ShouldBind(r)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})

	r.Run(":8080")
}

验证信息中文处理

未进行中文处理的验证错误信息是这个样子的:Key: ‘Register.Name’ Error:Field validation for ‘Name’ failed on the ‘required’ tag

首先先注册翻译器,然后创建通用翻译器,从通用翻译器中获取指定的翻译器(封装过的),然后将其绑定到gin的验证器上。
验证器返回的错误转换成 go-playground 的 ValidationErrors类型,调用Translate 指定翻译器进行翻译

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"
	en_translation "github.com/go-playground/validator/v10/translations/en"
	zh_translation "github.com/go-playground/validator/v10/translations/zh"
	"net/http"
)

type Register struct {
	Name string `json:"json_name" form:"form_name" xml:"xml_name" binding:"required,min=3,max=10"`
}

var trans ut.Translator

func initTranslator(language string) error {
	//转换成go-playground的validator
	validate, ok := binding.Validator.Engine().(*validator.Validate)
	if ok {
		//创建翻译器
		zhT := zh.New()
		enT := en.New()

		//创建通用翻译器
		//第一个参数是备用语言,后面的是应当支持的语言
		uni := ut.New(enT, enT, zhT)

		//从通过中获取指定语言翻译器
		trans, ok = uni.GetTranslator(language)
		if !ok {
			return fmt.Errorf("not found translator %s", language)
		}

		//绑定到gin的验证器上,对binding的tag进行翻译
		switch language {
		case "zh":
			err := zh_translation.RegisterDefaultTranslations(validate, trans)
			if err != nil {
				return err
			}
		default:
			err := en_translation.RegisterDefaultTranslations(validate, trans)
			if err != nil {
				return err
			}
		}

	}

	return nil
}

func main() {

	err := initTranslator("zh")
	if err != nil {
		panic(err)
	}
	r := gin.Default()

	r.POST("/register", func(c *gin.Context) {
		r := &Register{}
		err := c.ShouldBind(r)
		if err != nil {
			if errors, ok := err.(validator.ValidationErrors); ok {
				//调用指定翻译器进行翻译
				c.JSON(http.StatusBadRequest, gin.H{
					"error": errors.Translate(trans),
				})
			} else {
				c.JSON(http.StatusBadRequest, gin.H{
					"error": err.Error(),
				})
			}

			return
		}

		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})

	r.Run(":8080")
}

返回的错误格式处理

上面返回的错误格式是这个样子的

{
    "error": {
        "Register.Name": "Name为必填字段"
    }
}

首先我通过JSON格式请求的,error中的key应该是json_name,value
应该是json_name为必填字段
理想型数据结构

{
    "error": {
        "json_name": "json_name为必填字段"
    }
}

json_name 可以通过 RegisterTagNameFunc 设置返回字段json_name,但是就算设置json_name,但是key还是 Register.json_name。
解决方案:对错误结果进行处理,剔除 Register 部分

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"
	en_translation "github.com/go-playground/validator/v10/translations/en"
	zh_translation "github.com/go-playground/validator/v10/translations/zh"
	"net/http"
	"reflect"
	"strings"
)

type Register struct {
	Name string `json:"json_name" form:"form_name" xml:"xml_name" binding:"required,min=3,max=10"`
}

var trans ut.Translator

func initTranslator(language string) error {
	//转换成go-playground的validator
	validate, ok := binding.Validator.Engine().(*validator.Validate)

	validate.RegisterTagNameFunc(func(field reflect.StructField) string {
		// json:"json_name" form:"form_name" xml:"xml_name" binding:"required,min=3,max=10"
		// 设置返回 json tag 中的内容 如:json_names
		return field.Tag.Get("json")
	})

	if ok {
		//创建翻译器
		zhT := zh.New()
		enT := en.New()

		//创建通用翻译器
		//第一个参数是备用语言,后面的是应当支持的语言
		uni := ut.New(enT, enT, zhT)

		//从通过中获取指定语言翻译器
		trans, ok = uni.GetTranslator(language)
		if !ok {
			return fmt.Errorf("not found translator %s", language)
		}

		//对binding的tag进行翻译
		switch language {
		case "zh":
			err := zh_translation.RegisterDefaultTranslations(validate, trans)
			if err != nil {
				return err
			}
		default:
			err := en_translation.RegisterDefaultTranslations(validate, trans)
			if err != nil {
				return err
			}
		}

	}

	return nil
}

func remove(errors map[string]string) map[string]string {
	result := map[string]string{}
	for key, value := range errors {
		result[key[strings.Index(key, ".")+1:]] = value
	}
	return result
}

func main() {

	err := initTranslator("zh")
	if err != nil {
		panic(err)
	}
	r := gin.Default()

	r.POST("/register", func(c *gin.Context) {
		r := &Register{}
		err := c.ShouldBind(r)
		if err != nil {
			if errors, ok := err.(validator.ValidationErrors); ok {
				//调用指定翻译器进行翻译
				c.JSON(http.StatusBadRequest, gin.H{
					//调用自定义函数去除Register部分
					"error": remove(errors.Translate(trans)),
				})
			} else {
				c.JSON(http.StatusBadRequest, gin.H{
					"error": err.Error(),
				})
			}

			return
		}

		c.JSON(http.StatusOK, gin.H{
			"message": "ok",
		})
	})

	r.Run(":8080")
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值