乐趣区

关于golang:七天实现web框架中间件的实现

在 web 开发中,咱们须要在真正的逻辑解决前,咱们有些操作须要提前进行解决,同时逻辑解决后,咱们须要解决其余的逻辑。

什么是中间件

在正式的业务逻辑解决前或之后,咱们须要进行一些解决。这些解决是须要咱们增加中间件来进行增加的。因为在框架中,咱们须要给用户本人增加一些解决形式函数的中央的,这个中央呢咱们不能太过于底层,这样对于用户来说必定是不好的。
所以咱们须要一个比拟好的形式进行增加。

实现思路

对于这个中间件的增加,逻辑咱们也是很简略的,咱们应用一个切片进行存储函数来实现,首先是须要在路由中进行存储的,而后咱们将这个货色放在上下文中去进行存储。

type RouterGroup struct {
    prefix      string
    middlewares []HandlerFunc // support middleware
    parent      *RouterGroup  // support nesting
    engine      *Engine       // all groups share a Engine instance
}

上下文中的存储

type Context struct {
    // origin objects
    Writer http.ResponseWriter
    Req    *http.Request
    // request info
    Path   string
    Method string
    Params map[string]string
    // response info
    StatusCode int
    // middleware
    handlers []HandlerFunc
    index    int
}

func (c *Context) Next() {
    c.index++
    s := len(c.handlers)
    for ; c.index < s; c.index++ {c.handlers[c.index](c)
    }
}

为什么在路由中进行存储了咱们还须要在上下文进行存储呢?
咱们能够看到,在这里咱们还实现了一个函数,就是 next,对于 next 函数其实就是咱们将中间件分成了两个局部,一个是解决业务代码前解决,和业务代码解决后处理,那么在 next 前的就是业务逻辑前,而后前面就是业务逻辑后。而后这里咱们做一个演示:

func A(c *Context) {
    part1
    c.Next()
    part2
}
func B(c *Context) {
    part3
    c.Next()
    part4
}

咱们来看一下下面这个函数的执行程序:
假如咱们利用了中间件 A 和 B,和路由映射的 Handler。c.handlers 是这样的 [A, B, Handler],c.index 初始化为 -1。调用 c.Next(),接下来的流程是这样的:

c.index++,c.index 变为 0
0 < 3,调用 c.handlers[0],即 A
执行 part1,调用 c.Next()
c.index++,c.index 变为 1
1 < 3,调用 c.handlers[1],即 B
执行 part3,调用 c.Next()
c.index++,c.index 变为 2
2 < 3,调用 c.handlers[2],即 Handler
Handler 调用结束,返回到 B 中的 part4,执行 part4
part4 执行结束,返回到 A 中的 part2,执行 part2
part2 执行结束,完结。
一句话说分明重点,最终的程序是 part1 -> part3 -> Handler -> part 4 -> part2。恰好满足了咱们对中间件的要求,接下来看调用局部的代码,就能全副串起来了。

测试函数

func onlyForV2() gee.HandlerFunc {return func(c *gee.Context) {
        // Start timer
        t := time.Now()
        // if a server error occurred
        c.Fail(500, "Internal Server Error")
        // Calculate resolution time
        log.Printf("[%d] %s in %v for group v2", c.StatusCode, c.Req.RequestURI, time.Since(t))
    }
}

func main() {r := gee.New()
    r.Use(gee.Logger()) // global midlleware
    r.GET("/", func(c *gee.Context) {c.HTML(http.StatusOK, "<h1>Hello Gee</h1>")
    })

    v2 := r.Group("/v2")
    v2.Use(onlyForV2()) // v2 group middleware
    {v2.GET("/hello/:name", func(c *gee.Context) {
            // expect /hello/geektutu
            c.String(http.StatusOK, "hello %s, you're at %s\n", c.Param("name"), c.Path)
        })
    }

    r.Run(":9999")
}
退出移动版