gin框架是基于Golang的一个web框架,由于比较简单容易上手的开源框架
github地址:https://github.com/gin-gonic/gin
中文文档:https://gin-gonic.com/zh-cn/

1.特性

快速
基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。
支持中间件
传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。
Crash 处理
Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!
JSON 验证
Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。
路由组
更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。
错误管理
Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
内置渲染
Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。

2.具体使用

初始化与路由设置

  1. router := gin.New() //new方法需要手动加载 Recovery 和 Logger 中间件
  2. router = gin.Default() //Default 使用 Logger 和 Recovery 中间件
  3. router.Use(middleware...) //注册全局中间件
  4. userGroup := router.Group("/user")
  5. userGroup.Use(middleware...) //注册路由组中间件
  6. userGroup.Get("/detail",userController.Detail)//http://localhost:8088/user/detail

创建一个get的API接口

  1. func main() {
  2. r := gin.Default()
  3. r.GET("/testApi/print", func(c *gin.Context) {
  4. c.JSON(http.StatusOK, gin.H{
  5. "data": "hello world!",
  6. })
  7. })
  8. r.Run() // 监听并在 0.0.0.0:8080 上启动服务
  9. }

获取http请求的参数

  1. c := gin.Context
  2. userName := c.Query("userName")//获取get参数
  3. form := c.PostForm("userName")//获取post参数
  4. file,header,err := c.Request.FormFile("file")//以io流的形式获取文件,可以不用保存到本地
  5. err = c.Bind(formStruct)//获取表单,推荐;formStruct是你想要的数据的结构体
  6. type formStruct struct{
  7. UserName string `json:"userName"`
  8. PassWord string `json:"passWord"`
  9. }

设置结束函数

gin.Context.Next()与gin.Context.Abort()通常放在函数的末尾,分别代表加载下一个和结束

  1. //gin.Context.Next()
  2. func (c *Context) Next() {
  3. c.index++
  4. for c.index < int8(len(c.handlers)) {
  5. c.handlers[c.index](c)
  6. c.index++
  7. }
  8. }
  9. //gin.Context.Abort()
  10. const abortIndex int8 = math.MaxInt8 / 2
  11. func (c *Context) Abort() {
  12. c.index = abortIndex
  13. }

关闭http服务

  1. func main() {
  2. router := gin.Default()
  3. router.GET("/", func(c *gin.Context) {
  4. time.Sleep(5 * time.Second)
  5. c.String(http.StatusOK, "Welcome Gin Server")
  6. })
  7. srv := &http.Server{
  8. Addr: ":8080",
  9. Handler: router,
  10. }
  11. go func() {
  12. // 服务连接
  13. if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  14. log.Fatalf("listen: %s\n", err)
  15. }
  16. }()
  17. // 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
  18. quit := make(chan os.Signal)
  19. signal.Notify(quit, os.Interrupt)
  20. <-quit
  21. log.Println("Shutdown Server ...")
  22. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  23. defer cancel()
  24. if err := srv.Shutdown(ctx); err != nil {
  25. log.Fatal("Server Shutdown:", err)
  26. }
  27. log.Println("Server exiting")
  28. }

3.底层代码分析

Engine 容器对象,整个框架的基础
Engine.trees 负责存储路由和handle方法的映射,采用类似字典树的结构,go的每种方法(GET,POST,DELETE等)都有自己单独的树
Engine.RouterGroup,其中的Handlers存储着所有中间件
Context上下文对象,负责处理请求和回应,其中的handlers是存储处理请求时中间件和处理方法的

开启http处理及路由匹配

