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