gin参数验证

一. 结构体验证

        用gin框架的数据验证,可以不用解析数据,减少if else。如下面的代码,如果需要增加判断条件,就需要增加if或者if else。

type MyApi struct {
	a int
	b string
}

func checkMyApi(val *MyApi) bool {
	if val.a == 0 {
		return false
	}
	if val.b != "foo" && val.b != "bar" {
		return false
	}
	return true
}

        1.1 binding标签 

        在 Go 语言中,结构体的binding标签用于指定结构体字段在进行数据绑定(如表单数据绑定或请求体绑定)时的规则和验证。用于gin验证器。

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

type MyApi struct {
	A int       `form:"a" binding:"required"`
	B string    `form:"b" binding:"required,oneof=foo 'bar'"`
	C []float64 `form:"c" binding:"required,gt=0"`
}

func main() {
	r := gin.Default()
	r.GET("/check", func(c *gin.Context) {
		var api MyApi
		if err := c.ShouldBind(&api); err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}
		str := fmt.Sprintf("a:%d, b:%s, c:%#v", api.A, api.B, api.C)
		c.String(http.StatusOK, str)
	})
	r.Run()
}

        required:表示字段不能为对应类型的零值。

        oneof:用于限制字段取值必须是指定的多个值中的一个,多个值之间使用空格分隔。如果字符串本身包含空格,可以使用单引号括起来。

        gt表示greater than大于。对于数字,这将确保值大于给定的值。对于字符串,它检查字符串长度是否大于给定值。对于切片,数组和映射,验证元素的数量。

        常用tag:

required:表示该字段是必需的,不能为空。
min:指定字段的最小值。
max:指定字段的最大值。
eq: 等于,如:binding:“eq=3”
ne: 不等于,如:binding:“ne=12”
gt: 大于
gte: 大于等于
lt: 小于
lte: 小于等于
eqfield: 等于其它字段,如; Password string `bingding:“eqfield=ConfirmPassword”` 表示密码和确认密码一致
nefield: 不等于其它字段
email:验证字段是否为有效的电子邮件地址。如:binding:“email”
url:验证字段是否为有效的 URL。如:binding:“url”
datetime:验证字段是否为有效的日期时间格式。
len:指定字段的长度。
default:指定字段的默认值。
omitempty:指定当字段为空时,绑定时忽略该字段。
oneof:枚举验证,如:binding:“oneof=man woman”, 只能为man或者woman
contains: 字符串验证,包含某字符串,如:binding:“contains=love”
excludes: 字符串验证,不包含某字符串, 如:binding:“contains=money”
startswith 字符串验证,字符串前缀
endswith:字符串验证,字符串后缀
pattern:使用正则表达式验证字段的值。
datetime: 日期格式验证,如:binding:“datetime=2006-01-02 15:04:05”,注:时间必须是2006年1月2号下午3点4分5秒,不可以修改年月日时分秒的值。
忽略字段:binding:“-”

        1.2 dive的使用

         先看一个实例的请求结构体:

type PostAttributeValuesReq struct {
	CreatorId string `binding:"required"` 	// 创建者ID
	Values []struct {
		Value string `binding:"required"` 	// 属性值
		Days uint 							// 天数(计费模式使用)
	} `binding:"required,gt=0"` 			// 属性值数组
}

         假设使用json传参,发现Values.Value字段没有识别出来,也就是说切片元素struct字段的required并没有生效。

{
    "creatorId":"dablelv",
    "values":[{}]
}

        可以使用dive标签,这是告诉验证器深入到切片,数组或映射中,并使用元素标签来验证切片,数组或映射元素。

type PostAttributeValuesReq struct {
	CreatorId string `binding:"required"` 	// 创建者ID
	Values []struct {
		Value string `binding:"required"` 	// 属性值
		Days uint 							// 天数(计费模式使用)
	} `binding:"required,gt=0,dive"` 		// 属性值数组
}

        1.3 validate标签

        validate用于数据验证库的字段验证。

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
)

type MyApi struct {
	A int       `form:"a" validate:"required"`
	B string    `form:"b" validate:"required,oneof=foo 'bar'"`
	C []float64 `form:"c" validate:"required,gt=0"`
}

