快速入门
package main
import "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=Doe
router.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_great
func 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 main
import (
"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 data
func GetDataTest(c *gin.Context) {
var data data
err := 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 main
import (
"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 Booking
if 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的区别