golang/gin之验证器笔记

480 阅读4分钟

参数绑定


import (
	"fmt"

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

// json是json的绑定方式
// form是url绑定方式,自动绑定也是这个字段。例;127.0.0.1:9090/query?name=张三&age=12&sex=男
// uri是另一种url的绑定方式。例;127.0.0.1:9090/uri/张三/12/男
type UserInfo struct {
	Name string `json:"name" form:"name" uri:"name"`
	Age  int    `json:"age" form:"age" uri:"age"`
	Sex  string `json:"sex" form:"sex" uri:"sex"`
}

// 可以将前端传递来的数据与结构体进行参数绑定和校验
func main() {
	r := gin.Default()
	//绑定json参数
	r.POST("/", func(c *gin.Context) {
		var userInfo UserInfo
		//将传过来的json内容绑定到userInfo,如果校验不通过返回err
		err := c.ShouldBindJSON(&userInfo)
		if err != nil {
			c.JSON(200, gin.H{"msg": "你错了"})
			return
		}
		c.JSON(200, userInfo)
	})
	r.POST("/query", func(c *gin.Context) {
		var userInfo UserInfo
		//ShouldBindQuery。根据url或者form表单绑定动态参数,不一定非要query,对应form字段。
		//例:127.0.0.1:9090/query?name=张三&age=12&sex=男
		err := c.ShouldBindQuery(&userInfo)
		if err != nil {
			c.JSON(200, gin.H{"msg": "你错了"})
			return
		}
		c.JSON(200, userInfo)
	})
	///uri/:name/:age/:sex要和参数一一对应,不一定非要uri。例;127.0.0.1:9090/uri/张三/12/男
	r.POST("/uri/:name/:age/:sex", func(c *gin.Context) {
		var userInfo UserInfo
		//ShouldBindUri。根据url和绑定动态参数,对应uri字段。
		//例;127.0.0.1:9090/uri/张三/12/男
		err := c.ShouldBindUri(&userInfo)
		if err != nil {
			c.JSON(200, gin.H{"msg": "你错了"})
			return
		}
		c.JSON(200, userInfo)
	})
	r.POST("/form", func(c *gin.Context) {
		fmt.Println(c.MultipartForm())
		var userInfo UserInfo
		//ShouldBind。自动绑定动态参数,根据form字段来判断
		err := c.ShouldBind(&userInfo)
		if err != nil {
			c.JSON(200, gin.H{"msg": "你错了"})
			return
		}
		c.JSON(200, userInfo)
	})
	r.Run(":9090")
}

参数验证


import (
	"fmt"

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

type SignUserInfo struct {
	Name       string   `json:"name" binding:"required"`                                //用户名,如果加了binding则必须填写而且不能为空,不填会报错
	Age        int      `json:"age" binding:"lt=30,gt=18"`                              //年龄,小于30大于18
	Password   string   `json:"password" binding:"min=4,max=6"`                         //密码,字符串最小是4位,最大是6位
	RePassword string   `json:"re_password" binding:"eqfield=Password"`                 //密码确认,必须和Passord一致,不然会异常
	Sex        string   `json:"sex" binding:"oneof=man woman"`                          //只能是man或者woman
	Fyes       string   `json:"fyes" binding:"contains=f"`                              //字符串必须包含f
	Fno        string   `json:"fno" binding:"excludes=f"`                               //字符串必须不包含f
	Likelist   []string `json:"likelist" binding:"required,dive,min=1,startswith=like"` //数组前缀必须是like才能通过校验
	IP         string   `json:"ip" binding:"ip"`                                        //必须是IP地址
	URL        string   `json:"url" binding:"url"`                                      //必须是url地址
	URI        string   `json:"uri" binding:"uri"`                                      //必须是uri地址,uri是url的子集,相当于/12/31/1,也可以是url,uri的范围比url要广
	Date       string   `json:"date" binding:"datetime=2006-01-02 15:04:06"`            //传时间内容,datetime=2006-01-02 15:04:06相当于格式,必须按照这个格式来传输
}

func main() {
	r := gin.Default()
	r.POST("/", func(c *gin.Context) {
		var user SignUserInfo
		//将前端传来的数据放入结构体,如果类型错误则报错
		err := c.ShouldBindJSON(&user)
		fmt.Println(user.Name)
		if err != nil {
			c.JSON(200, gin.H{"msg": err.Error()})
			return
		}
		c.JSON(200, user)
	})
	r.Run(":9090")
}

// {
//     "name":"\"西游记\"",
//     "age":3,
//     "password":"122332",
//     "re_password":"122332",
//     "sex":"man",
//     "fyes":"manf",
//     "fno":"aaa",
//     "likelist":["likego"],
//     "ip":"255.255.255.255",
//     "url":"http://www.baidu.com/123",
//     "uri":"/12/31/1",
//     "date":"2022-12-07 01:10:11"
// }

自定义错误信息


import (
	"fmt"
	"reflect"

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

type User struct {
	Name string `json:"name" binding:"required" msg:"用户名校验失败"`
	Age  int    `json:"age" binding:"required" msg:"请输入年龄"`
}

// 获取结构体中的msg参数
func GetValidMsg(err error, obj any) string {
	//映射类型
	getObj := reflect.TypeOf(obj).Elem()
	//err其实是一个接口
	//将err接口断言为具体类型validator.ValidationErrors是固定格式,如果是这个格式就走下面
	if errs, ok := err.(validator.ValidationErrors); ok {
		//断言成功
		//errs其实是一个切片,报错会存在多个地方报错,所以肯定是一个数组或者切片
		msg := ""
		for _, e := range errs {
			fmt.Println(e)
			//循环每个错误信息
			//根据报错字段获取结构体的具体字段,比如name字段错误,就返回请输入年龄,e是结构体里的值,e.Field()是报错的字段
			if f, ok2 := getObj.FieldByName(e.Field()); ok2 {
				//获取字段对应的json值
				msg += f.Tag.Get("msg") + " "
			}
		}
		return msg
	}
	return err.Error()
}

func main() {
	r := gin.Default()
	r.POST("/", func(c *gin.Context) {
		var user User
		//进行校验
		err := c.ShouldBindJSON(&user)
		//如果校验未通过
		if err != nil {
			c.JSON(200, gin.H{"msg": GetValidMsg(err, &user)})
			return
		}
		c.JSON(200, gin.H{"data": user})
	})
	r.Run(":9090")
}

自定义验证信息


import (
	"fmt"
	"reflect"

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

type User struct {
	Name string `json:"name" binding:"required,sign" msg:"用户名校验失败"`
	Age  int    `json:"age" binding:"required" msg:"请输入年龄"`
}

// 获取结构体中的msg参数
func GetValidMsg(err error, obj interface{}) string {
	//映射类型
	getObj := reflect.TypeOf(obj).Elem()
	//err其实是一个接口
	//将err接口断言为具体类型validator.ValidationErrors是固定格式,如果是这个格式就走下面
	if errs, ok := err.(validator.ValidationErrors); ok {
		//断言成功
		//errs其实是一个切片,报错会存在多个地方报错,所以肯定是一个数组或者切片
		msg := ""
		for _, e := range errs {
			fmt.Println(e)
			//循环每个错误信息
			//根据报错字段获取结构体的具体字段,比如name字段错误,就返回请输入年龄,e是结构体里的值,e.Field()是报错的字段
			if f, ok2 := getObj.FieldByName(e.Field()); ok2 {
				//获取字段对应的json值
				msg += f.Tag.Get("msg") + " "
			}
		}
		return msg
	}
	return err.Error()
}

func signValid(fl validator.FieldLevel) bool {
	//如果name字段包含以下内容则报错
	for _, nameStr := range []string{"张三", "李四", "王五"} {
		name := fl.Field().Interface().(string)
		if name == nameStr {
			return false
		}
	}
	return true
}

func main() {
	r := gin.Default()
	//注册器
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("sign", signValid)
	}

	r.POST("/", func(c *gin.Context) {
		var user User
		//进行校验
		err := c.ShouldBindJSON(&user)
		//如果校验未通过
		if err != nil {
			c.JSON(200, gin.H{"msg": GetValidMsg(err, &user)})
			return
		}
		c.JSON(200, gin.H{"data": user})
	})
	r.Run(":9090")
}