Gin框架自带参数校验的使用详解

 更新时间:2023年09月19日 10:53:52   作者:uccs  
这篇文章主要为大家详细介绍了如何使用Gin框架自带的参数校验,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解下

gin 框架内置参数验证,写在 binging tag 中,如下所示:

type Tag struct {
  ID   int32  `json:"id" binding:"required"`
  Name string `json:"name" binding:"required"`
}

这个验证器是由 validator 提供的,文档

常用的操作符:

  • ,:且,多个验证之间同时满足
  • |:或,满足其中一个
  • -:跳过验证
  • =:等于

例子来源于官方文档:

type User struct {
  FirstName      string     `binding:"required"`
  LastName       string     `binding:"required"`
  Age            uint8      `binding:"gte=0,lte=130"`
  Email          string     `binding:"required,email"` // 验证是否是一个有效的 email 地址
  Gender         string     `binding:"oneof=male female prefer_not_to` // oneof 表示只能是其中之一 可以用 eq=male|eq=female|eq=prefer_not_to 代替
  FavouriteColor string     `binding:"iscolor"`                // iscolor 表示是否是一个有效的颜色值
  Addresses      []*Address `binding:"required,dive,required"` // dive 对嵌套结构体进行递归验证
}
type Address struct {
  Street string `binding:"required"`
  City   string `binding:"required"`
  Planet string `binding:"required"`
  Phone  string `binding:"required"`
}

在我们日常中会经常做手机号验证,但是官方只有一个 e164 的验证,e164 是国际通用标准(要加区号),就不太符合国内用户

这就需要自定义验证器 mobile,根据官网提供的例子,实现它也是比较简单的

自定义验证器

首先定义一个 ValidateMobile 方法

import (
  "regexp"
  "github.com/go-playground/validator/v10"
)
func ValidateMobile(fl validator.FieldLevel) bool {
  mobile := fl.Field().String()
  ok, _ := regexp.MatchString(`^1([38][0-9]|14[579]|5[^4]16[6]|7[1-35-8]|9[189])\d{8}$`, mobile) // 用正则去匹配
  return ok
}

将方法注册到 validator

import (
  "github.com/gin-gonic/gin/binding"
)
// 从 gin 中获取到 validator 验证器,然后注册 mobile
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
  _ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
}

自定义验证器就定义好了,使用的时候就可以这样写了

type User struct {
  Name    string `json:"name" binding:"required"`
  Mobile  string `json:"mobile" binding:"required,mobile"`
}

错误信息翻译

validator 默认错误信息是英文,如果需要翻译成中文,自己做转换:

  • 跳过 json tag- 的字段
  • 实例化 zhen 翻译包
  • 初始化翻译器
  • 注册翻译器
  • 调用 InitTrans,传入 zh 或者 en 即可

具体代码如下:

import (
  "fmt"
  "mxshop_api/order_web/global"
  "reflect"
  "strings"
  "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"
  en_translations "github.com/go-playground/validator/v10/translations/en"
  zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
func InitTrans(local string) (err error) {
  // 修改 gin 中 validator 实现定制
  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]
      // 如果 json tag 为 - 则不处理
      if name == "-" {
        return ""
      }
      return name
    })
    zhT := zh.New()
    enT := en.New()
    // 第一个参数是备用的语言环境,后面的参数是应该支持的语言环境
    uni := ut.New(enT, zhT, enT)
    // 初始化翻译器
    global.Trans, ok = uni.GetTranslator(local)
    if !ok {
      return fmt.Errorf("uni.GetTranslator(%s)", local)
    }
    switch local {
    case "en":
      en_translations.RegisterDefaultTranslations(v, global.Trans)
    case "zh":
      zh_translations.RegisterDefaultTranslations(v, global.Trans)
    default:
      en_translations.RegisterDefaultTranslations(v, global.Trans)
    }
    return
  }
  return
}

使用:

if err := initialize.InitTrans("zh"); err != nil {
  panic(err)
}

这个翻译器只能够翻译内置的错误信息,自定义的验证器需要自己翻译

比如我们上面定义的 mobile 验证器,如果验证失败,返回的错误信息需要我们自己翻译

参照官方例子,如下代码:

// 四个参数
// 需要翻译的字段名 翻译器实例 翻译方法 返回错误的信息
_ = validator.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
  // 翻译的内容
  return ut.Add("mobile", "{0} 非法的手机号码!", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
  // fe.Field() 获取到的是字段名,而不是 json tag
  t, _ := ut.T("mobile", fe.Field())
  return t
})

这样就可以翻译自定义的验证器了,最终代码如下:

// 注册验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
  _ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
  _ = v.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
    return ut.Add("mobile", "{0} 非法的手机号码!", true) // see universal-translator for details
  }, func(ut ut.Translator, fe validator.FieldError) string {
    t, _ := ut.T("mobile", fe.Field())
    return t
  })
}

格式化返回信息

validator 默认返回的错误信息是 json tag,这不是我们希望的

我们希望抛出去的错误是结构化的

  • 使用 shouldBindJSON 将参数接写到对应的结构体中
  • 如果解析失败,则断言这个错误为 validator.ValidationErrors
  • 如果断言成功,则将错误进行翻译
  • 去掉结构体中的前缀,并生成一个 map
  • 将这个 map 抛出去

最终代码如下:

if err := ctx.ShouldBindJSON(&bannerForm); err != nil {
  HandleValidatorError(ctx, err)
  return
}
func HandleValidatorError(c *gin.Context, err error) {
  errs, ok := err.(validator.ValidationErrors)
  if !ok {
    c.JSON(http.StatusOK, gin.H{
      "msg": err.Error(),
    })
  }
  c.JSON(http.StatusBadRequest, gin.H{
    "error": removeTopStruct(errs.Translate(global.Trans)),
  })
  return
}
func removeTopStruct(fields map[string]string) map[string]string {
  rsp := map[string]string{}
  for field, err := range fields {
    // 去掉结构体中的前缀 {User.mobile: "mobile 非法的手机号码!"}
    rsp[field[strings.Index(field, ".")+1:]] = err
  }
  return rsp
}

错误类型在文档中有说明type FieldError

  • Tag():得到的是你写的 requiremobile
  • Field():结构体中定义的字段名
  • Translate():翻译

总结

validator 能够满足我们日常的需求:

  • 对嵌套结构体进行递归验证
  • 自定义验证器
  • 错误信息翻译
  • 格式化返回信息

到此这篇关于Gin框架自带参数校验的使用详解的文章就介绍到这了,更多相关Gin参数校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Kotlin编程基础语法编码规范

    Kotlin编程基础语法编码规范

    这篇文章主要为大家介绍了Kotlin编程条件控制示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • GO语io包的常用接口

    GO语io包的常用接口

    这篇文章主要介绍了GO语io包的常用接口,分析了GO语言接口的概念与功能,并实例列举了几个最常用的接口的用法,具有一定的参考借鉴价值,需要的朋友可以参考下
    2014-12-12
  • go语言优雅地处理error工具及技巧详解

    go语言优雅地处理error工具及技巧详解

    这篇文章主要为大家介绍了go语言优雅地处理error工具及技巧详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 重学Go语言之变量与常量的声明与使用详解

    重学Go语言之变量与常量的声明与使用详解

    变量、常量的声明与使用是掌握一门编程语言的基础,在这篇文章中,小编就来带大家学习一下Go语言是怎么样声明和使用变量与常量吧
    2023-03-03
  • Golang文件读写操作详情

    Golang文件读写操作详情

    这篇文章主要介绍了Golang文件读写操作详情,文件是数据源(保存数据的地方)的一种,文件最主要的作用就是保存数据,文件在程序中是以流的形式来操作的,更多详细内容需要的朋友可以参考一下
    2022-07-07
  • Go语言变量创建的五种方法

    Go语言变量创建的五种方法

    这篇文章主要介绍了Go语言五种变量创建的方法,本文给大家提到了匿名变量的优点,通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2020-02-02
  • 一文详解在Go中如何使用Viper来管理配置

    一文详解在Go中如何使用Viper来管理配置

    Viper 是一个功能齐全的 Go 应用程序配置库,支持很多场景。在本文中,我们将深入探讨 Viper 的各种用法和使用场景,以帮助读者更好地了解和使用 Viper 来管理应用程序配置,感兴趣的同学可以参考阅读
    2023-05-05
  • Golang并发编程之调度器初始化详解

    Golang并发编程之调度器初始化详解

    这篇文章主要为大家详细介绍了Golang并发编程中关于调度器初始化的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2023-03-03
  • Golang中的sync包的WaitGroup操作

    Golang中的sync包的WaitGroup操作

    这篇文章主要介绍了Golang中的sync包的WaitGroup操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golong字符串拼接性能优化及原理介绍

    Golong字符串拼接性能优化及原理介绍

    最近在做性能优化,有个函数里面的耗时特别长,看里面的操作大多是一些字符串拼接的操作,而字符串拼接在 golang 里面其实有很多种实现,下面这篇文章主要给大家介绍了关于Golang语言如何高效拼接字符串的相关资料,需要的朋友可以参考下
    2023-04-04

最新评论