中间的几个关键对象
- Engine
- RouterGroup
- HandlerFunc
- Context
Engine
New()Default()
NewDefaultloggerrecover
Engine.Use()RouterGroup
Engine.Run
RouterGroup
type RouterGroup struct {
Handlers HandlersChain
basePath string
engine *Engine
root bool
}
HandlersChain[]HandlerFunc
engine.trees
Engine 包含 RouterGroup, RouterGroup 也会存储 Engine 的地址. 目的是为了, 加点api地址时, 根据当前具体配置的中间件, 再添加engine的路由树. 也就是说, 过程中修改了中间件, 不会影响到已经配置的路由的中间件.
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
注册路由之前, 先将当前的 http 处理方法, 与当前中间件配置合并, 再添加到engine.
RouterGroup
RouterGroup.GroupRouterGroup
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
return &RouterGroup{
Handlers: group.combineHandlers(handlers),
basePath: group.calculateAbsolutePath(relativePath),
engine: group.engine,
}
}
创建新的 Group 时候,也会继承旧的 Group, 原先已经设置的 middleware. gin 框架不支持移除中间件的操作,所以在上层设置的中间件的时候一定要确保是公共 common 的
相似的路由捆绑在一块, 路由组写法, 可以做绑定相同中间件处理.
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
中间的花括号只起美化代码作用, 非必需
HandlerFunc
中间件和请求处理方法的函数签名
type HandlerFunc func(*Context)
处理请求的时候, HandlerFunc 执行完毕 -- 代表该请求处理结束
Context
gin 自己实现的 Context 结构
- 在中间件中传递参数
- 控制调用流程
- 获取参数
- 返回结果
Context 结构的重要组成部分介绍
type Context struct {
// 存储URL 参数
Params Params
// 写入请求的结果
Writer ResponseWriter
// 请求的所有处理方法集合
handlers HandlersChain
// 当前执行到第几个方法
index int8
// 存储流转于中间件的参数式
Keys map[string]any
}
在中间件中传递参数
context.Getcontext.Set
流程控制
context.Next()
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
HandlerFunc
获取参数
context.Param()context.Query()context.QueryMap()context.PostFormMapcontext.Bindbinding:"required"
调用结束
conetxt.JSONjsonBindingcontext.AbortWithStatus()
Cookie 操作
context.Cookie()context.SetCookie()
Middleware
Engine.Use
Use
Next
调用流程图:
middleware 1
| - doing m1 work
| - call Context.Next()
| - call middleware 2
| - doing m2 work
| - call Context.Next()
| - call middleware 3
| - - done m3
| - continue m2 work
| - - done m2 work
| - coninue m1 work
| - done
finish
Context.Next()Context.Next()
Abort
Context.Abort()
func (c *Context) Abort() {
c.index = abortIndex
}
会将函数调用链的index指向一个超大整数 -- 放弃后面所有的处理函数, 但是 调用Abort后, 同一个函数接下的代码还会继续执行
小demo
func main() {
eg := gin.New()
eg.Use(m1, m2)
eg.GET("/greet", m3, greet)
eg.GET("/abort", abort, m3, greet)
eg.Run(":8080")
}
func m1(c *gin.Context) {
fmt.Println("before m1 next")
c.Next()
fmt.Println("after m1 next")
}
func m2(c *gin.Context) {
fmt.Println("\tbefore m2 next")
c.Next()
fmt.Println("\tafter m2 next")
}
func m3(c *gin.Context) {
fmt.Println("\t\tbefore m3 next")
c.Next()
fmt.Println("\t\tafter m3 next")
}
func abort(c *gin.Context) {
fmt.Println("\t\t\tbefore abort")
c.Abort()
fmt.Println("\t\t\tafter abort")
}
func greet(c *gin.Context) {
fmt.Println("\t\t\t\thow are you doing?")
c.JSON(200, "great")
}
/greet
before m1 next
before m2 next
before m3 next
how are you doing?
after m3 next
after m2 next
after m1 next
/abort
before m1 next
before m2 next
before abort
after abort
after m2 next
after m1 next
其他
gin.H
type gin.H map[string]interface{}
// 常用于 engine.JSON() 时返回 json 数据
几个问题
Gin 的 context 有什么作用和怎么用?
context 是 Gin 代码请求流转的核心, 存储处理请求的所有必须参数
基本的使用方法:
BindParamNextAbortJSON
Gin 的整体框架流程是怎么样的? 从接受到一个请求再到返回请求中间的流程?
Engine.ServeHTTP
整体流程
nodetreeHandlerChaingin.Contextgin.Contextgin.Context.Writer