因爲本人也是golang初學者,因此本教程停留在gin的使用層面,沒有對其實現細節進行剖析,後面有機會再來進行深刻研究。git
gin是目前golang的主要web框架之一,之因此選擇這個框架是由於其擁有高效的路由性能,而且有人長期維護,目前github上的star數已經破3W。本教程適用於新手上路。github
這裏略過go的安裝,直接進入gin的安裝環節,本文經過govendor(管理包依賴工具)進行安裝:golang
go get github.com/kardianos/govendor
複製代碼
mkdir $GOPATH/src/myapp && cd $_
複製代碼
govendor init
govendor fetch github.com/gin-gonic/gin
複製代碼
到這裏已經安裝完畢,此時咱們能夠看到項目中多了一個vendor目錄,此目錄下就是本項目所須要的依賴。web
gin的語法很是簡單,咱們能夠一目瞭然,下面的示例建立了一個gin router,使用了默認的中間件(logger和recovery)。而後建立了一個"/ping"請求的路由監聽。最後啓動服務,默認監聽8080端口。shell
// main.go
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default() // 使用默認中間件(logger和recovery)
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{ // 返回一個JSON,狀態碼是200,gin.H是map[string]interface{}的簡寫
"message": "pong",
})
})
r.Run() // 啓動服務,並默認監聽8080端口
}
複製代碼
執行命令:json
go run main.go
複製代碼
請求方法包括:get, post, patch, delete and options。此外還有any,即任何請求方法都會監聽到。服務器
func main() {
router := gin.Default()
router.GET("/someGet", handle)
router.POST("/somePost", handle)
router.PUT("/somePut", handle)
router.DELETE("/someDelete", handle)
router.PATCH("/somePatch", handle)
router.HEAD("/someHead", handle)
router.OPTIONS("/someOptions", handle)
router.ANY("/any", handle)
router.Run()
}
func handle(context *gin.Context) {
context.String(http.StatusOK, "hello world")
}
複製代碼
分組路由能夠經過router.Group:app
func main() {
router := gin.Default()
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
複製代碼
func main() {
router := gin.Default()
// 匹配/user/john
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 匹配/user/john/和/user/john/send
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
router.Run(":8080")
}
複製代碼
func main() {
router := gin.Default()
// welcome?firstname=Jane&lastname=Doe
router.GET("/user", func(c *gin.Context) {
firstname := c.DefaultQuery("name", "kim") // 獲取query中的name,沒有的話就爲kim
lastname := c.Query("age") // 獲取query中的age
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}
複製代碼
func main() {
router := gin.Default()
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("age")
nick := c.DefaultPostForm("name", "kim")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
router.Run(":8080")
}
複製代碼
curl http://127.0.0.1:8080 -X POST -d 'name=john&age=25'
複製代碼
咱們已經見識了x-www-form-urlencoded
類型的參數處理,如今愈來愈多的應用習慣使用JSON來通訊,也就是不管返回的response仍是提交的request,其content-type類型都是application/json
的格式。而對於一些舊的web表單頁仍是x-www-form-urlencoded
的形式,這就須要咱們的服務器能改hold住這多種content-type的參數了。
因爲go是靜態語言,須要先實現定義數據模型,這就須要用到gin的model bind功能了。框架
gin使用go-playground/validator.v8驗證參數,查看完整文檔。curl
須要在綁定的字段上設置tag,好比,綁定格式爲json,須要這樣設置json:"fieldname"
。
此外,Gin還提供了兩套綁定方法:
Bind
, BindJSON
, BindXML
, BindQuery
, BindYAML
MustBindWith
,若是存在綁定錯誤,請求將被如下指令停止 c.AbortWithError(400, err).SetType(ErrorTypeBind)
,響應狀態代碼會被設置爲400,請求頭Content-Type
被設置爲text/plain; charset=utf-8
。注意,若是你試圖在此以後設置響應代碼,將會發出一個警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422
,若是你但願更好地控制行爲,請使用ShouldBind
相關的方法ShouldBind
, ShouldBindJSON
, ShouldBindXML
, ShouldBindQuery
, ShouldBindYAML
當咱們使用綁定方法時,Gin會根據Content-Type推斷出使用哪一種綁定器,若是你肯定你綁定的是什麼,你可使用MustBindWith
或者BindingWith
。
你還能夠給字段指定特定規則的修飾符,若是一個字段用binding:"required"
修飾,而且在綁定時該字段的值爲空,那麼將返回一個錯誤。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Person struct {
Name string `json:"name" binding:"required"` // json格式從name取值,而且該值爲必須的
Age int `json:"age" binding:"required,gt=20"` // json格式從age取值,而且該值爲必須的,且必須大於20
}
func main() {
router := gin.Default()
router.POST("/test", func(context *gin.Context) {
var person Person
// 這裏我肯定傳過來的必定是JSON因此用ShouldBindJSON,不然能夠用ShouldBind
if err := context.ShouldBindJSON(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
})
router.Run(":3000")
}
複製代碼
curl http://localhost:3000/test -X POST -d '{"name":"kim","age": 10}'
複製代碼
package main
import (
"net/http"
"reflect"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v8"
)
type Booking struct {
// 這裏的驗證方法爲bookabledate
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
// gtfield=CheckIn表示大於的字段爲CheckIn
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 {
// 這裏有兩個知識點,映射和斷言
// 在這裏,field是一個reflect.Type的接口類型變量,經過Interface方法得到field接口類型變量的真實類型,能夠理解爲reflect.Value的逆操做
// 在這裏,斷言就是將一個接口類型的變量轉化爲time.Time,前提是後者必須實現了前者的接口
// 綜上,這裏就是將field進行了類型轉換
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()})
}
}
複製代碼