可以看出底层还是使用了go自带的http包来进行处理,通过实现ServeHTTP方法来实现gin自己的http处理
go自带的处理函数虽然已经实现了基本功能,但是路由匹配功能过于简单(处理http请求是单独开启gorouti)

  1. func (engine *Engine) Run(addr ...string) (err error) {
  2. //省略部分代码...
  3. address := resolveAddress(addr)
  4. debugPrint("Listening and serving HTTP on %s\n", address)
  5. err = http.ListenAndServe(address, engine)
  6. return
  7. }
  1. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  2. c := engine.pool.Get().(*Context)//pool是一个sync.pool结构
  3. // Get对象后做初始化
  4. c.writermem.reset(w)
  5. c.Request = req
  6. c.reset()//重置所有参数
  7. engine.handleHTTPRequest(c)
  8. engine.pool.Put(c)
  9. }
  1. // gin.go
  2. func (engine *Engine) handleHTTPRequest(c *Context) {
  3. // ...
  4. // 根据请求方法找到对应的路由树
  5. t := engine.trees
  6. for i, tl := 0, len(t); i < tl; i++ {
  7. if t[i].method != httpMethod {
  8. continue
  9. }
  10. root := t[i].root
  11. // Find route in tree
  12. value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
  13. if value.params != nil {
  14. c.Params = *value.params
  15. }
  16. if value.handlers != nil {
  17. c.handlers = value.handlers
  18. c.fullPath = value.fullPath
  19. c.Next() // 执行函数链条
  20. c.writermem.WriteHeaderNow()
  21. return
  22. }
  23. //...
  24. }
  25. // ...
  26. c.handlers = engine.allNoRoute
  27. serveError(c, http.StatusNotFound, default404Body)
  28. }
  1. // tree.go
  2. type nodeValue struct {
  3. handlers HandlersChain
  4. params *Params //[]Param key->value
  5. tsr bool
  6. fullPath string
  7. }
  8. func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) {
  9. var globalParamsCount int16
  10. walk: // Outer loop for walking the tree
  11. for {
  12. prefix := n.path
  13. if len(path) > len(prefix) {
  14. if path[:len(prefix)] == prefix {
  15. path = path[len(prefix):]
  16. // Try all the non-wildcard children first by matching the indices
  17. idxc := path[0]
  18. for i, c := range []byte(n.indices) {
  19. if c == idxc {
  20. // strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
  21. if n.wildChild {
  22. index := len(*skippedNodes)
  23. *skippedNodes = (*skippedNodes)[:index+1]
  24. (*skippedNodes)[index] = skippedNode{
  25. path: prefix + path,
  26. node: &node{
  27. path: n.path,
  28. wildChild: n.wildChild,
  29. nType: n.nType,
  30. priority: n.priority,
  31. children: n.children,
  32. handlers: n.handlers,
  33. fullPath: n.fullPath,
  34. },
  35. paramsCount: globalParamsCount,
  36. }
  37. }
  38. n = n.children[i]
  39. continue walk
  40. }
  41. }
  42. if !n.wildChild {
  43. // If the path at the end of the loop is not equal to '/' and the current node has no child nodes
  44. // the current node needs to roll back to last vaild skippedNode
  45. if path != "/" {
  46. for l := len(*skippedNodes); l > 0; {
  47. skippedNode := (*skippedNodes)[l-1]
  48. *skippedNodes = (*skippedNodes)[:l-1]
  49. if strings.HasSuffix(skippedNode.path, path) {
  50. path = skippedNode.path
  51. n = skippedNode.node
  52. if value.params != nil {
  53. *value.params = (*value.params)[:skippedNode.paramsCount]
  54. }
  55. globalParamsCount = skippedNode.paramsCount
  56. continue walk
  57. }
  58. }
  59. }
  60. // Nothing found.
  61. // We can recommend to redirect to the same URL without a
  62. // trailing slash if a leaf exists for that path.
  63. value.tsr = path == "/" && n.handlers != nil
  64. return
  65. }
  66. // Handle wildcard child, which is always at the end of the array
  67. n = n.children[len(n.children)-1]
  68. globalParamsCount++
  69. switch n.nType {
  70. case param:
  71. // fix truncate the parameter
  72. // tree_test.go line: 204
  73. // Find param end (either '/' or path end)
  74. end := 0
  75. for end < len(path) && path[end] != '/' {
  76. end++
  77. }
  78. // Save param value
  79. if params != nil && cap(*params) > 0 {
  80. if value.params == nil {
  81. value.params = params
  82. }
  83. // Expand slice within preallocated capacity
  84. i := len(*value.params)
  85. *value.params = (*value.params)[:i+1]
  86. val := path[:end]
  87. if unescape {
  88. if v, err := url.QueryUnescape(val); err == nil {
  89. val = v
  90. }
  91. }
  92. (*value.params)[i] = Param{
  93. Key: n.path[1:],
  94. Value: val,
  95. }
  96. }
  97. // we need to go deeper!
  98. if end < len(path) {
  99. if len(n.children) > 0 {
  100. path = path[end:]
  101. n = n.children[0]
  102. continue walk
  103. }
  104. // ... but we can't
  105. value.tsr = len(path) == end+1
  106. return
  107. }
  108. if value.handlers = n.handlers; value.handlers != nil {
  109. value.fullPath = n.fullPath
  110. return
  111. }
  112. if len(n.children) == 1 {
  113. // No handle found. Check if a handle for this path + a
  114. // trailing slash exists for TSR recommendation
  115. n = n.children[0]
  116. value.tsr = n.path == "/" && n.handlers != nil
  117. }
  118. return
  119. case catchAll:
  120. // Save param value
  121. if params != nil {
  122. if value.params == nil {
  123. value.params = params
  124. }
  125. // Expand slice within preallocated capacity
  126. i := len(*value.params)
  127. *value.params = (*value.params)[:i+1]
  128. val := path
  129. if unescape {
  130. if v, err := url.QueryUnescape(path); err == nil {
  131. val = v
  132. }
  133. }
  134. (*value.params)[i] = Param{
  135. Key: n.path[2:],
  136. Value: val,
  137. }
  138. }
  139. value.handlers = n.handlers
  140. value.fullPath = n.fullPath
  141. return
  142. default:
  143. panic("invalid node type")
  144. }
  145. }
  146. }
  147. if path == prefix {
  148. // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node
  149. // the current node needs to roll back to last vaild skippedNode
  150. if n.handlers == nil && path != "/" {
  151. for l := len(*skippedNodes); l > 0; {
  152. skippedNode := (*skippedNodes)[l-1]
  153. *skippedNodes = (*skippedNodes)[:l-1]
  154. if strings.HasSuffix(skippedNode.path, path) {
  155. path = skippedNode.path
  156. n = skippedNode.node
  157. if value.params != nil {
  158. *value.params = (*value.params)[:skippedNode.paramsCount]
  159. }
  160. globalParamsCount = skippedNode.paramsCount
  161. continue walk
  162. }
  163. }
  164. // n = latestNode.children[len(latestNode.children)-1]
  165. }
  166. // We should have reached the node containing the handle.
  167. // Check if this node has a handle registered.
  168. if value.handlers = n.handlers; value.handlers != nil {
  169. value.fullPath = n.fullPath
  170. return
  171. }
  172. // If there is no handle for this route, but this route has a
  173. // wildcard child, there must be a handle for this path with an
  174. // additional trailing slash
  175. if path == "/" && n.wildChild && n.nType != root {
  176. value.tsr = true
  177. return
  178. }
  179. // No handle found. Check if a handle for this path + a
  180. // trailing slash exists for trailing slash recommendation
  181. for i, c := range []byte(n.indices) {
  182. if c == '/' {
  183. n = n.children[i]
  184. value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
  185. (n.nType == catchAll && n.children[0].handlers != nil)
  186. return
  187. }
  188. }
  189. return
  190. }
  191. // Nothing found. We can recommend to redirect to the same URL with an
  192. // extra trailing slash if a leaf exists for that path
  193. value.tsr = path == "/" ||
  194. (len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
  195. path == prefix[:len(prefix)-1] && n.handlers != nil)
  196. // roll back to last valid skippedNode
  197. if !value.tsr && path != "/" {
  198. for l := len(*skippedNodes); l > 0; {
  199. skippedNode := (*skippedNodes)[l-1]
  200. *skippedNodes = (*skippedNodes)[:l-1]
  201. if strings.HasSuffix(skippedNode.path, path) {
  202. path = skippedNode.path
  203. n = skippedNode.node
  204. if value.params != nil {
  205. *value.params = (*value.params)[:skippedNode.paramsCount]
  206. }
  207. globalParamsCount = skippedNode.paramsCount
  208. continue walk
  209. }
  210. }
  211. }
  212. return
  213. }
  214. }

