Gin c.Next(),c.Abort()以及洋葱模型

  • 前置源码

    // gin context处理
    func (engine *Engine) HandleContext(c *Context) {
    oldIndexValue := c.index
    c.reset()
    engine.handleHTTPRequest(c)

    c.index = oldIndexValue
    }

    // 路由匹配逻辑
    func (engine *Engine) handleHTTPRequest(c *Context) {
    httpMethod := c.Request.Method
    rPath := c.Request.URL.Path
    unescape := false
    if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
    rPath = c.Request.URL.RawPath
    unescape = engine.UnescapePathValues
    }

    if engine.RemoveExtraSlash {
    rPath = cleanPath(rPath)
    }

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

    if engine.HandleMethodNotAllowed {
    // According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
    // containing a list of the target resource's currently supported methods.
    allowed := make([]string, 0, len(t)-1)
    for _, tree := range engine.trees {
    if tree.method == httpMethod {
    continue
    }
    if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
    allowed = append(allowed, tree.method)
    }
    }
    if len(allowed) > 0 {
    c.handlers = engine.allNoMethod
    c.writermem.Header().Set("Allow", strings.Join(allowed, ", "))
    serveError(c, http.StatusMethodNotAllowed, default405Body)
    return
    }
    }

    c.handlers = engine.allNoRoute
    serveError(c, http.StatusNotFound, default404Body)
    }
  • c.Next()源码

    func (c *Context) Next() {
    c.index++  // index是int8类型,初始值是-1
    for c.index < int8(len(c.handlers)) {
    c.handlers[c.index](c)
    c.index++
    }
    }
    // handlers包括,中间件和路由方法
    /*
    文字演示:
    // 假设gin.context会经过3个中间件,一个路由方法,且中间件中都调用了c.Next(),没有中断. //
    从前面handleHTTPRequest方法代码可以知道,在请求进入中间件之前,调用了一次c.Next(),调用了第一个中间件,即c.handlers[0],此时index是0.
    第一个中间件调用c.Next()之后,index再次加1,等于1,然后调用c.handler[1],进入第二个中间件,
    第二个中间件调用c.Next()之后,index再次加1,等于2,然后调用c.handler[2],进入第三个中间件,
    第三个中间件调用c.Next()之后,index再次加1,等于3,然后调用c.handler[3],进入路由方法.
    路由方法执行完之后,会执行c.handlers[c.index](c)后面的c.index++,所以index再加1,变成4,
    此时index<len(c.handlers)不成立,所以退出当前循环,
    由于当前循环是上个循环中调用的,退出当前循环之后,会调用上一层循环的c.index++,此时index变成5
    以此类推,退出所有循环之后,index会加到7.
    总结,index最终大小是"-1 + (len(c.handlers) * 2)".
    由于index是int8类型,最大正数值是127,所以len(c.handlers)不能大于63,
    所以,默认设计中,中间件数量不能大于62个,否则会出现错乱.
    */
  • c.Abort源码

    func (c *Context) Abort() {
    c.index = abortIndex  // abortIndex = math.MaxInt8 >> 1,即127
    }

    /*
    将index置为int8最大值,从而在下次
    for c.index < int8(len(c.handlers)){
    ...
    }
    循环时,不再进入c.handlers[n]的调用,实现阻止后续中间件/路由的作用.
    同样的,这个设计依然依赖中间件数量不超过62,否则会出现错乱.
    */
  • c.Next()&c.Abort()总结

    /*
    两个方法通过index巧妙配合,实现了对context的控制.
    总结如下:
    1.默认中间件数量不能超过62个.
    2.c.Abort()只对还没调用c.Next()的中间件有效,
    已经调用c.Next()的中间件,
    其c.Next()之前代码已经执行,自不受影响,
    其c.Next()之后代码,仍会执行,也不受影响,因为退出循环之前,肯定是先把"c.handlers[c.index](c)"执行完才能退出.
    3.c.Abort()并不会终止当前中间件的代码,需要加return才能结束当前中间件的调用.
    */
  • 洋葱模型

    https://juejin.cn/post/7134929302822322207 这个文章有很清晰的图片,进去主要看图.

posted @ 2025-06-27 14:26  CJTARRR  阅读(30)  评论(0)    收藏  举报