func main() {
	r := gin.Default()
	r.GET("/check", func(c *gin.Context) {
		var api MyApi
		if err := c.ShouldBind(&api); err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}

		//数据验证
		validate := validator.New()
		if err := validate.Struct(api); err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}

		str := fmt.Sprintf("a:%d, b:%s, c:%#v", api.A, api.B, api.C)
		c.String(http.StatusOK, str)
	})
	r.Run()
}

         validate的常用tag:https://github.com/go-playground/validator/blob/master/README.md

        1.4 binding和validate区别

        上下文:

  • binding:主要用于Web框架的参数绑定,例如:Gin。在请求参数绑定到结构体字段时,验证数据。
  • validate:主要用于数据验证库,例如:go-playground/validator

        错误处理:

  • 在Web框架中,binding通常会导致框架返回HTTP 400 Bad Request错误,指示客户端请求参数不合法。
  • 在数据验证库中,会在验证时产生相应的验证错误,开发者可根据需要进行特别处理。

        使用场景:

  • 适用于Web框架的参数绑定
  • 适用于在通用的数据验证场景中

二. 自定义验证

         使用go-playground/validator包。

        2.1 validator包简介

        validator包是Golang中一个非常受欢迎的数据验证工具,它提供了丰富的验证规则和简单易用的API。使用validator包可以轻松的定义和执行各种验证规则,如必填字段,最大长度,最小值等。同时validator包还支持自定义验证规则,可根据具体业务需求进行扩展。

        validator包安装:

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

        2.2  基本使用

        2.3 自定义规则验证

        除了支持内置的验证规则,validator包还支持自定义验证规则。我们可以通过实现validator.Func类型的函数来定义自己的验证规则。

        利用validator包,使binding注册自定义规则。

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
)

type User struct {
	//2. 在参数binding上使用自定义的校验方法函数注册时的名称
	Name  string `form:"name" binding:"NotNullAndAdmin"`
	Age   int    `form:"age" binding:"gte=0,lte=100"`
	Email string `form:"email" binding:"email"`
}

// 1. 自定义校验方法
// func notNullAndAdmin(c *validator.Validate, topStruct reflect.Value, curStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func notNullAndAdmin(c validator.FieldLevel) bool {
	value := c.Field().String()
	//字段不能为空,并且不等于admin
	return value != "" && !(value == "admin")
}

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

	//3.将我们自定义的校验方法注册到validator中
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("NotNullAndAdmin", notNullAndAdmin)
	}

	r.GET("/check", func(c *gin.Context) {
		var u User
		if err := c.ShouldBind(&u); err != nil {
			c.String(http.StatusBadRequest, err.Error())
		}

		c.String(http.StatusOK, "check pass")
	})
	r.Run()
}

          利用validator包,使validate注册自定义规则。

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
)

type User struct {
	//2. 在参数binding上使用自定义的校验方法函数注册时的名称
	Name  string `form:"name" validate:"NotNullAndAdmin"`
	Age   int    `form:"age" validate:"gte=0,lte=100"`
	Email string `form:"email" validate:"email"`
}

// 1. 自定义校验方法
// func notNullAndAdmin(c *validator.Validate, topStruct reflect.Value, curStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func notNullAndAdmin(c validator.FieldLevel) bool {
	value := c.Field().String()
	//字段不能为空,并且不等于admin
	return value != "" && !(value == "admin")
}

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

	r.GET("/check", func(c *gin.Context) {
		var u User
		if err := c.ShouldBind(&u); err != nil {
			c.String(http.StatusBadRequest, err.Error())
		}

		//3.将我们自定义的校验方法注册到validator中
		validate := validator.New()
		validate.RegisterValidation("NotNullAndAdmin", notNullAndAdmin)
		//数据校验
		err := validate.Struct(u)
		if err != nil {
			c.String(http.StatusBadRequest, err.Error())
		}

		c.String(http.StatusOK, "check pass")
	})
	r.Run()
}

原理是:

        通过反射获取struct中的tag,根据不同的验证规则进行验证。 

三. 多语言翻译验证

        validator库本身使支持国际化的,可以借助相应的语言包实现多语言翻译验证。

        验证器使用的是:

go get gopkg.in/go-playground/validator.v9

         翻译器:

github.com/go-playground/universal-translator

        验证器注册翻译器:

gopkg.in/go-playground/validator.v9/translations/en
gopkg.in/go-playground/validator.v9/translations/zh
gopkg.in/go-playground/validator.v9/translations/zh_tw

        例如:当业务系统对验证信息有特殊需求时,返回信息需要自定义,手机端返回的信息需要时中文,而pc端发挥返回的信息需要是英文,如何做到请求一个借口满足上述三种情况。

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/go-playground/locales/en"
	"github.com/go-playground/locales/zh"
	"github.com/go-playground/locales/zh_Hant_TW"
	ut "github.com/go-playground/universal-translator"
	"gopkg.in/go-playground/validator.v9"
	en_translations "gopkg.in/go-playground/validator.v9/translations/en"
	zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
	zh_tw_translations "gopkg.in/go-playground/validator.v9/translations/zh_tw"
)

var (
	Uni      *ut.UniversalTranslator
	Validate *validator.Validate
	Trans    ut.Translator
)

type User struct {
	Name     string `form:"name" validate:"required"`
	Tagline  string `form:"tag_line" validate:"required,lt=10"`
	Tagline2 string `form:"tag_line2" validate:"required,gt=1"`
}

func main() {
	en := en.New()
	zh := zh.New()
	zh_tw := zh_Hant_TW.New()

	Uni = ut.New(en, zh, zh_tw)
	Validate = validator.New()

	r := gin.Default()

	r.GET("/check", registerTranslation(), startPage)
	r.Run()
}

func registerTranslation() gin.HandlerFunc {
	return func(c *gin.Context) {
		//获得参数
		locale := c.DefaultQuery("locale", "zh")
		//翻译器
		Trans, _ = Uni.GetTranslator(locale)
		//验证器注册翻译器
		switch locale {
		case "zh":
			zh_translations.RegisterDefaultTranslations(Validate, Trans)
		case "en":
			en_translations.RegisterDefaultTranslations(Validate, Trans)
		case "zh_tw":
			zh_tw_translations.RegisterDefaultTranslations(Validate, Trans)
		default:
			zh_translations.RegisterDefaultTranslations(Validate, Trans)
		}
	}
}

func startPage(c *gin.Context) {
	//自定义错误内容
	Validate.RegisterTranslation("required", Trans, func(ut ut.Translator) error {
		return ut.Add("required", "{0} must have val!", true)
	}, func(ut ut.Translator, fe validator.FieldError) string {
		t, _ := ut.T("required", fe.Field())
		return t
	})

	//验证数据
	var u User
	if err := c.ShouldBind(&u); err != nil {
		c.String(http.StatusBadRequest, err.Error())
		return
	}
	fmt.Println(u)

	if err := Validate.Struct(u); err != nil {
		errs := err.(validator.ValidationErrors)
		sliceErr := []string{}

		for _, e := range errs {
			//翻译错误
			sliceErr = append(sliceErr, e.Translate(Trans))
		}
		c.String(http.StatusOK, fmt.Sprintf("%#v", sliceErr))
		return
	}
	c.String(http.StatusOK, fmt.Sprintf("%#v", u))
}

 演示:

  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Gin框架中,参数绑定是将HTTP请求中的数据绑定到Go结构体中的过程。Gin框架支持多种参数绑定方式,包括将查询字符串参数绑定到结构体字段、将POST表单数据绑定到结构体字段、将JSON数据绑定到结构体字段等。 以下是一个示例,展示如何在Gin框架中使用参数绑定: ```go type User struct { Name string `form:"name"` Password string `form:"password"` } func main() { r := gin.Default() r.POST("/login", func(c *gin.Context) { var user User if err := c.ShouldBind(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // TODO: 验证用户名和密码 c.JSON(http.StatusOK, gin.H{"message": "登录成功"}) }) r.Run() // 启动服务 } ``` 在上面的示例中,我们定义了一个`User`结构体,并使用`form`标签指定了每个字段对应的查询字符串参数名。在处理`/login`路由时,我们使用`ShouldBind`方法将HTTP请求中的数据绑定到`User`结构体中,如果绑定失败,则返回一个错误响应。如果绑定成功,则可以使用`User`结构体中的字段进行用户名和密码验证,并返回成功响应。 需要注意的是,Gin框架使用了`ShouldBind`方法来实现参数绑定,这个方法会自动根据HTTP请求的Content-Type字段来选择绑定方式。如果Content-Type为application/json,则会将JSON数据绑定到结构体中;如果Content-Type为application/x-www-form-urlencoded,则会将POST表单数据绑定到结构体中,以此类推。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值