Go語言web框架 gin

gin是go語言環境下的一個web框架, 它相似於Martini, 官方聲稱它比Martini有更好的性能, 比Martini快40倍, Ohhhh….看着不錯的樣子, 因此就想記錄一下gin的學習. gin的github代碼在這裏: gin源碼. gin的效率得到如此日新月異, 得益於另外一個開源項目httprouter, 項目地址: httprouter源碼. 下面主要記錄一下gin的使用.html


###1. 安裝gin 使用命令go get github.com/gin-gonic/gin就能夠. 咱們使用gin的時候引入相應的包就OKimport "github.com/gin-gonic/gin".git


###2. 使用方法github

<1> 一種最簡單的使用GET/POST方法

gin服務端代碼是:web

// func1: 處理最基本的GET
func func1 (c *gin.Context)  {
    // 回覆一個200OK,在client的http-get的resp的body中獲取數據
    c.String(http.StatusOK, "test1 OK")
}
// func2: 處理最基本的POST
func func2 (c *gin.Context) {
    // 回覆一個200 OK, 在client的http-post的resp的body中獲取數據
    c.String(http.StatusOK, "test2 OK")
}
func main(){
    // 註冊一個默認的路由器
    router := gin.Default()
    // 最基本的用法
    router.GET("/test1", func1)
    router.POST("/test2", func2)
    // 綁定端口是8888
    router.Run(":8888")
}

客戶端代碼是:json

func main(){
    // 調用最基本的GET,並得到返回值
    resp,_ := http.Get("http://0.0.0.0:8888/test1")
    helpRead(resp)

    // 調用最基本的POST,並得到返回值
    resp,_ = http.Post("http://0.0.0.0:8888/test2", "",strings.NewReader(""))
    helpRead(resp)
}

在服務端, 實例化了一個router, 而後使用GET和POST方法分別註冊了兩個服務, 當咱們使用HTTP GET方法的時候會使用GET註冊的函數, 若是使用HTTP POST的方法, 那麼會使用POST註冊的函數. gin支持全部的HTTP的方法例如: GET, POST, PUT, PATCH, DELETE 和 OPTIONS等. 看客戶端中的代碼, 當調用http.Get("http://0.0.0.0:8888/test1")的時候, 服務端接收到請求, 並根據/test1將請求路由到func1函數進行 處理. 同理, 調用http.Post("http://0.0.0.0:8888/test2", "",strings.NewReader(""))時候, 會使用func2函數處理. 在func1和func2中, 使用gin.Context填充了一個String的回覆. 固然也支持JSON, XML, HTML等其餘一些格式數據. 當執行c.String或者c.JSON時, 至關於向http的回覆緩衝區寫入了 一些數據. 最後調用router.Run(「:8888」)開始進行監聽,Run的核心代碼是:瀏覽器

func (engine *Engine) Run(addr string) (err error) {
	debugPrint("Listening and serving HTTP on %s\n", addr)
	defer func() { debugPrintError(err) }()
	// 核心代碼
	err = http.ListenAndServe(addr, engine)
	return
}

其本質就是http.ListenAndServe(addr, engine). 
注意: helpRead函數是用於讀取response的Body的函數, 你能夠本身定義, 本文中此函數定義爲:服務器

// 用於讀取resp的body
func helpRead(resp *http.Response)  {
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("ERROR2!: ", err)
    }
    fmt.Println(string(body))
}

<2> 傳遞參數

傳遞參數有幾種方法, 對應到gin使用幾種不一樣的方式來解析.app

**第一種:** 使用gin.Context中的Param方法解析

對應的服務端代碼爲:框架

// func3: 處理帶參數的path-GET
func func3(c *gin.Context)  {
    // 回覆一個200 OK
    // 獲取傳入的參數
    name := c.Param("name")
    passwd := c.Param("passwd")
    c.String(http.StatusOK, "參數:%s %s  test3 OK", name, passwd)
}
// func4: 處理帶參數的path-POST
func func4(c *gin.Context)  {
    // 回覆一個200 OK
    // 獲取傳入的參數
    name := c.Param("name")
    passwd := c.Param("passwd")
    c.String(http.StatusOK, "參數:%s %s  test4 OK", name, passwd)
}
// func5: 注意':'和'*'的區別
func func5(c *gin.Context)  {
    // 回覆一個200 OK
    // 獲取傳入的參數
    name := c.Param("name")
    passwd := c.Param("passwd")
    c.String(http.StatusOK, "參數:%s %s  test5 OK", name, passwd)
}