总体来说,就是遍历整个前缀树,直到找到有handler方法的节点

  1. // Next should be used only inside middleware.
  2. // It executes the pending handlers in the chain inside the calling handler.
  3. func (c *Context) Next() {
  4. c.index++
  5. for c.index < int8(len(c.handlers)) {
  6. c.handlers[c.index](c)
  7. c.index++
  8. }
  9. }

注意next方法要在中间件中使用,类似嵌套关系,使用next()代表执行完next()所在行之前的方法后就先加载链路后面的内容,否则就先执行本节点后面的内容

路由注册

在我们使用GET,POST等方法后,都会调用handle方法,只不过httpMethod对应的不同

  1. func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
  2. //拼接绝对路径,将之前可能有的和新输入的进行拼接,源码看下面
  3. absolutePath := group.calculateAbsolutePath(relativePath)
  4. //方法拼接
  5. handlers = group.combineHandlers(handlers)
  6. group.engine.addRoute(httpMethod, absolutePath, handlers)
  7. return group.returnObj()
  8. }
  9. func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
  10. //省略判断路由格式代码...
  11. //trees是一个methodTree切片,包含method和root,root是一个*node,method是GET,POST等
  12. root := engine.trees.get(method)
  13. if root == nil {
  14. root = new(node)
  15. root.fullPath = "/"
  16. engine.trees = append(engine.trees, methodTree{method: method, root: root})
  17. }
  18. root.addRoute(path, handlers)//更新前缀树
  19. // Update maxParams...
  20. }
  1. type node struct {
  2. // 当前节点路径
  3. path string
  4. // 和children字段对应, 保存的是分裂的分支的第一个字符
  5. // 例如search和support, 那么s节点的indices对应的"eu"
  6. // 代表有两个分支, 分支的首字母分别是e和u
  7. indices string
  8. // 儿子节点
  9. children []*node
  10. // 处理函数链条(切片)
  11. handlers HandlersChain
  12. // 优先级,子节点、子子节点等注册的handler数量
  13. priority uint32
  14. // 节点类型,包括static, root, param, catchAll
  15. // static: 静态节点(默认),比如上面的s,earch等节点
  16. // root: 树的根节点
  17. // catchAll: 有*匹配的节点
  18. // param: 参数节点
  19. nType nodeType
  20. // 路径上最大参数个数
  21. maxParams uint8
  22. // 节点是否是参数节点,比如上面的:post
  23. wildChild bool
  24. // 完整路径
  25. fullPath string
  26. }

