golang中的gin框架學習

gin框架中常用方法

gin.H{ }

有這麼一行c.JSON(200, gin.H{“message”: “use get method”})
  這其中有一個gin.H{ },看樣子,這像是一個結構體struct,查看gin框架的源碼,聲明如下:
  所以,這只是一個map結構,別以爲是一個struct

設置http請求方式

gin框架封裝了http庫,提供了GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS這些http請求方式。
  使用router.method()來綁定路由
  聲明如下:
  其中的METHOD可以是上面的7種方式。使用對應的METHOD訪問對應的 url path,返回對應的response。
  但是如果使用不對應的method訪問path,就會返回404,比如使用post方法訪問localhost:8080/get會返回404

切換輸出的格式

router.GET("/get", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "use get method"})
})

上面的這段代碼,就是在用戶訪問localhost:8080時,c.JSON()返回一個響應,響應中的狀態碼是200,響應內容是一個JSON格式的字符串。
  那麼我們就很容易知道,不同的響應內容格式是通過調用*gin.Context類型變量的不同方法來實現的。

常見響應內容格式

gin框架提供了很多響應內容的格式,常用的方法聲明如下:

//返回json格式
func (c *Context) JSON(code int, obj interface{})
 
//返回xml格式
func (c *Context) XML(code int, obj interface{})
 
//返回yaml格式
func (c *Context) YAML(code int, obj interface{})
 
//返回string格式
func (c *Context) String(code int, format string, values ...interface{})
 
//渲染html模板後返回
func (c *Context) HTML(code int, name string, obj interface{})

狀態碼

注意上面每一個響應中都有一個狀態碼200。
  這個狀態碼不僅可以手動指定一個數字,比如200,500,404;也可以使用http包中的狀態碼,語義化的狀態碼更好理解;
  下面解釋http協議中所有的狀態碼了。

package http
 
// HTTP status codes as registered with IANA.
// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
const (
    StatusContinue           = 100 // RFC 7231, 6.2.1
    StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
    StatusProcessing         = 102 // RFC 2518, 10.1
 
    StatusOK                   = 200 // RFC 7231, 6.3.1
    StatusCreated              = 201 // RFC 7231, 6.3.2
    StatusAccepted             = 202 // RFC 7231, 6.3.3
    StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
    StatusNoContent            = 204 // RFC 7231, 6.3.5
    StatusResetContent         = 205 // RFC 7231, 6.3.6
    StatusPartialContent       = 206 // RFC 7233, 4.1
    StatusMultiStatus          = 207 // RFC 4918, 11.1
    StatusAlreadyReported      = 208 // RFC 5842, 7.1
    StatusIMUsed               = 226 // RFC 3229, 10.4.1
 
    StatusMultipleChoices   = 300 // RFC 7231, 6.4.1
    StatusMovedPermanently  = 301 // RFC 7231, 6.4.2
    StatusFound             = 302 // RFC 7231, 6.4.3
    StatusSeeOther          = 303 // RFC 7231, 6.4.4
    StatusNotModified       = 304 // RFC 7232, 4.1
    StatusUseProxy          = 305 // RFC 7231, 6.4.5
    _                       = 306 // RFC 7231, 6.4.6 (Unused)
    StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
    StatusPermanentRedirect = 308 // RFC 7538, 3
 
    StatusBadRequest                   = 400 // RFC 7231, 6.5.1
    StatusUnauthorized                 = 401 // RFC 7235, 3.1
    StatusPaymentRequired              = 402 // RFC 7231, 6.5.2
    StatusForbidden                    = 403 // RFC 7231, 6.5.3
    StatusNotFound                     = 404 // RFC 7231, 6.5.4
    StatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5
    StatusNotAcceptable                = 406 // RFC 7231, 6.5.6
    StatusProxyAuthRequired            = 407 // RFC 7235, 3.2
    StatusRequestTimeout               = 408 // RFC 7231, 6.5.7
    StatusConflict                     = 409 // RFC 7231, 6.5.8
    StatusGone                         = 410 // RFC 7231, 6.5.9
    StatusLengthRequired               = 411 // RFC 7231, 6.5.10
    StatusPreconditionFailed           = 412 // RFC 7232, 4.2
    StatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11
    StatusRequestURITooLong            = 414 // RFC 7231, 6.5.12
    StatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13
    StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
    StatusExpectationFailed            = 417 // RFC 7231, 6.5.14
    StatusTeapot                       = 418 // RFC 7168, 2.3.3
    StatusUnprocessableEntity          = 422 // RFC 4918, 11.2
    StatusLocked                       = 423 // RFC 4918, 11.3
    StatusFailedDependency             = 424 // RFC 4918, 11.4
    StatusUpgradeRequired              = 426 // RFC 7231, 6.5.15
    StatusPreconditionRequired         = 428 // RFC 6585, 3
    StatusTooManyRequests              = 429 // RFC 6585, 4
    StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
    StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3
 
    StatusInternalServerError           = 500 // RFC 7231, 6.6.1
    StatusNotImplemented                = 501 // RFC 7231, 6.6.2
    StatusBadGateway                    = 502 // RFC 7231, 6.6.3
    StatusServiceUnavailable            = 503 // RFC 7231, 6.6.4
    StatusGatewayTimeout                = 504 // RFC 7231, 6.6.5
    StatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6
    StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
    StatusInsufficientStorage           = 507 // RFC 4918, 11.5
    StatusLoopDetected                  = 508 // RFC 5842, 7.2
    StatusNotExtended                   = 510 // RFC 2774, 7
    StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
    )

