快速入门
package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.Run() // listen and serve on 0.0.0.0:8080}
restful使用方法,路径参数绑定
func main() {router := gin.Default()// 此规则能够匹配/user/john这种格式,但不能匹配/user/ 或 /user这种格式router.GET("/user/:name", func(c *gin.Context) {name := c.Param("name")c.String(http.StatusOK, "Hello %s", name)})router.Run(":8080")}
获取Get参数
func main() {router := gin.Default()// 匹配的url格式: /welcome?firstname=Jane&lastname=Doerouter.GET("/welcome", func(c *gin.Context) {firstname := c.DefaultQuery("firstname", "Guest")lastname := c.Query("lastname")c.String(http.StatusOK, "Hello %s %s", firstname, lastname)})router.Run(":8080")}
获取Post参数
func main() {router := gin.Default()router.POST("/form_post", func(c *gin.Context) {message := c.PostForm("message")nick := c.DefaultPostForm("nick", "anonymous") // 此方法可以设置默认值c.JSON(200, gin.H{"status": "posted","message": message,"nick": nick,})})router.Run(":8080")}
Get + Post 混合
//POST /post?id=1234&page=1 HTTP/1.1// Content-Type: application/x-www-form-urlencoded//name=manu&message=this_is_greatfunc main() {router := gin.Default()router.POST("/post", func(c *gin.Context) {id := c.Query("id")page := c.DefaultQuery("page", "0")name := c.PostForm("name")message := c.PostForm("message")fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)})router.Run(":8080")}// 结果:id: 1234; page: 1; name: manu; message: this_is_great
真正的绑定技巧来了
其实gin提供了两套绑定方法,先来看看两套方法的概述信息:
Must bind
Methods: Bind, BIndJSON,BindXML,BindYAML
Should bind:
Methods: ShouldBind,ShouldBindJSON,ShouldBindXML, ShouldBindXML,ShouldBindYAML
简单一点说,bindxxxx和shouldbindxxxx的区别:当产生异常的时候,会在header里面添加400的返回信息,而shouldbindxxxx不会。
当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用MustBindWith或者BindingWith。
你还可以给字段指定特定规则的修饰符,如果一个字段用binding:”required”修饰,并且在绑定时该字段的值为空,那么将返回一个错误。如果我们使用binding:”-“,就不会校验这个字段。
但是这个require的key word对于struct却没有作用,这就需要我们使用dive标记
示例:
package mainimport ("fmt""net/http""github.com/gin-gonic/gin")type user struct {Name string `json:"name" binding:"required"`Email string `json:"email" binding:"required,email"`}type data struct {User []user `json:"user" binding:"required,dive"` // use dive tag}//GetDataTest will get test datafunc GetDataTest(c *gin.Context) {var data dataerr := c.Bind(&data)if err == nil {fmt.Printf("%+v", data)c.JSON(http.StatusOK, gin.H{"message": "done",})} else {c.JSON(http.StatusBadRequest, gin.H{"message": err.Error(),})}}func main(){route := gin.Default()route.POST("/", GetDataTest)route.Run(":8080")}//服务报错// $ curl -H "Content-Type: application/json" -X POST --data '{"user": [{"email": "alu@alu.com","name": "alu"},{"email": "","name": "alu"}]}' http://localhost:8080/// {"message":"Key: 'data.User[1].Email' Error:Field validation for 'Email' failed on the 'required' tag"}
有时候我们还可以自定义验证器
package mainimport ("net/http""reflect""time""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""gopkg.in/go-playground/validator.v8")// Booking contains binded and validated data.type Booking struct {CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`}func bookableDate(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,) bool {if date, ok := field.Interface().(time.Time); ok {today := time.Now()if today.Year() > date.Year() || today.YearDay() > date.YearDay() {return false}}return true}func main() {route := gin.Default()if v, ok := binding.Validator.Engine().(*validator.Validate); ok {v.RegisterValidation("bookabledate", bookableDate)}route.GET("/bookable", getBookable)route.Run(":8085")}func getBookable(c *gin.Context) {var b Bookingif err := c.ShouldBindWith(&b, binding.Query); err == nil {c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}}
omitempty的使用
omitempty的作用就是当该字段为空的时候则不输出,这个空可以是根本没有这个字段,也可能是空字符串。
在开发的过程中,遇到了一个小小的误区, 导致了程序出现了一些问题。我定义的数据结构是这样的:
type LoginInfo struct {UserName string `json:"username" binding:"required"`PassWord string `json:"password,omitempty" binding:"required"`ClientID string `json:"clientid" binding:"required"`SockPort string `json:"sockport" binding:"required"`}
在需求中,password可以传空字符串。但是在这个VO的model中,password的tag为:json:"password,omitempty" binding:"required"。经过实践发现,password定义为omitempty同时binding为required,当password为空字符串(“”)的时候,会导致ShouldBindJSON异常。
参考:
go gin框架 binding:”required” 注解不起作用
Gin框架中文文档
gin的BindJSON和ShouldBindJSON,ShouldBindWith的区别