func main(){
    router := gin.Default()
    // TODO:注意':'必需要匹配,'*'選擇匹配,即存在就匹配,不然能夠不考慮
    router.GET("/test3/:name/:passwd", func3)
    router.POST("/test4/:name/:passwd", func4)
    router.GET("/test5/:name/*passwd", func5)

    router.Run(":8888")
}

客戶端測試代碼是:ide

func main() {
    // GET傳參數,使用gin的Param解析格式: /test3/:name/:passwd
    resp,_ = http.Get("http://0.0.0.0:8888/test3/name=TAO/passwd=123")
    helpRead(resp)

    // POST傳參數,使用gin的Param解析格式: /test3/:name/:passwd
    resp,_ = http.Post("http://0.0.0.0:8888/test4/name=PT/passwd=456", "",strings.NewReader(""))
    helpRead(resp)

    // 注意Param中':'和'*'的區別
    resp,_ = http.Get("http://0.0.0.0:8888/test5/name=TAO/passwd=789")
    helpRead(resp)
    resp,_ = http.Get("http://0.0.0.0:8888/test5/name=TAO/")
    helpRead(resp)
}

注意上面定義參數的方法有兩個輔助符號: ‘:’和’*’. 若是使用’:’參數方法, 那麼這個參數是必需要匹配的, 例如上面的router.GET(「/test3/:name/:passwd」, func3), 當請求URL是 相似於http://0.0.0.0:8888/test3/name=TAO/passwd=123這樣的參會被匹配, 若是是http://0.0.0.0:8888/test3/name=TAO 或者http://0.0.0.0:8888/test3/passwd=123是不能匹配的. 可是若是使用’*‘參數, 那麼這個參數是可選的. router.GET(「/test5/:name/*passwd」, func5) 能夠匹配http://0.0.0.0:8888/test5/name=TAO/passwd=789, 也能夠匹配http://0.0.0.0:8888/test5/name=TAO/. 須要注意的一點是, 下面這個URL是否是可以 匹配呢? http://0.0.0.0:8888/test5/name=TAO, 注意TAO後面沒有’/’, 這個其實就要看有沒有一個路由是到http://0.0.0.0:8888/test5/name=TAO路徑的, 若是有, 那麼指定的那個函數進行處理, 若是沒有http://0.0.0.0:8888/test5/name=TAO會被重定向到http://0.0.0.0:8888/test5/name=TAO/, 而後被當前註冊的函數進行處理.

 

**第二種:** 使用gin.Context中的Query方法解析

這個相似於正常的URL中的參數傳遞, 先看服務端代碼:

// 使用Query獲取參數
func func6(c *gin.Context)  {
    // 回覆一個200 OK
    // 獲取傳入的參數
    name := c.Query("name")
    passwd := c.Query("passwd")
    c.String(http.StatusOK, "參數:%s %s  test6 OK", name, passwd)
}
// 使用Query獲取參數
func func7(c *gin.Context)  {
    // 回覆一個200 OK
    // 獲取傳入的參數
    name := c.Query("name")
    passwd := c.Query("passwd")
    c.String(http.StatusOK, "參數:%s %s  test7 OK", name, passwd)
}

func main(){
    router := gin.Default()
    // 使用gin的Query參數形式,/test6?firstname=Jane&lastname=Doe
    router.GET("/test6", func6)
    router.POST("/test7", func7)

    router.Run(":8888")
}

客戶端測試代碼是:

func main() {
    // 使用Query獲取參數形式/test6?firstname=Jane&lastname=Doe
    resp,_ = http.Get("http://0.0.0.0:8888/test6?name=BBB&passwd=CCC")
    helpRead(resp)
    resp,_ = http.Post("http://0.0.0.0:8888/test7?name=DDD&passwd=EEE", "",strings.NewReader(""))
    helpRead(resp)
}

這種方法的參數也是接在URL後面, 形如http://0.0.0.0:8888/test6?name=BBB&passwd=CCC. 服務器可使用name := c.Query(「name」)這種 方法來解析參數.

 