比如下面的語句:

router.GET("/json", func(c *gin.Context) {
    c.JSON(http.StatusNotFound, gin.H{"message": "not found"})
    //等價於
    //c.JSON(404, gin.H{"message": "not found"})
})

獲取URL中的值與request body中的值

參考:https://blog.csdn.net/weixin_34199335/article/details/86362964
gin的文檔,接收從客戶端發來的各種參數,有兩大類方式:

接收單個參數的方法:

c.Param()
c.Query          // 查詢URL中的參數
c.DefaultQuery   // 查詢URL中的參數,URL中有值則使用該值,沒有值使用設置的默認值    
c.PostForm       // (前端request)消息主體body裏的x-www-form-urlencoded 參數
c.DefaultPostForm  //前端傳過來的消息主體中的參數,可以設置默認值
c.QueryMap
c.PostFormMap
c.FormFile
c.MultipartForm

其中,
c.Query() 和 c.DefaultQuery()方法 ,查詢的是URL中的參數
c.PostForm()和c.DefaultPostForm()方法,查詢的是前端傳過來的消息主體中的參數

各種綁定方法

c.Bind
c.BindJSON
c.BindXML
c.BindQuery
c.BindYAML
c.ShouldBind
c.ShouldBindJSON
c.ShouldBindXML
c.ShouldBindQuery
c.ShouldBindYAML

獲取url中的參數

對於請求 url 查詢參數用 c.Query

如下面官方的例子中 ?id=1234&page=1 這個,就是 查詢參數(query params)
你會看到這個參數就放在url裏的
如果參數不是放在url裏的,也可以在body裏,比如 body 裏的x-www-form-urlencoded 參數,如下面的name=manu&message=this_is_great
對於gin,要使用 name := c.PostForm(“name”) api
關注下 Content-Type 這個字段,表示了body的類型,更多看看這篇文章
四種常見的 POST 提交數據方式://imququ.com/post/four-ways-to-post-data-in-http.html
還有像 c.FormFile,用於處理上傳文件的
請求示例

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") // (前端request)消息主體 body 裏的x-www-form-urlencoded 參數
        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

獲取請求中的參數

假如有這麼一個請求:

POST   /post/test?id=1234&page=1  HTTP/1.1
請求頭:  Content-Type: application/x-www-form-urlencoded
form表單參數:  name=manu&message=this_is_great

gin的實現:

id := c.Query("id") //查詢請求URL後面的參數
page := c.DefaultQuery("page", "0") //查詢請求URL後面的參數,如果沒有填寫默認值
name := c.PostForm("name") //從表單中查詢參數

/////////////////////////////////
//POST和PUT主體參數優先於URL查詢字符串值。
name := c.Request.FormValue("name") 

//返回POST並放置body參數,URL查詢參數被忽略
name := c.Request.PostFormValue("name")

//從表單中查詢參數,如果沒有填寫默認值  
message := c.DefaultPostForm("message", "aa") 

假如gin定義的路由路徑爲:

router.POST("/post/:uuid", func(c *gin.Context){
    ...
}

則獲取uuid的值方法爲

uuid := c.Param("uuid") //取得URL中參數

其他:

s, _ := c.Get("current_manager") //從用戶上下文讀取值      
type User struct{
  ID int
  Name string
  Age int
}
var u User

//從http.Request中讀取值到User結構體中,手動確定綁定類型binding.Form

err1 := c.BindWith(&u, binding.Form) 

//從http.Request中讀取值到User結構體中,根據請求方法類型和請求內容格式類型自動確定綁定類型

err2 := c.Bind(&u)

從session中讀取值

//用戶上下文和session生命週期不同,每一次請求會生成一個對應的上下文,一次http請求結束,該次請求的上下文結束,一般來說session(會話)會留存一段時間
//session(會話)中一般保存用戶登錄狀態等信息,context(上下文)主要用於在一次http請求中,在中間件(流)中進行信息傳遞
user := sessions.Default©.get(“user”)

將請求體綁定到不同的結構體中

ShouldBind(&objA)方法
綁定請求體的常規方法使用c.Request.Body,並且不能多次調用

type formA struct {
  Foo string `json:"foo" xml:"foo" binding:"required"`
}

type formB struct {
  Bar string `json:"bar" xml:"bar" binding:"required"`
}

func SomeHandler(c *gin.Context) {
  objA := formA{}
  objB := formB{}
  // This c.ShouldBind consumes c.Request.Body and it cannot be reused.
  if errA := c.ShouldBind(&objA); errA == nil {
    c.String(http.StatusOK, `the body should be formA`)
  // Always an error is occurred by this because c.Request.Body is EOF now.
  } else if errB := c.ShouldBind(&objB); errB == nil {
    c.String(http.StatusOK, `the body should be formB`)
  } else {
    ...
  }
}

ShouldBindBodyWith(&objA)方法
同樣,可以使用c.ShouldBindBodyWith

func SomeHandler(c *gin.Context) {
  objA := formA{}
  objB := formB{}
  // This reads c.Request.Body and stores the result into the context.
  if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
    c.String(http.StatusOK, `the body should be formA`)
  // At this time, it reuses body stored in the context.
  } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
    c.String(http.StatusOK, `the body should be formB JSON`)
  // And it can accepts other formats
  } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
    c.String(http.StatusOK, `the body should be formB XML`)
  } else {
    ...
  }
}

c.ShouldBindBodyWith 在綁定之前將body存儲到上下文中,這對性能有輕微影響,因此如果你要立即調用,則不應使用此方法
此功能僅適用於這些格式 – JSON, XML, MsgPack, ProtoBuf。對於其他格式,Query, Form, FormPost, FormMultipart, 可以被c.ShouldBind()多次調用而不影響性能(參考 #1341)

參考鏈接:https://www.jianshu.com/p/98965b3ff638

獲取URL中的路徑參數

比如:http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wigth=100
  上面的這個鏈接中,可以通過向上面講的使用/user/:name/:age/:addr/:sex來分別匹配jane、20、beijing、female。

獲取URL的所有路徑參數

gin框架中如果使用URL傳遞參數,那麼在綁定的時候,是這樣的:/user/:name/:age/:addr/:sex
  上面這個path綁定了4個URL參數,分別是name、age、addr、sex
  首先gin.Context對象的Params屬性保存着URL中的所有參數:

router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) {
    //打印URL中所有參數
    c.JSON(200, fmt.Sprintln(c.Params))
})

如果請求http://localhost:8080/user/jane/20/beijing/female
  得到的輸出如下:

"[{name jane} {age 20} {addr beijing} {sex female}]"

注意最後面的那個換行符

獲取URL中的指定路徑參數

使用gin.Context對象的Param(key)方法獲取某一個key的值,方法聲明如下:

router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) {
    name := c.Param("name") //獲取參數的時候,不要寫name前面的冒號:
    c.JSON(200, name)
})

訪問http://localhost:8080/user/jane/20/beijing/female,輸出"jane"

獲取URL中請求的參數(GET請求)

獲取URL中路徑值和獲取參數不一樣。
  比如:http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wight=100
  可以使用下面的方法獲取請求參數id、height、weight的值。
獲取指定參數的值

//返回URL中key的值
func (c *Context) Query(key string) string

使用gin.Context.Query(key string)可以獲取指定key的值,
  測試:

router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) {
    id := c.Query("id")
    height := c.Query("height")
    weight := c.Query("weight")
    c.JSON(200, gin.H{"id": id, "height": height, "weight": weight})
})

訪問http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wight=100,輸出內容如下:

{"height":"170","id":"999","weight":"100"}

獲取指定參數的值(帶有默認值的接收)

當然,以前寫php的時候,如果要想判斷是否接收到了某個參數,如果沒有接收到,那麼就設置一個默認值,最常用的就是三元運算符,比如下面這樣:

$id = empty($_POST['id']) ? 0 : $_POST['id'];

gin框架當然也想到了這麼一點,gin.Context.DefaultQuery()方法,允許指定接收的參數名,以及沒有接收到該參數值時,設置的默認值,聲明如下:

func (c *Context) DefaultQuery(key, defaultValue string) string

只有當請求沒有攜帶key,那麼此時的默認值就會生效。其他情況,默認值不生效。即使URL中的該key的值爲空,那麼也不會啓用默認值,獲取的值就是空。
  注意,這是獲取URL中的參數值。 
接收multipart/urlencoded form(POST表單數據)
  和獲取URL中的參數很相似:
  GET請求中的參數使用Query(key),DefaultQuery(key,default)來獲取
  POST請求中的參數使用PostForm(key),DefaultPostForm(key,default)來獲取。

package main
 
import (
    "github.com/gin-gonic/gin"
)
 
func main() {
    router := gin.Default()
    router.POST("/post", func(c *gin.Context) {
        name := c.PostForm("name")
        age := c.PostForm("age")
        sex := c.DefaultPostForm("sex", "male")
        c.JSON(200, gin.H{"name": name, "age": age, "sex": sex})
    })
    router.Run()
 }

獲注意要想獲取POST方式傳遞的參數,那麼綁定的路由監聽方式就必須是router.POST,不能使router.GET。
同時獲取URL中的參數和POST的參數
  前提:請求方必須是使用POST方式傳遞,只不過URL中也包含一部分參數而已。
  同時,服務器端必須使用router.POST方式來接收,不能使用router.GET方式接收,因爲router.GET只能接收GET方式傳遞的數據。

package main
 
import (
    "github.com/gin-gonic/gin"
)
 
func main() {
    router := gin.Default()
    //注意,這裏必須使用router.POST
    router.POST("/post", func(c *gin.Context) {
        name := c.PostForm("name")
        age := c.PostForm("age")
        sex := c.DefaultPostForm("sex", "male")
        addr := c.Query("addr")
        hobby := c.DefaultQuery("hobby", "basketball")
        c.JSON(200, gin.H{"name": name, "age": age, "sex": sex, "addr": addr, "hobby": hobby})
    })
    router.Run()
}

請求:localhost:8080/post?addr=beijing&hobby=football
  同時使用post方式傳遞name=abc&age=21111&sex=female
  那麼得到的響應就是如下內容:

將GET或POST函數中的func(c *gin.Context)封裝成一個函數作爲參數傳入

實際在項目開發中,通常將將GET或POST函數中的func(c *gin.Context)封裝成一個函數作爲參數傳入,同時將c.JSON(statuscode, gin.H{key:value, })中的gin.H{}配置成所需要的json格式的數據結構,內容使用自定義的struct類型的數據結構返回,例如:

//main.go
//維度補充信息關聯數據庫
router.GET("/table/dimension/relate/db", handler.GetDBList)

//package handle
//GetDBList 路由中與db相關的綁定方法,不顯示返回內容
func GetDBList(c *gin.Context) {
//通過select語句從數據庫中獲取需要的數據
    dbList, err := api.GetDB()

    if err != nil {
        fmt.Println("get related db info from data_source error:", err)
        response.StatusOK(c, http.StatusInternalServerError, err.Error(), nil)
        return
    }
//使用map[string]interface{}定義數據結構類型,{"list":dbList}使用從數據庫中獲取到的數據初始化要response給前端的數據內容
    c.JSON(http.StatusOK, map[string]interface{}{
        "list": dbList,
    })
}

//package api
//GetDB 獲取有關數據庫的id和數據庫名稱組成的list
func GetDB() ([]InstanceNameID, error) {
//初始化自定義的InstanceNameID數組,用於存放從數據庫中篩選出的數據
    res := []InstanceNameID{}
    conn := databases.GetDBConn()
    err := conn.Table("data_source").
        Select("id, instance_name").
        Where("data_source.is_deleted=0").Scan(&res).Error
    if err != nil {
        return []InstanceNameID{}, err
    }
    return res, err
}

