golang中的gin框架学习

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/love666666shen/article/details/88901785

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

猜你喜欢

转载自blog.csdn.net/love666666shen/article/details/88901785