母亲节快乐~ 祝天下的母亲都快快乐乐
Context是框架中非常重要的一点,它允许我们在中间件间共享变量,管理整个流程。携带了HTTP请求的request、response。
- 一、结构
- 二、主要方法介绍
- func (c *Context) reset()
- func (c *Context) Copy() *Context
- func (c *Context) Next()
- func (c *Context) Error(err error) *Error
- func (c *Context) Set(key string, value interface{})
- func (c *Context) Get(key string) (value interface{}, exists bool)
- func (c *Context) Param(key string) string
- func (c *Context) Query(key string) string
- func (c *Context) PostForm(key string) string
- func (c *Context) Bind(obj interface{}) error
- func (c *Context) ClientIP() string
- func (c *Context) RemoteIP() (net.IP, bool)
- func (c *Context) Render(code int, r render.Render)
- func (c *Context) Deadline() (deadline time.Time, ok bool)
- func (c *Context) Done() <-chan struct{}
- func (c *Context) Err() error
- func (c *Context) Value(key interface{}) interface{}
一、结构
代码位置:https://github.com/gin-gonic/gin/blob/master/gin.go。
Context的主要功能是处理HTTP请求中的Request、Response,处理具体的HTTP数据传输方式,比如HTTP头部,请求体参数,状态码以及响应体和其他的一些常见HTTP行为。
Context虽然在Gin中的地位极其显耀,但是内部逻辑相对简单。主要分为构造方法、流程控制、错误管理、元数据管理、Request数据管理、Response渲染、内容协商、官方Context实现这几大类。
Context结构:
1 | type Context struct { |
- writermem: 包含size,status和net/http.ResponseWriter的结构体
- Request: http的请求体(指向原生的http.Request指针)
- Writer: ResponseWriter,这里没搞明白为什么要同时搞两个writermem和Writer
- Params: 请求参数[]{“Key”:”Value”},这里同params。参数通过path传入的时候解析对应的参数。/a/b/c/:id
- handlers: 当前path匹配到的所有handler集合
- index: 当前执行到的handler在切片中的索引位置
- fullPath: 当前HTTP请求的路径
- engine: 当前Engine
- params: 同Params
- mu: 在下面Keys中存取键值对时的互斥锁
- Keys: 每个请求的context中的唯一键值对,这里猜测是可以让用户放入一些自定义参数通过上下文传输
- Errors: 绑定到所有使用该context的handler/middlewares的错误列表
- Accepted: 定义了允许的格式被用于内容协商(content),请求头的Accept字段
- queryCache:queryCache 使用url.ParseQuery来缓存参数查询结果(c.Request.URL.Query()) /a/b/c?s=1&b=2
- formCache: formCache 使用url.ParseQuery来缓存PostForm包含的表单数据(来自POST,PATCH,PUT请求体参数)
- sameSite: sameSite允许服务器定义cookie属性,使浏览器不可能在跨站点请求时发送此cookie。
二、主要方法介绍
1 | func (c *Context) reset() |
func (c *Context) reset()
1 | func (c *Context) reset() { |
reset()方法主要功能就是初始化一个Context对象,在engine.pool中获取一个Context对象后,对这个Context对象进行一些初始化动作。避免一些脏数据造成的影响。(原因参见sync.Pool)的介绍)
func (c *Context) Copy() *Context
1 | func (c *Context) Copy() *Context { |
Copy返回当前上下文的一个副本,可以在请求范围之外安全地使用。当需要将上下文传递给goroutine时,必须使用此方法。
func (c *Context) Next()
1 | func (c *Context) Next() { |
依次运行handler链中的下个个handler
func (c *Context) Error(err error) *Error
1 | func (c *Context) Error(err error) *Error { |
返回一些错误对象。Error将错误附加到当前上下文。将错误推入错误列表。对于在解析请求期间发生的每个错误,最好都调用Error。中间件可用于收集所有错误并将它们一起推送到数据库、打印日志或将其附加到HTTP响应中。如果err为nil,错误就会恐慌。
func (c *Context) Set(key string, value interface{})
1 | func (c *Context) Set(key string, value interface{}) { |
向Keys添加键值对
func (c *Context) Get(key string) (value interface{}, exists bool)
1 | func (c *Context) Get(key string) (value interface{}, exists bool) { |
从Keys中查找key对应的值
func (c *Context) Param(key string) string
1 | func (c *Context) Param(key string) string { |
返回url路径中的参数值(/user/:id, c.Param(“id”)返回id对应的值)
func (c *Context) Query(key string) string
1 | func (c *Context) Query(key string) string { |
返回url路径中的参数值(GET /path?id=1234&name=Manu&value=, c.Param(“id”)返回id对应的值)
func (c *Context) PostForm(key string) string
1 | func (c *Context) PostForm(key string) string { |
从POST url编码表单或多部分表单中返回指定的键,否则返回空字符串’(“”)’。
func (c *Context) Bind(obj interface{}) error
1 | func (c *Context) Bind(obj interface{}) error { |
Bind检查Content-Type以自动选择绑定引擎,
根据“Content-Type”头使用不同的绑定:
“application/json” ——> json绑定
“application/xm” ——> xml绑定
否则——>返回错误。
如果Content-Type == “application/ JSON “使用JSON或XML作为JSON输入,它将请求的主体解析为JSON。
它将json有效负载解码为指定为指针的结构。
如果输入无效,它会写一个400错误,并在响应中设置Content-Type头“text/plain”。
func (c *Context) ClientIP() string
1 | func (c *Context) ClientIP() string { |
ClientIP实现了一个最佳努力算法来返回真实的客户端IP。
它在底层调用c.RemoteIP(),以检查远程IP是否为可信代理。
如果是,它将尝试解析在Engine中定义的头文件。RemoteIPHeaders(默认为[X-Forwarded-For, X-Real-Ip])。
如果Header在语法上无效或远端IP与可信代理不对应,返回远端IP(来自Request.RemoteAddr)。
func (c *Context) RemoteIP() (net.IP, bool)
1 | func (c *Context) RemoteIP() (net.IP, bool) { |
RemoteIP从请求解析IP。正常化并返回IP(不带端口)。
它还检查remoteIP是否为可信代理。
为了执行这个验证,它将查看IP是否包含在Engine.TrustedProxies中定义的至少一个CIDR块中。
func (c *Context) Render(code int, r render.Render)
1 | func (c *Context) Render(code int, r render.Render) { |
Render写入响应头并调用Render,渲染到渲染数据。根据不同的binding类型采用不同的渲染逻辑。
gin.Context还实现了context/Context接口(参见Context的介绍),但是并没有实现具体逻辑。