//package api
//InstanceNameID 的數據結構定義, ``中的gorm和json的位置無先後順序,且gorm的value中儘量帶上Column, gorm不要拼錯,否則其指定的內容會爲空
type InstanceNameID struct {
//自定義的struct中的字段,通常需要在其他包中使用,所以需要將首字母大寫,代表是導出字段,在其他包中也可以引用;如果不小心寫成小寫,則改字段的內容可能爲空。
    Name string `gorm:"Column:instance_name" json:"name"`
    ID   int64  `gorm:"Column:id" json:"id"`
}

GET與POST請求

什麼是 HTTP?

超文本傳輸協議(HTTP)的設計目的是保證客戶機與服務器之間的通信。
HTTP 的工作方式是客戶機與服務器之間的請求-應答協議。
web 瀏覽器可能是客戶端,而計算機上的網絡應用程序也可能作爲服務器端。
舉例:客戶端(瀏覽器)向服務器提交 HTTP 請求;服務器向客戶端返回響應。響應包含關於請求的狀態信息以及可能被請求的內容。

GET與POST請求

gin框架封裝了http庫,提供了GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS這些http請求方式。
  使用router.method()來綁定路由,聲明如下:

func (group *RouterGroup) METHOD(relativePath string, handlers ...HandlerFunc) IRouters

兩種最常用的 HTTP 方法是:GET 和 POST。

在客戶機和服務器之間進行請求-響應時,兩種最常被用到的方法是:GET 和 POST。
GET - 從指定的資源請求數據。
POST - 向指定的資源提交要被處理的數據

GET 方法

請注意,查詢字符串(名稱/值 對)是在 GET 請求的 URL 中發送的,例如下列URL(中?後面的鍵值對)形式,就需要放在GET方法中:
/test/demo_form.asp?name1=value1&name2=value2
有關 GET 請求的其他一些說明:
GET 請求可被緩存
GET 請求保留在瀏覽器歷史記錄中
GET 請求可被收藏爲書籤
GET 請求不應在處理敏感數據時使用
GET 請求只應當用於取回數據
GET 請求有長度限制

POST 方法

請注意,查詢字符串(名稱/值 對)是在 POST 請求的 HTTP 消息主體中發送的:
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
有關 POST 請求的其他一些解釋:
POST 請求不會被緩存
POST 請求不會保留在瀏覽器歷史記錄中
POST 不能被收藏爲書籤
POST 請求對數據長度沒有要求
GET與POST對比
圖片: https://uploader.shimo.im/f/AUqcKycHeyULC7E4.png

摘自:HTTP 方法:GET 對比 POST
http://www.w3school.com.cn/tags/html_ref_httpmethods.asp
gin框架中對url參數的解析
https://xiaowei0516.github.io/2017/08/10/gin-upload-file/

Browser(前端) 發出請求, Server(後端)給出響應。
golang要請求遠程網頁,可以使用net/http包中提供的http.GET()或者http.POST()方法實現。
query string 參數與body參數
query stirng 路由用 ?連接參數的形式,如key1=value1?key2=value2,這是通過urlencode編碼後的。 對於query string,經常出現參數不存在的情況,需要提供默認值。

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)
    })

DefaultQuery是當不存在的時候提供一個默認的參數。
body格式有四種: application/json、application/x-www-form-urlencoded、application/xml、multipart/form-data
c.PostFROM解析的是x-www-form-urlencoded或from-data的參數。

https://www.cnblogs.com/-beyond/p/9391892.html

比如,運行下面的代碼go run main.go

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    
    router := gin.Default()
    router.GET("/table/dimension/relate/field", func(c *gin.Context) {
        db := c.Query("db")
        table := c.DefaultQuery("table", "dimension")
        c.JSON(200, gin.H{"db": db, "table": table})
    })
    router.Run("127.0.0.1:5000")
}

並在瀏覽器中輸入這個URL
http://localhost:5000/table/dimension/relate/field?db=dw&table=dimension
將會返回json格式的內容—— {“db”:“dw”,“table”:“dimension”}

net/http中的部分方法解析

ListenAndServe函數
圖片: https://uploader.shimo.im/f/tBWNnCgLHw0Aox7S.png
ListenAndServe方法根據addr和handler參數構建了一個Server對象,並調用該對象的ListenAndServe方法進行監聽和響應。那麼第二個參數Handler的作用是什麼呢,爲什麼通常傳nil值?

// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

Handler的定義如下:

type Handler interface {
	ServerHTTP(ResponseWriter, *Request)
}

Handler是一個接口類型,包含了ServerHTTP方法,該方法對客戶端的請求(Request對象)進行處理,並通過ResponseWriter將響應信息傳送回客戶端。所以,Handler是Server對象處理請求的邏輯的關鍵所在。但是上面ListenAndServe方法的Handler參數爲nil,Handler爲Nil的Server對象如何實現處理請求的邏輯呢?

注意到Server對象的Handler屬性的註釋有提到如果Handler值爲nil,那麼Server對象將使用http.DefaultServeMux來處理請求。

http.DefaultServeMux是一個ServeMux對象,ServeMux對象是一個HTTP請求的分發器,將匹配了相應URL的請求分發給相應的處理函數進行處理。其定義如下:

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	hosts bool // whether any patterns contain hostnames
}


type muxEntry struct {
	h       Handler
	pattern string
}

ServeMux正是通過map結構(map[string]muxEntry)來匹配請求和Handler的,起到了路由的作用。並且ServeMux本身也是一個Handler,因爲它實現了Handler接口的ServeHTTP方法。

其中Server定義如下:

// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
	Addr    string  // TCP address to listen on, ":http" if empty
	Handler Handler // handler to invoke, http.DefaultServeMux if nil


	// TLSConfig optionally provides a TLS configuration for use
	// by ServeTLS and ListenAndServeTLS. Note that this value is
	// cloned by ServeTLS and ListenAndServeTLS, so it's not
	// possible to modify the configuration with methods like
	// tls.Config.SetSessionTicketKeys. To use
	// SetSessionTicketKeys, use Server.Serve with a TLS Listener
	// instead.
	TLSConfig *tls.Config


	// ReadTimeout is the maximum duration for reading the entire
	// request, including the body.
	//
	// Because ReadTimeout does not let Handlers make per-request
	// decisions on each request body's acceptable deadline or
	// upload rate, most users will prefer to use
	// ReadHeaderTimeout. It is valid to use them both.
	ReadTimeout time.Duration


	// ReadHeaderTimeout is the amount of time allowed to read
	// request headers. The connection's read deadline is reset
	// after reading the headers and the Handler can decide what
	// is considered too slow for the body.
	ReadHeaderTimeout time.Duration


	// WriteTimeout is the maximum duration before timing out
	// writes of the response. It is reset whenever a new
	// request's header is read. Like ReadTimeout, it does not
	// let Handlers make decisions on a per-request basis.
	WriteTimeout time.Duration


	// IdleTimeout is the maximum amount of time to wait for the
	// next request when keep-alives are enabled. If IdleTimeout
	// is zero, the value of ReadTimeout is used. If both are
	// zero, ReadHeaderTimeout is used.
	IdleTimeout time.Duration


	// MaxHeaderBytes controls the maximum number of bytes the
	// server will read parsing the request header's keys and
	// values, including the request line. It does not limit the
	// size of the request body.
	// If zero, DefaultMaxHeaderBytes is used.
	MaxHeaderBytes int


	// TLSNextProto optionally specifies a function to take over
	// ownership of the provided TLS connection when an NPN/ALPN
	// protocol upgrade has occurred. The map key is the protocol
	// name negotiated. The Handler argument should be used to
	// handle HTTP requests and will initialize the Request's TLS
	// and RemoteAddr if not already set. The connection is
	// automatically closed when the function returns.
	// If TLSNextProto is not nil, HTTP/2 support is not enabled
	// automatically.
	TLSNextProto map[string]func(*Server, *tls.Conn, Handler)


	// ConnState specifies an optional callback function that is
	// called when a client connection changes state. See the
	// ConnState type and associated constants for details.
	ConnState func(net.Conn, ConnState)


	// ErrorLog specifies an optional logger for errors accepting
	// connections, unexpected behavior from handlers, and
	// underlying FileSystem errors.
	// If nil, logging is done via the log package's standard logger.
	ErrorLog *log.Logger


	disableKeepAlives int32     // accessed atomically.
	inShutdown        int32     // accessed atomically (non-zero means we're in Shutdown)
	nextProtoOnce     sync.Once // guards setupHTTP2_* init
	nextProtoErr      error     // result of http2.ConfigureServer if used


	mu         sync.Mutex
	listeners  map[*net.Listener]struct{}
	activeConn map[*conn]struct{}
	doneChan   chan struct{}
	onShutdown []func()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章