**第三種:** 使用gin.Context中的PostForm方法解析

咱們須要將參數放在請求的Body中傳遞, 而不是URL中. 先看服務端代碼:

// 參數是form中得到,即從Body中得到,忽略URL中的參數
func func8(c *gin.Context)  {
    message := c.PostForm("message")
    extra := c.PostForm("extra")
    nick := c.DefaultPostForm("nick", "anonymous")

    c.JSON(200, gin.H{
        "status":  "test8:posted",
        "message": message,
        "nick":    nick,
        "extra": extra,
    })
}

func main(){
    router := gin.Default()
    // 使用post_form形式,注意必需要設置Post的type,
    // 同時此方法中忽略URL中帶的參數,全部的參數須要從Body中得到
    router.POST("/test8", func8)

    router.Run(":8888")
}

客戶端代碼是:

func main() {
    // 使用post_form形式,注意必需要設置Post的type,同時此方法中忽略URL中帶的參數,全部的參數須要從Body中得到
    resp,_ = http.Post("http://0.0.0.0:8888/test8", "application/x-www-form-urlencoded",strings.NewReader("message=8888888&extra=999999"))
    helpRead(resp)
}

因爲咱們使用了request Body, 那麼就須要指定Body中數據的形式, 此處是form格式, 即application/x-www-form-urlencoded. 常見的幾種http提交數據方式有: application/x-www-form-urlencoded; multipart/form-data; application/json; text/xml. 具體使用請google.
在服務端, 使用message := c.PostForm(「message」)方法解析參數, 而後進行處理.

<3> 傳輸文件

下面測試從client傳輸文件到server. 傳輸文件須要使用multipart/form-data格式的數據, 全部須要設定Post的類型是multipart/form-data. 
首先看服務端代碼:

// 接收client上傳的文件
// 從FormFile中獲取相關的文件data!
// 而後寫入本地文件
func func9(c *gin.Context) {
    // 注意此處的文件名和client處的應該是同樣的
    file, header , err := c.Request.FormFile("uploadFile")
    filename := header.Filename
    fmt.Println(header.Filename)
    // 建立臨時接收文件
    out, err := os.Create("copy_"+filename)
    if err != nil {
        log.Fatal(err)
    }
    defer out.Close()
    // Copy數據
    _, err = io.Copy(out, file)
    if err != nil {
        log.Fatal(err)
    }
    c.String(http.StatusOK, "upload file success")
}

func main(){
    router := gin.Default()
    // 接收上傳的文件,須要使用
    router.POST("/upload", func9)

    router.Run(":8888")
}

客戶端代碼是:

func main() {
    // 上傳文件POST
    // 下面構造一個文件buf做爲POST的BODY
    buf := new(bytes.Buffer)
    w := multipart.NewWriter(buf)
    fw,_ := w.CreateFormFile("uploadFile", "images.png") //這裏的uploadFile必須和服務器端的FormFile-name一致
    fd,_ := os.Open("images.png")
    defer fd.Close()
    io.Copy(fw, fd)
    w.Close()
    resp,_ = http.Post("http://0.0.0.0:8888/upload", w.FormDataContentType(), buf)
    helpRead(resp)
}

首先客戶端本地須要有一張」images.png」圖片, 同時須要建立一個Form, 並將field-name命名爲」uploadFile」, file-name命名爲」images.png」. 在服務端, 經過」uploadFile」能夠獲得文件信息. 客戶端繼續將圖片數據copy到建立好的Form中, 將數據數據Post出去, 注意數據的類型指定! 在服務端, 經過file, header , err := c.Request.FormFile(「uploadFile」)得到文件信息, file中就是文件數據, 將其拷貝到本地文件, 完成文件傳輸.

 

<4> binding數據

gin內置了幾種數據的綁定例如JSON, XML等. 簡單來講, 即根據Body數據類型, 將數據賦值到指定的結構體變量中. (相似於序列化和反序列化) 
看服務端代碼:

// Binding數據
// 注意:後面的form:user表示在form中這個字段是user,不是User, 一樣json:user也是
// 注意:binding:"required"要求這個字段在client端發送的時候必須存在,不然報錯!
type Login struct {
    User     string `form:"user" json:"user" binding:"required"`
    Password string `form:"password" json:"password" binding:"required"`
}
// bind JSON數據
func funcBindJSON(c *gin.Context) {
    var json Login
    // binding JSON,本質是將request中的Body中的數據按照JSON格式解析到json變量中
    if c.BindJSON(&json) == nil {
        if json.User == "TAO" && json.Password == "123" {
            c.JSON(http.StatusOK, gin.H{"JSON=== status": "you are logged in"})
        } else {
            c.JSON(http.StatusUnauthorized, gin.H{"JSON=== status": "unauthorized"})
        }
    } else {
        c.JSON(404, gin.H{"JSON=== status": "binding JSON error!"})
    }
}

// 下面測試bind FORM數據
func funcBindForm(c *gin.Context) {
    var form Login
    // 本質是將c中的request中的BODY數據解析到form中

    // 方法一: 對於FORM數據直接使用Bind函數, 默認使用使用form格式解析,if c.Bind(&form) == nil
    // 方法二: 使用BindWith函數,若是你明確知道數據的類型
    if c.BindWith(&form, binding.Form) == nil{
        if form.User == "TAO" && form.Password == "123" {
            c.JSON(http.StatusOK, gin.H{"FORM=== status": "you are logged in"})
        } else {
            c.JSON(http.StatusUnauthorized, gin.H{"FORM=== status": "unauthorized"})
        }
    } else {
        c.JSON(404, gin.H{"FORM=== status": "binding FORM error!"})
    }
}

func main(){
    router := gin.Default()
    // 下面測試bind JSON數據
    router.POST("/bindJSON", funcBindJSON)

    // 下面測試bind FORM數據
    router.POST("/bindForm", funcBindForm)

    // 下面測試JSON,XML等格式的rendering
    router.GET("/someJSON", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "hey, budy", "status": http.StatusOK})
    })

    router.GET("/moreJSON", func(c *gin.Context) {
        // 注意:這裏定義了tag指示在json中顯示的是user不是User
        var msg struct {
            Name    string `json:"user"`
            Message string
            Number  int
        }
        msg.Name = "TAO"
        msg.Message = "hey, budy"
        msg.Number = 123
        // 下面的在client的顯示是"user": "TAO",不是"User": "TAO"
        // 因此整體的顯示是:{"user": "TAO", "Message": "hey, budy", "Number": 123
        c.JSON(http.StatusOK, msg)
    })

    //  測試發送XML數據
    router.GET("/someXML", func(c *gin.Context) {
        c.XML(http.StatusOK, gin.H{"name":"TAO", "message": "hey, budy", "status": http.StatusOK})
    })

    router.Run(":8888")
}

客戶端代碼:

func main() {
    // 下面測試binding數據
    // 首先測試binding-JSON,
    // 注意Body中的數據必須是JSON格式
    resp,_ = http.Post("http://0.0.0.0:8888/bindJSON", "application/json", strings.NewReader("{\"user\":\"TAO\", \"password\": \"123\"}"))
    helpRead(resp)

    // 下面測試bind FORM數據
    resp,_ = http.Post("http://0.0.0.0:8888/bindForm", "application/x-www-form-urlencoded", strings.NewReader("user=TAO&password=123"))
    helpRead(resp)

    // 下面測試接收JSON和XML數據
    resp,_ = http.Get("http://0.0.0.0:8888/someJSON")
    helpRead(resp)
    resp,_ = http.Get("http://0.0.0.0:8888/moreJSON")
    helpRead(resp)
    resp,_ = http.Get("http://0.0.0.0:8888/someXML")
    helpRead(resp)
}

客戶端發送請求, 在服務端能夠直接使用c.BindJSON綁定到Json結構體上. 或者使用BindWith函數也能夠, 可是須要指定綁定的數據類型, 例如JSON, XML, HTML等. Bind*函數的本質是讀取request中的body數據, 拿BindJSON爲例, 其核心代碼是:

func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
	// 核心代碼: decode請求的body到obj中
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err != nil {
		return err
	}
	return validate(obj)
}

<5> router group

router group是爲了方便前綴相同的URL的管理, 其基本用法以下. 
首先看服務端代碼:

// router GROUP - GET測試
func func10(c *gin.Context)  {
    c.String(http.StatusOK, "test10 OK")
}
func func11(c *gin.Context)  {
    c.String(http.StatusOK, "test11 OK")
}

