golang web框架——gin使用教程(一)

因爲本人也是golang初學者,因此本教程停留在gin的使用層面,沒有對其實現細節進行剖析,後面有機會再來進行深刻研究。git

gin是目前golang的主要web框架之一,之因此選擇這個框架是由於其擁有高效的路由性能,而且有人長期維護,目前github上的star數已經破3W。本教程適用於新手上路。github

如何安裝

這裏略過go的安裝,直接進入gin的安裝環節,本文經過govendor(管理包依賴工具)進行安裝:golang

  1. 安裝govendor
go get github.com/kardianos/govendor
複製代碼
  1. 在gopath下建立項目目錄並進入
mkdir $GOPATH/src/myapp && cd $_
複製代碼
  1. 用govendor初始化項目並拉取gin
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")
}
複製代碼

請求參數獲取

path中的參數

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")
}
複製代碼

query中的參數

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")
}
複製代碼

multipart/urlencoded form中的參數

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還提供了兩套綁定方法:

  • Must bind
    • Methods - BindBindJSONBindXMLBindQueryBindYAML
    • Behavior - 這些方法底層使用 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相關的方法
  • Should bind
    • Methods - ShouldBindShouldBindJSONShouldBindXMLShouldBindQueryShouldBindYAML
    • Behavior - 這些方法底層使用 ShouldBindWith,若是存在綁定錯誤,則返回錯誤,開發人員能夠正確處理請求和錯誤。

當咱們使用綁定方法時,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()})
	}
}
複製代碼
相關文章
相關標籤/搜索