前言
Gin文档地址:https://gin-gonic.com/zh-cn/docs/examples/binding-and-validation/
使用gin框架进行模型绑定时,似乎使用两种绑定方法都可以,我很好奇ShouldBindQuery与BindQuery的区别,于是查了一下资料,果然是自己没有好好看文档。
Gin提供了两类绑定方法:
- Must bind
- Methods -
Bind
,BindJSON
,BindXML
,BindQuery
,BindYAML
- Behavior - 这些方法属于
MustBindWith
的具体调用。 如果发生绑定错误,则请求终止,并触发c.AbortWithError(400, err).SetType(ErrorTypeBind)
。响应状态码被设置为 400 并且Content-Type
被设置为text/plain; charset=utf-8
。 如果您在此之后尝试设置响应状态码,Gin会输出日志[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422
。 如果您希望更好地控制绑定,考虑使用ShouldBind
等效方法。
- Methods -
- Should bind
- Methods -
ShouldBind
,ShouldBindJSON
,ShouldBindXML
,ShouldBindQuery
,ShouldBindYAML
- Behavior - 这些方法属于
ShouldBindWith
的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。
- Methods -
ShouldBindQuery与BindQuery测试
测试代码:
type Student struct {
Name string `binding:"required"` //注意此处添加了binding注解,便于测试
}
func main() {
r := gin.Default()
//使用ShouldBindQuery
r.GET("/student1", func(c *gin.Context) {
var student Student
if err := c.ShouldBindQuery(&student); err != nil {
c.JSON(http.StatusOK, gin.H{"msg": "fail"})
} else {
fmt.Println(student)
c.JSON(http.StatusOK, gin.H{"msg": "success"})
}
})
//使用BindQuery
r.GET("/student2", func(c *gin.Context) {
var student Student
if err := c.BindQuery(&student); err != nil {
c.JSON(http.StatusOK, gin.H{"msg": "success"})
} else {
c.JSON(http.StatusOK, gin.H{"msg": "fail"})
}
})
r.Run(":8080")
}
测试结果
访问使用ShouldBindQuery绑定的url:
![1](https://img-blog.csdnimg.cn/047aa2457f644b73b75f8a69189a882f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP6I-c6bih5pys6I-c,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
访问使用BindQuery绑定的url:
![2](https://img-blog.csdnimg.cn/20a383940505404287266741ce595b48.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP6I-c6bih5pys6I-c,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
可以注意到,使用BindQuery绑定模型时,如果发生绑定错误,则会自动返回400响应状态码,并且Content-Type
被设置为 text/plain; charset=utf-8
,这也就是响应的body看起来与ShouldBindQuery不一样的原因。
BindQuery和ShouldBindQuery源码分析
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
func (c *Context) BindQuery(obj interface{}) error {
return c.MustBindWith(obj, binding.Query)
}
// MustBindWith binds the passed struct pointer using the specified binding engine.
// It will abort the request with HTTP 400 if any error occurs.
// See the binding package.
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
if err := c.ShouldBindWith(obj, b); err != nil {
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
return err
}
return nil
}
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
func (c *Context) ShouldBindQuery(obj interface{}) error {
return c.ShouldBindWith(obj, binding.Query)
}
从源码可以看出,BindQuery底层调用了MustBindWith,而MustBindWith调用了ShouldBindWith,因此BindQuery本质上是对ShouldBindQuery再封装。
总结
相同点:都能够将参数与模型进行绑定,操作方法一致。
不同点:当绑定发生错误时(如参数输入错误),BindQuery会自动返回400状态码并将Content-Type
被设置为 text/plain; charset=utf-8
,ShouldBindQuery则不会。