不论是Group()还是Handle()都使用了calculateAbsolutePathcombineHandlers这两个方法,保存在RouterGroup这个指针对象中

  1. //计算路由
  2. func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
  3. return joinPaths(group.basePath, relativePath)
  4. }
  5. func joinPaths(absolutePath, relativePath string) string {
  6. if relativePath == "" {
  7. return absolutePath
  8. }
  9. finalPath := path.Join(absolutePath, relativePath)
  10. if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' {
  11. return finalPath + "/"
  12. }
  13. return finalPath
  14. }
  15. //方法拼接
  16. func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
  17. finalSize := len(group.Handlers) + len(handlers)
  18. if finalSize >= int(abortIndex) {
  19. panic("too many handlers")
  20. }
  21. mergedHandlers := make(HandlersChain, finalSize)
  22. copy(mergedHandlers, group.Handlers)
  23. copy(mergedHandlers[len(group.Handlers):], handlers)
  24. return mergedHandlers
  25. }
  1. // tree.go
  2. // addRoute 将具有给定句柄的节点添加到路径中。
  3. // 不是并发安全的
  4. func (n *node) addRoute(path string, handlers HandlersChain) {
  5. fullPath := path
  6. n.priority++
  7. // 空树就直接插入当前节点
  8. if len(n.path) == 0 && len(n.children) == 0 {
  9. n.insertChild(path, fullPath, handlers)
  10. n.nType = root
  11. return
  12. }
  13. parentFullPathIndex := 0
  14. walk:
  15. for {
  16. // 找到最长的通用前缀
  17. // 这也意味着公共前缀不包含“:”"或“*” /
  18. // 因为现有键不能包含这些字符。
  19. i := longestCommonPrefix(path, n.path)
  20. // 分裂边缘(此处分裂的是当前树节点)
  21. // 例如一开始path是search,新加入support,s是他们通用的最长前缀部分
  22. // 那么会将s拿出来作为parent节点,增加earch和upport作为child节点
  23. if i < len(n.path) {
  24. child := node{
  25. path: n.path[i:], // 公共前缀后的部分作为子节点
  26. wildChild: n.wildChild,
  27. indices: n.indices,
  28. children: n.children,
  29. handlers: n.handlers,
  30. priority: n.priority - 1, //子节点优先级-1
  31. fullPath: n.fullPath,
  32. }
  33. n.children = []*node{&child}
  34. // []byte for proper unicode char conversion, see #65
  35. n.indices = bytesconv.BytesToString([]byte{n.path[i]})
  36. n.path = path[:i]
  37. n.handlers = nil
  38. n.wildChild = false
  39. n.fullPath = fullPath[:parentFullPathIndex+i]
  40. }
  41. // 将新来的节点插入新的parent节点作为子节点
  42. if i < len(path) {
  43. path = path[i:]
  44. // 取path首字母,用来与indices做比较
  45. c := path[0]
  46. // 处理参数后加斜线情况
  47. if n.nType == param && c == '/' && len(n.children) == 1 {
  48. parentFullPathIndex += len(n.path)
  49. n = n.children[0]
  50. n.priority++
  51. continue walk
  52. }
  53. // 检查路path下一个字节的子节点是否存在
  54. // 比如s的子节点现在是earch和upport,indices为eu
  55. // 如果新加一个路由为super,那么就是和upport有匹配的部分u,将继续分列现在的upport节点
  56. for i, max := 0, len(n.indices); i < max; i++ {
  57. if c == n.indices[i] {
  58. parentFullPathIndex += len(n.path)
  59. i = n.incrementChildPrio(i)
  60. n = n.children[i]
  61. continue walk
  62. }
  63. }
  64. // 否则就插入
  65. if c != ':' && c != '*' && n.nType != catchAll {
  66. // []byte for proper unicode char conversion, see #65
  67. // 注意这里是直接拼接第一个字符到n.indices
  68. n.indices += bytesconv.BytesToString([]byte{c})
  69. child := &node{
  70. fullPath: fullPath,
  71. }
  72. // 追加子节点
  73. n.children = append(n.children, child)
  74. n.incrementChildPrio(len(n.indices) - 1)
  75. n = child
  76. }else if n.wildChild {// 如果是参数节点
  77. n = n.children[len(n.children)-1]
  78. n.priority++
  79. // 检查通配符是否匹配
  80. if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
  81. // Adding a child to a catchAll is not possible
  82. n.nType != catchAll &&
  83. // 检查更长的通配符, 例如 :name and :names
  84. (len(n.path) >= len(path) || path[len(n.path)] == '/') {
  85. continue walk
  86. }
  87. // Wildcard conflict
  88. pathSeg := path
  89. if n.nType != catchAll {
  90. pathSeg = strings.SplitN(pathSeg, "/", 2)[0]
  91. }
  92. prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
  93. panic("'" + pathSeg +
  94. "' in new path '" + fullPath +
  95. "' conflicts with existing wildcard '" + n.path +
  96. "' in existing prefix '" + prefix +
  97. "'")
  98. }
  99. n.insertChild(path, fullPath, handlers)
  100. return
  101. }
  102. // 已经注册过的节点
  103. if n.handlers != nil {
  104. panic("handlers are already registered for path '" + fullPath + "'")
  105. }
  106. n.handlers = handlers
  107. return
  108. }
  109. }
  1. // tree.go
  2. func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {
  3. // 找到所有的参数
  4. for numParams > 0 {
  5. // 第一个通配符之前的前缀
  6. wildcard, i, valid := findWildcard(path)
  7. if i < 0 { // 没有发现通配符
  8. break
  9. }
  10. // 通配符的名称必须不包含':' 和 '*'
  11. if !valid {
  12. panic("only one wildcard per path segment is allowed, has: '" +
  13. wildcard + "' in path '" + fullPath + "'")
  14. }
  15. // 检查通配符是否有名称
  16. if len(wildcard) < 2 {
  17. panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
  18. }
  19. // 检查这个节点是否有已经存在的子节点
  20. // 如果我们在这里插入通配符,这些子节点将无法访问
  21. if len(n.children) > 0 {
  22. panic("wildcard segment '" + wildcard +
  23. "' conflicts with existing children in path '" + fullPath + "'")
  24. }
  25. if wildcard[0] == ':' { // param
  26. if i > 0 {
  27. // 在当前通配符之前插入前缀
  28. n.path = path[:i]
  29. path = path[i:]
  30. }
  31. n.wildChild = true
  32. child := &node{
  33. nType: param,
  34. path: wildcard,
  35. maxParams: numParams,
  36. fullPath: fullPath,
  37. }
  38. n.children = []*node{child}
  39. n = child
  40. n.priority++
  41. numParams--
  42. // 如果路径没有以通配符结束
  43. // 那么将有另一个以'/'开始的非通配符子路径。
  44. if len(wildcard) < len(path) {
  45. path = path[len(wildcard):]
  46. child := &node{
  47. maxParams: numParams,
  48. priority: 1,
  49. fullPath: fullPath,
  50. }
  51. n.children = []*node{child}
  52. n = child // 继续下一轮循环
  53. continue
  54. }
  55. // 否则我们就完成了。将处理函数插入新叶子中
  56. n.handlers = handlers
  57. return
  58. }
  59. // catchAll
  60. if i+len(wildcard) != len(path) || numParams > 1 {
  61. panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
  62. }
  63. if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
  64. panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
  65. }
  66. // currently fixed width 1 for '/'
  67. i--
  68. if path[i] != '/' {
  69. panic("no / before catch-all in path '" + fullPath + "'")
  70. }
  71. n.path = path[:i]
  72. // 第一个节点:路径为空的catchAll节点
  73. child := &node{
  74. wildChild: true,
  75. nType: catchAll,
  76. maxParams: 1,
  77. fullPath: fullPath,
  78. }
  79. // 更新父节点的maxParams
  80. if n.maxParams < 1 {
  81. n.maxParams = 1
  82. }
  83. n.children = []*node{child}
  84. n.indices = string('/')
  85. n = child
  86. n.priority++
  87. // 第二个节点:保存变量的节点
  88. child = &node{
  89. path: path[i:],
  90. nType: catchAll,
  91. maxParams: 1,
  92. handlers: handlers,
  93. priority: 1,
  94. fullPath: fullPath,
  95. }
  96. n.children = []*node{child}
  97. return
  98. }
  99. // 如果没有找到通配符,只需插入路径和句柄
  100. n.path = path
  101. n.handlers = handlers
  102. n.fullPath = fullPath
  103. }

注册中间件

  1. // Use adds middleware to the group, see example code in GitHub.
  2. func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
  3. group.Handlers = append(group.Handlers, middleware...)
  4. return group.returnObj()
  5. }
  6. type RouterGroup struct {
  7. Handlers HandlersChain //type HandlersChain []HandlerFunc
  8. basePath string
  9. engine *Engine
  10. root bool
  11. }

之前提到gin.Default()和gin.New()的不同在于是否需要自己加载log和reovery中间件

  1. func Default() *Engine {
  2. debugPrintWARNINGDefault()
  3. engine := New()
  4. engine.Use(Logger(), Recovery()) // 默认注册的两个中间件
  5. return engine
  6. }

参考

https://baijiahao.baidu.com/s?id=1716852044736696668&wfr=spider&for=pc
https://blog.csdn.net/luo1324574369/article/details/108310032
https://blog.csdn.net/weixin_45961841/article/details/113722686
https://blog.csdn.net/qq_37767455/article/details/104712028