<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] https://www.cnblogs.com/binHome/p/13411878.html ![](https://img.kancloud.cn/b8/91/b8914c0d9c8f6b61f32151d3dd319dd6_2261x1908.png) ### 路由樹 是一顆前綴樹 一共9個總結點,即:get、post、put等等 ### 注冊路由 > 邏輯主要有`addRoute`函數和`insertChild`方法。 1、 ~~~ 這是gin.go的方法 func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { 。。。 root := engine.trees.get(method) //1、調用的樹的get方法,獲取當前總的方法節點,沒有返回nil,eg:get的樹節點, if root == nil { root = new(node) root.fullPath = "/" engine.trees = append(engine.trees, methodTree{method: method, root: root}) } root.addRoute(path, handlers) //2、再調用樹的的addRoute,注冊 。。。 } ~~~ 2、如果是新添加的,那就直接添加返回,否則就獲取前綴,獲取前綴后,已前綴作為新的父節點,并把之前的節點(通過insertChild方法)作為子節點,放到前綴節點的下面 >`insertChild`函數是根據`path`本身進行分割,將`/`分開的部分分別作為節點保存,形成一棵樹結構。參數匹配中的`:`和`*`的區別是,前者是匹配一個字段而后者是匹配后面所有的路徑。 ![](https://img.kancloud.cn/46/33/4633a4e368e1115c4e5d6523fe35ae0f_800x420.gif) ### 路由匹配 ~~~ 這是gin.go的方法 func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { `// 這里使用了對象池` c := engine.pool.Get().(*Context) // 這里有一個細節就是Get對象后做初始化 c.writermem.reset(w) c.Request = req c.reset() engine.handleHTTPRequest(c) //處理HTTP請求的函數 engine.pool.Put(c) // 處理完請求后將對象放回池子 } ~~~ ~~~ func (engine *Engine) handleHTTPRequest(c *Context) { 。。。。 // Find root of the tree for the given HTTP method // 根據請求方法找到對應的路由樹 t := engine.trees for i, tl := 0, len(t); i < tl; i++ { if t[i].method != httpMethod { continue } root := t[i].root // Find route in tree // 在路由樹中根據path查找,主要的方法 value := root.getValue(rPath, c.params, c.skippedNodes, unescape) if value.params != nil { c.Params = *value.params } if value.handlers != nil { c.handlers = value.handlers c.fullPath = value.fullPath c.Next() // 執行函數鏈條 c.writermem.WriteHeaderNow() return } if httpMethod != http.MethodConnect && rPath != "/" { if value.tsr && engine.RedirectTrailingSlash { redirectTrailingSlash(c) return } if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) { return } } break } 。。。。。 c.handlers = engine.allNoRoute serveError(c, http.StatusNotFound, default404Body) } ~~~ >路由匹配是由節點的`getValue`方法實現的。`getValue`根據給定的路徑(鍵)返回`nodeValue`值, 保存注冊的處理函數和匹配到的路徑參數數據。 如果找不到任何處理函數,則會嘗試TSR(尾隨斜杠重定向)。 步驟: 調用 handleHTTPRequest 函數,通過getValue ,獲取方法樹的節點,沒有就報錯,有就遍歷節點上的handers(切片類型,next遍歷中間件和方法) ## gin框架中間件詳解 gin框架涉及中間件相關有4個常用的方法,它們分別是`c.Next()`、`c.Abort()`、`c.Set()`、`c.Get()`。 ### gin.Default()和gin.New() 本質上,起到的作用是一樣的,都是初始化engine引擎 default是調用了new,并且use了兩個中間件 #### Default ~~~ func Default() *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine } ~~~ #### New ~~~ func New() *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ Handlers: nil, basePath: "/", root: true, }, FuncMap: template.FuncMap{}, RedirectTrailingSlash: true, RedirectFixedPath: false, HandleMethodNotAllowed: false, ForwardedByClientIP: true, RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"}, TrustedPlatform: defaultPlatform, UseRawPath: false, RemoveExtraSlash: false, UnescapePathValues: true, MaxMultipartMemory: defaultMultipartMemory, trees: make(methodTrees, 0, 9), //初始化路由樹,get/post/put 按類型分 delims: render.Delims{Left: "{{", Right: "}}"}, secureJSONPrefix: "while(1);", trustedProxies: []string{"0.0.0.0/0", "::/0"}, trustedCIDRs: defaultTrustedCIDRs, } engine.RouterGroup.engine = engine engine.pool.New = func() any { //這里是context的臨時對象池 return engine.allocateContext() } return engine } ~~~ ### 流程分析 1、從default,找到Use方法,這是注冊中間件的方法 2、 ~~~ func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { engine.RouterGroup.Use(middleware...) // 實際上調用的是RouterGroup的Use函數 engine.rebuild404Handlers() engine.rebuild405Handlers() return engine } ~~~ 從下方的代碼可以看出,注冊中間件其實就是將中間件函數追加到`group.Handlers`中: ~~~ RouterGroup的Use函數 func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { group.Handlers = append(group.Handlers, middleware...) return group.returnObj() } ~~~ 注冊路由時會將對應路由的函數和之前的中間件函數結合到一起: ~~~ 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() } ~~~ 結合操作的函數內容如下,注意觀察這里是如何實現拼接兩個切片得到一個新切片的 ~~~ const abortIndex int8 = math.MaxInt8 >> 1 //相當于除以2 func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { finalSize := len(group.Handlers) + len(handlers) assert1(finalSize < int(abortIndex), "too many handlers") mergedHandlers := make(HandlersChain, finalSize) //`type``HandlersChain []HandlerFunc` copy(mergedHandlers, group.Handlers) copy(mergedHandlers[len(group.Handlers):], handlers) return mergedHandlers } ~~~ ### gin 中間件的原理 洋蔥模型,通過next()和abort() next():后面代碼先不執行(本方法掛起等待),先調用其他中間件(類似指針) abort():組織掛起,后面的不再執行,直接返回 next:通過索引遍歷`HandlersChain`鏈條,從而實現依次調用該路由的每一個函數 Abor:直接把index設置為最大值,中斷遍歷 ~~~ func (c *Context) Abort() { c.index = abortIndex ~~~ ### c.Set()/c.Get() `c.Set()`和`c.Get()`這兩個方法多用于在多個函數之間通過`c`傳遞數據的,比如我們可以在認證中間件中獲取當前請求的相關信息(userID等)通過`c.Set()`存入`c`,然后在后續處理業務邏輯的函數中通過`c.Get()`來獲取當前請求的用戶。`c`就像是一根繩子,將該次請求相關的所有的函數都串起來了 ![](https://img.kancloud.cn/b3/c4/b3c4ed4553b2e9d0a58b6af24f66a388_1560x640.png) ### 多次綁定 報錯eof
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看