// router GROUP - POST測試
func func12(c *gin.Context)  {
    c.String(http.StatusOK, "test12 OK")
}
func func13(c *gin.Context)  {
    c.String(http.StatusOK, "test13 OK")
}

func main(){
    router := gin.Default()
    // router Group是爲了將一些前綴相同的URL請求放在一塊兒管理
    group1 := router.Group("/g1")
    group1.GET("/read1", func10)
    group1.GET("/read2", func11)

    group2 := router.Group("/g2")
    group2.POST("/write1", func12)
    group2.POST("/write2", func13)

    router.Run(":8888")
}

客戶端測試代碼:

func main() {
    // 下面測試router 的GROUP
    resp,_ = http.Get("http://0.0.0.0:8888/g1/read1")
    helpRead(resp)
    resp,_ = http.Get("http://0.0.0.0:8888/g1/read2")
    helpRead(resp)
    resp,_ = http.Post("http://0.0.0.0:8888/g2/write1", "", strings.NewReader(""))
    helpRead(resp)
    resp,_ = http.Post("http://0.0.0.0:8888/g2/write2", "", strings.NewReader(""))
    helpRead(resp)
}

在服務端代碼中, 首先建立了一個組group1 := router.Group(「/g1」), 並在這個組下注冊了兩個服務, group1.GET(「/read1」, func10) 和group1.GET(「/read2」, func11), 那麼當使用http://0.0.0.0:8888/g1/read1和http://0.0.0.0:8888/g1/read2訪問時, 是能夠路由 到上面註冊的位置的. 同理對於group2 := router.Group(「/g2」)也是同樣的.

 

<6> 靜態文件服務

能夠向客戶端展現本地的一些文件信息, 例如顯示某路徑下地文件. 服務端代碼是:

func main(){
    router := gin.Default()
    // 下面測試靜態文件服務
    // 顯示當前文件夾下的全部文件/或者指定文件
    router.StaticFS("/showDir", http.Dir("."))
    router.Static("/files", "/bin")
    router.StaticFile("/image", "./assets/1.png")

    router.Run(":8888")
}

首先你須要在服務器的路徑下建立一個assert文件夾, 而且放入1.png文件. 若是已經存在, 請忽略. 
測試代碼: 請在瀏覽器中輸入0.0.0.0:8888/showDir, 顯示的是服務器當前路徑下地文件信息:

1

輸入0.0.0.0:8888/files, 顯示的是/bin目錄下地文件信息:

2

輸入0.0.0.0:8888/image, 顯示的是服務器下地./assets/1.png圖片:

3

 

<7> 加載模板templates

gin支持加載HTML模板, 而後根據模板參數進行配置並返回相應的數據. 
看服務端代碼

func main(){
    router := gin.Default()
    // 下面測試加載HTML: LoadHTMLTemplates
    // 加載templates文件夾下全部的文件
    router.LoadHTMLGlob("templates/*")
    // 或者使用這種方法加載也是OK的: router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
    router.GET("/index", func(c *gin.Context) {
        // 注意下面將gin.H參數傳入index.tmpl中!也就是使用的是index.tmpl模板
        c.HTML(http.StatusOK, "index.tmpl", gin.H{
            "title": "GIN: 測試加載HTML模板",
        })
    })

    router.Run(":8888")
}

客戶端測試代碼是:

func main() {
    // 測試加載HTML模板
    resp,_ = http.Get("http://0.0.0.0:8888/index")
    helpRead(resp)
}

在服務端, 咱們須要加載須要的templates, 這裏有兩種方法: 第一種使用LoadHTMLGlob加載全部的正則匹配的模板, 本例中使用的是*, 即匹配全部文件, 因此加載的是 templates文件夾下全部的模板. 第二種使用LoadHTMLFiles加載指定文件. 在本例服務器路徑下有一個templates目錄, 下面有一個index.tmpl模板, 模板的 內容是:

<html>
    <h1>
       { { .title } }
    </h1>
</html>

當客戶端請求/index時, 服務器使用這個模板, 並填充相應的參數, 此處參數只有title, 而後將HTML數據返回給客戶端. 
你也能夠在瀏覽器請求0.0.0.0:8888/index, 效果以下圖所示:

4

 

<8> 重定向

重定向相對比較簡單, 服務端代碼是:

func main(){
    router := gin.Default()
    // 下面測試重定向
    router.GET("/redirect", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently, "http://shanshanpt.github.io/")
    })

    router.Run(":8888")
}

客戶端測試代碼是:

func main() {
    // 下面測試重定向
    resp,_ = http.Get("http://0.0.0.0:8888/redirect")
    helpRead(resp)
}

當咱們請求http://0.0.0.0:8888/redirect的時候, 會重定向到http://shanshanpt.github.io/這個站點.

 

<9> 使用middleware

這裏使用了兩個例子, 一個是logger, 另外一個是BasiAuth, 具體看服務器代碼:

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        t := time.Now()
        // 設置example變量到Context的Key中,經過Get等函數能夠取得
        c.Set("example", "12345")
        // 發送request以前
        c.Next()
        // 發送request以後
        latency := time.Since(t)
        log.Print(latency)

        // 這個c.Write是ResponseWriter,咱們能夠得到狀態等信息
        status := c.Writer.Status()
        log.Println(status)
    }
}


func main(){
    router := gin.Default()
    // 1
    router.Use(Logger())
    router.GET("/logger", func(c *gin.Context) {
        example := c.MustGet("example").(string)
        log.Println(example)
    })

    // 2
    // 下面測試BasicAuth()中間件登陸認證
    //
    var secrets = gin.H{
        "foo":    gin.H{"email": "foo@bar.com", "phone": "123433"},
        "austin": gin.H{"email": "austin@example.com", "phone": "666"},
        "lena":   gin.H{"email": "lena@guapa.com", "phone": "523443"},
    }
    // Group using gin.BasicAuth() middleware
    // gin.Accounts is a shortcut for map[string]string
    authorized := router.Group("/admin", gin.BasicAuth(gin.Accounts{
        "foo":    "bar",
        "austin": "1234",
        "lena":   "hello2",
        "manu":   "4321",
    }))
    // 請求URL: 0.0.0.0:8888/admin/secrets
    authorized.GET("/secrets", func(c *gin.Context) {
        // get user, it was set by the BasicAuth middleware
        user := c.MustGet(gin.AuthUserKey).(string)
        if secret, ok := secrets[user]; ok {
            c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
        } else {
            c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
        }
    })

    router.Run(":8888")
}

客戶端測試代碼是:

func main() {
    // 下面測試使用中間件
    resp,_ = http.Get("http://0.0.0.0:8888/logger")
    helpRead(resp)

    // 測試驗證權限中間件BasicAuth
    resp,_ = http.Get("http://0.0.0.0:8888/admin/secrets")
    helpRead(resp)
}

服務端使用Use方法導入middleware, 當請求/logger來到的時候, 會執行Logger(), 而且咱們知道在GET註冊的時候, 同時註冊了匿名函數, 全部請看Logger函數中存在一個c.Next()的用法, 它是取出全部的註冊的函數都執行一遍, 而後再回到本函數中, 因此, 本例中至關因而先執行了 c.Next()即註冊的匿名函數, 而後回到本函數繼續執行. 因此本例的Print的輸出順序是: 
log.Println(example) 
log.Print(latency) 
log.Println(status) 
若是將c.Next()放在log.Print(latency)後面, 那麼log.Println(example)和log.Print(latency)執行的順序就調換了. 因此一切都取決於c.Next()執行的位置. c.Next()的核心代碼以下:

// Next should be used only in the middlewares.
// It executes the pending handlers in the chain inside the calling handler.
// See example in github.
func (c *Context) Next() {
	c.index++
	s := int8(len(c.handlers))
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

它實際上是執行了後面全部的handlers. 
關於使用gin.BasicAuth() middleware, 能夠直接使用一個router group進行處理, 本質和logger同樣.

 

<10> 綁定http server

以前全部的測試中, 咱們都是使用router.Run(":8888")開始執行監聽, 其實還有兩種方法:

// 方法二
http.ListenAndServe(":8888", router)

// 方法三:
server := &http.Server{
    Addr:           ":8888",
    Handler:        router,
    ReadTimeout:    10 * time.Second,
    WriteTimeout:   10 * time.Second,
    MaxHeaderBytes: 1 << 20,
}
server.ListenAndServe()

至此, gin最基本的一些應用都整理完了

相關文章
相關標籤/搜索