gin 之 Context
gin 之 Context
Context 是 gin 当中最为重要的一部分,它用于在中间件当中传递变量,管理流程,请求和响应部分 都封装在 Context,Context 是 gin 中上下文管理的核心,它包括了请求处理,响应处理,表单解析等重要工作。
Go 标准库 Context 和 gin Context
从go1.7开始,golang.org/x/net/context
包正式作为context包进入了标准库。那么,这个包到底是做什么的呢?根据官方的文档说明:
Package context defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.
也就是说,通过context,我们可以方便地对同一个请求所产生地goroutine进行约束管理,可以设定超时、deadline,甚至是取消这个请求相关的所有goroutine。使用context可以使开发者方便的在这些goroutine里传递request相关的数据、取消goroutine的signal或截止日期。也就是说引入Context
主要是为了方便管理gotoutine
从而更容易的实现并发编程。来看看它的接口设计:
type Context interface { //Done 当绑定当前context的任务被取消时,将返回一个关闭的channel;如果当前context不会被取消,将返回nil Done() <-chan struct{} //如果Done返回的channel没有关闭,将返回nil;如果Done返回的channel已经关闭,将返回非空的值表示任务结束的原因。如果是context被取消,Err将返回Canceled;如果是context超时,Err将返回DeadlineExceeded Err() error // Deadline返回绑定当前context的任务被取消的截止时间 Deadline() (deadline time.Time, ok bool) // Value 返回context存储的键值对中当前key对应的值,如果没有对应的key,则返回nil Value(key interface{}) interface{} }
上面就是Context interface
,可以看出它的设计主要就是用于控制管理goroutine
的生命周期的,但gin Context
主要是请求处理,响应处理,表单解析,上下文管理等重要工作,当然它也实现了这个interface
的四个方法,只不过除了Value
方法之外其它的几个方法都是空实现,如果想实现其它几个方法,应该使用gin Request.Context()
,具体见gin context 源码,gin Context
内部封装了大量的细节,接下来看看。
数据结构
Context
type Context struct { writermem responseWriter // 实现了 golang net/http ResponseWriter 接口 Request *http.Request // golang net/http Request 对象 Params Params // 数组,按序记录了URL 参数 handlers HandlersChain // 中间件数组 index int8 // 记录当前执行到的中间件index,初始化被置为-1 fullPath string // URL 路径 engine *Engine // gin engine mu sync.RWMutex // 锁,防止keys map 被并发修改 // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} // Keys 用来存储每次request的键值对 ... }
Context
是 gin 中最为重要的部分,它的主要功能是传递变量,管理流程,接收验证request以及渲染响应response,其中的writermem
和Request
字段实现了Go
的net/http
的ResponseWriter
和Request
接口,因此具备了读取请求和响应请求的能力,其中的params
、fullPath
则是分别记录请求的URL参数和完整的URL路径,handlers
和index
则是为中间件
服务的,handlers
按照定义顺序记录了所有注册的中间件函数,而index
则记录当前执行到的中间件index
,初始化被设置为-1。Keys
则是被用来传递流程中的变量。Engine
type Engine struct { RouterGroup ... // 省略一些次要字段 } // RouterGroup (路由组)被用来在内部配置路由,一个RouterGroup 和URL 前缀以及一组中间件关联(中间件也叫middleware,后面会详细介绍gin的middleware,这里只要知道middleware本质上也是一些处理函数即可) type RouterGroup struct { Handlers HandlersChain // handerFunc 数组(一组中间件) basePath string // 路由组URL前缀 engine *Engine root bool // 是否根节点(这里涉及到路由树,在讲解路由的时候再讲) }
Engine
是一个框架的实例,它包含了路由、中间件、配置选项,New()
或者 Default()
创建一个Engine
,RouterGroup
提供路由分组的能力,被用来在内部配置路由,一个RouterGroup
和URL 前缀以及一组中间件关联(中间件也叫middleware
,后面会详细介绍gin的middleware
,这里只要知道middleware
本质上也是一些处理函数即可),由此可知gin的中间件不仅可以全局可用,还可以运用在不同的路由组。
Context 提供的方法
中间件相关
// 下一个中间件 func (c *Context) Next() { c.index++ // 当前执行到的中间件index + 1 for c.index < int8(len(c.handlers)) { // 依次执行后面所有的中间件 c.handlers[c.index](c) c.index++ } } // 终止后面的中间件(handerFunc)处理(gin 也提供了AbortWithStatus、AbortWithStatusJSON等方法) func (c *Context) Abort() { c.index = abortIndex // abortIndex 是常量changmath.MaxInt8/2,直接将context.index 置为无穷大,后面的中间件han
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> <span style="font-size:14px;">本专刊是Go开源项目源码分析专栏,共 17 篇文章,挑选了Go 开源界知名的 4 个开源项目gnet(高效的网络库)、gin(知名的Go微型web框架)、fasthttp(高性能web框架)、nsq(Go消息队列)来对它们进行源码分析,分析它们的设计思想和代码实现。每个项目的讲解都是由浅入深,由设计思想的剖析到源码实现的分析,更易于读者理解。</span> </p> <p> <br /> </p> <h2> <b><span style="font-size:16px;line-height:1;">购买须知:</span></b> </h2> <span style="font-size:14px;">订阅成功后,用户即可通过牛客网 PC 端、App 端享有永久阅读的权限;</span><br /> <span style="font-size:14px;">牛客专刊为虚拟内容服务,订阅成功后概不退款;</span><br /> <span style="font-size:14px;line-height:1;">在专刊阅</span><span style="font-size:14px;line-height:1;">读过程中,如有任何问题,可在文章评论区底部留言,或添加牛客导师,加入读者交流群;</span><br /> <span style="font-size:14px;">想成为牛客作者,请邮件联系yinxiaoxiao@nowcoder.com,邮件主题【牛客作者+写作方向】,并附上个人简历一份及近期作品一份;</span><br /> <p> <span style="font-size:14px;">牛客专刊版权归本平台所有,任何机构、媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布 / 发表,违者将依法追究责任</span><span style="font-size:14px;">。</span> </p> <p> <br /> </p>