整体结构认识

image.png

路由匹配

gin框架处理请求的入口函数ServeHTTP

  1. // gin.go
  2. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  3. // 这里使用了对象池
  4. c := engine.pool.Get().(*Context)
  5. // Get对象后做初始化
  6. c.writermem.reset(w)
  7. c.Request = req
  8. c.reset()
  9. engine.handleHTTPRequest(c) // 我们要找的处理HTTP请求的函数
  10. engine.pool.Put(c) // 处理完请求后将对象放回池子
  11. }

处理 handleHTTPRequest

  1. func (engine *Engine) handleHTTPRequest(c *Context) {
  2. httpMethod := c.Request.Method
  3. rPath := c.Request.URL.Path
  4. unescape := false
  5. if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
  6. rPath = c.Request.URL.RawPath
  7. unescape = engine.UnescapePathValues
  8. }
  9. if engine.RemoveExtraSlash {
  10. rPath = cleanPath(rPath)
  11. }
  12. // Find root of the tree for the given HTTP method
  13. // 根据请求方法找到对应的路由树
  14. t := engine.trees
  15. for i, tl := 0, len(t); i < tl; i++ {
  16. if t[i].method != httpMethod {
  17. continue
  18. }
  19. root := t[i].root
  20. // Find route in tree 在路由树中根据path查找
  21. value := root.getValue(rPath, c.params, unescape)
  22. if value.params != nil {
  23. c.Params = *value.params
  24. }
  25. if value.handlers != nil {
  26. c.handlers = value.handlers
  27. c.fullPath = value.fullPath
  28. // 执行函数链条
  29. c.Next()
  30. c.writermem.WriteHeaderNow()
  31. return
  32. }
  33. if httpMethod != "CONNECT" && rPath != "/" {
  34. if value.tsr && engine.RedirectTrailingSlash {
  35. redirectTrailingSlash(c)
  36. return
  37. }
  38. if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
  39. return
  40. }
  41. }
  42. break
  43. }
  44. if engine.HandleMethodNotAllowed {
  45. for _, tree := range engine.trees {
  46. if tree.method == httpMethod {
  47. continue
  48. }
  49. if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil {
  50. c.handlers = engine.allNoMethod
  51. serveError(c, http.StatusMethodNotAllowed, default405Body)
  52. return
  53. }
  54. }
  55. }
  56. c.handlers = engine.allNoRoute
  57. serveError(c, http.StatusNotFound, default404Body)
  58. }

路由匹配是由节点的 getValue方法实现的。getValue根据给定的路径(键)返回nodeValue值,保存注册的处理函数和匹配到的路径参数数据。

gin框架涉及中间件相关有4个常用的方法,它们分别是c.Next()c.Abort()c.Set()c.Get()

中间件的注册

gin框架中的中间件设计很巧妙,从最常用的r := gin.Default()Default函数开始看,它内部构造一个新的engine之后就通过Use()函数注册了Logger中间件和Recovery中间件:

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

Use() 函数

  1. func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
  2. engine.RouterGroup.Use(middleware...) // 实际上还是调用的RouterGroup的Use函数
  3. engine.rebuild404Handlers()
  4. engine.rebuild405Handlers()
  5. return engine
  6. }

注册中间件其实就是将中间件函数追加到group.Handlers中:

  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. }

而我们注册路由时会将对应路由的函数和之前的中间件函数结合到一起:

  1. func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
  2. absolutePath := group.calculateAbsolutePath(relativePath)
  3. handlers = group.combineHandlers(handlers) // 将处理请求的函数与中间件函数结合
  4. group.engine.addRoute(httpMethod, absolutePath, handlers)
  5. return group.returnObj()
  6. }
  1. package main
  2. import "github.com/gin-gonic/gin"
  3. func main() {
  4. r := gin.Default()
  5. r.GET("/ping", func(c *gin.Context) {
  6. c.JSON(200, gin.H{
  7. "message": "success",
  8. })
  9. })
  10. r.Run() // listen and serve on 0.0.0.0:8080
  11. }

先看r.Run()

  1. func (engine *Engine) Run(addr ...string) (err error) {
  2. defer func() { debugPrintError(err) }()
  3. address := resolveAddress(addr)
  4. debugPrint("Listening and serving HTTP on %s\n", address)
  5. err = http.ListenAndServe(address, engine)
  6. return
  7. }

https://www.haohongfan.com/post/2019-02-17-gin-01/

https://www.linkinstar.wiki/2019/08/20/golang/open-source-component/gin/