当前位置: 首页 > 科技观察

GolangGinWebFramework4-请求参数绑定和验证

时间:2023-03-12 17:04:04 科技观察

引言本文继上文(GolangGinWebFramework3-自定义日志格式和输出方式/启用日志颜色)继续探索GinWeb框架模型绑定和验证使用模型绑定将请求体绑定到一个Go类型。目前支持JSON、XML、YAML、标准形式(如foo=bar&boo=baz)的Binding。Gin使用go-playground/validator/v10包来验证请求。验证中标签的使用详见(https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags)注意:绑定前请确认要绑定的字段标签在结构与绑定类型一致,比如绑定json,设置标签:json:"fieldname"Gin提供了两种方式(类型)来完成绑定:必须bind1。方法:绑定、BindJSON、BindXML、BindQuery、BindYAML、BindHeader2。特点:这些方法底层使用了MustBindWith方法。如果发生绑定错误,请求将以状态码400结束,返回失败信息:c.AbortWithError(400,err).SetType(ErrorTypeBind),设置响应中的Content-Type头为text/plain;字符集=utf-8。如果您手动设置响应代码,它会警告已设置响应标头,例如:[GIN-debug][WARNING]Headerswerealreadywritten。想用422覆盖状态码400,如果想更好的控制这些行为,推荐使用下面对应的ShoudBind方法。应该绑定1。方法:ShouldBind、ShouldBindJSON、ShouldBindXML、ShouldBindQuery、ShouldBindYAML、ShouldBindHeader2。特点:这些方法在底层使用了ShouldBindWith。如果存在绑定错误,则会返回错误,开发者可以控制并妥善处理这些错误。在使用绑定方法时,Gin会尝试HeadContent-Type标头自动推断要使用的活页夹。如果已经确定需要绑定的类型,可以直接使用底层的MustBindWith或者ShouldBindWith方法。也可以为特殊字段指定required标签值,如果某个字段指定了:binding:"required"标签,但是如果绑定时该字段为空,则会返回错误。例如,以下代码绑定JSON::"required"`//分别定义form,json,xml,binding等标签//Passwordstring`form:"password"json:"password"xml:"password"binding:"required"`Passwordstring`form:"password"json:"password"xml:"password"binding:"-"`}funcmain(){router:=gin.Default()//绑定示例JSON({"user":"manu","password":"123"})router.POST("/loginJSON",func(c*gin.Context){varjsonLoginiferr:=c.ShouldBindJSON(&json);err!=nil{c.JSON(http.StatusBadRequest,gin.H{"error":err.Error()})return}ifjson.User!="manu"||json.Password!="123"{c.JSON(http.StatusUnauthorized,gin.H{"status":"unauthorized"})return}c.JSON(http.StatusOK,gin.H{"status":"youareloggedin"})})//绑定XML示例(//////user//123//)router.POST("/loginXML",func(c*gin.Context){varxmlLoginiferr:=c.ShouldBindXML(&xml);err!=nil{c.JSON(http.StatusBadRequest,gin.H{"error":err.Error()})return}ifxml.User!="manu"||xml.Password!="123"{c.JSON(http.StatusUnauthorized,gin.H{"status":"unauthorized"})return}c.JSON(http.StatusOK,gin.H{"status":"youareloggedin"})})//绑定HTML表单的例子(user=manu&password=123)router.POST("/loginForm",func(c*gin.Context){varformLogin//Thiswillinferwhatbindertousedependingonthecontent-typeheader.iferr:=c.ShouldBind(&form);err!=nil{c.JSON(http.StatusBadRequest,gin.H{"error":err.Error()})return}ifform.User!="manu"||form.Password!="123"{c.JSON(http.StatusUnauthorized,gin.H{"status":"unauthorized"})return}c.JSON(http.StatusOK,gin.H{"status":"youareloggedin"})})//Listenandserveon0.0.0.0:8080router.Run(":8080")}//模拟请求:curl-v-XPOSThttp://localhost:8080/loginJSON-H'content-type:application/json'-d'{"user":"manu","password":"123"}'跳过验证:对应的binding:"required"标签为binding:"-",表示该字段不绑定,所以绑定时字段为空不会报错。您也可以自己注册自定义验证器。示例代码请参考(https://github.com/gin-gonic/examples/blob/master/custom-validation/server.go)packagemainimport("net/http""time""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""github.com/go-playground/validator/v10")//Bookingcontainsbindedandvalidateddata.//Booking结构定义了binder和date验证器标签typeBookingstruct{CheckIntime.Time`form:"check_in"binding:"required,bookabledate"time_format:"2006-01-02"`//结账时间CheckOuttime.Time`form:"check_out"binding:"required,gtfield=CheckIn"time_format:"2006-01-02"`//gtfield=CheckIn表示结账时间必须大于注册时间}//定义日期验证器varbookableDatevalidator.Func=func(flvalidator.FieldLevel)bool{date,ok:=fl.Field().Interface().(time.Time)//使用反射获取字段值->转换为interface->typeassertion(timetype)ifok{today:=time.Now()iftoday.After(date){//如果当前时间在checkIn字段时间之后,则返回false,即不能注册时间早于当前时间returnfalse}}returntrue}funcmain(){route:=gin.Default()//对binding.Validator.Engine()接口进行类型断言,断言类型为Validatestru文化。如果断言成功,自定义验证器会注册到Gin内部ifv,ok:=binding.Validator.Engine().(*validator.Validate);ok{//-如果key已经存在,则之前的验证函数将被替换。该注册方法将替换现有的验证器//-thismethodisnotthread-safeitisintendedthattheseallberegisteredpriortoanyvalidation//注册方式不是线程安全的。在验证开始之前,需要确保所有验证器都注册成功。v.RegisterValidation("bookabledate",bookableDate)}route.GET("/bookable",getBookable)route.Run(":8085")}funcgetBookable(c*gin.Context){varbBookingiferr:=c.ShouldBindWith(&b,binding.Query);err==nil{c.JSON(http.StatusOK,gin.H{"message":"Bookingdatesarevalid!"})}else{c.JSON(http.StatusBadRequest,gin.H{"error":err.Error()})}}//模拟请求://注册时间和结账时间满足条件//$curl"localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17"//{"message":"Bookingdatesarevalid!"}////注册时间晚于checkout时间,不满足gtfield学校Validation规则//$curl"localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09"//{"error":"Key:'Booking.CheckOut'Error:Fieldvalidationfor'CheckOut'failedonthe'gtfield'tag"}////注册时间早于当前时间,不满足自定义验证器//$curl"localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10"//{"error":"Key:'Booking.CheckIn'Error:Fieldvalidationfor'CheckIn'failedonthe'bookabledate'tag"}%另外,结构层的验证通过以下方式注册,v.RegisterStructValidation(UserStructLevelValidation,User{}),请参考struct-lvl-validation示例(https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations)参考文档Gin官方仓库:https://github.com/gin-gonic/gin