echo 框架中的 middleware 设计深度解析

Posted 魏小言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了echo 框架中的 middleware 设计深度解析相关的知识,希望对你有一定的参考价值。

关注我,了解更多源码设计及实现细节...

echo 框架中的 middleware 设计深度解析

“ echo web 框架是 go 语言开发的一种高性能,可扩展,轻量级的web框架。几行代码就可以启动一个高性能的 http 服务端... ”

Echo 简介

了解 Go 语言的同学可能熟悉 Echo ,它是一款高性能、极简的 Web 框架。

Package echo implements high performance, minimalist Go web framework.

Echo 从性能和功能的角度都极大的提升了开发效率。相比其他的 Web 框架,它提供 “ 支持更多类型以及效率更高的 Router,Middleware、及高效的内存管理……“ 等众多优秀的功能特性,并赢得了众多开发者的青睐。

这里就其中 Middleware 组件良好的设计实现展开介绍。

Middleware 组件介绍

Middleware,嵌入在 HTTP 的请求和响应之间。它可以获得 Echo#Context 对象用来进行一些特殊的操作,比如记录每个请求或者统计请求数。

根据其作用生效的位置及对象,可将 Middleware 分为四种,分别是:Before router、After router、Group、Router 

Before router

Echo#Pre() 用于注册一个在路由执行之前运行的中间件,可以用来修改请求的一些属性。比如在请求路径结尾添加或者删除一个’/‘来使之能与路由匹配。

下面的这几个内建中间件应该被注册在这一级别:

  • AddTrailingSlash

  • RemoveTrailingSlash

  • MethodOverride

    注意: 由于在这个级别路由还没有执行,所以这个级别的中间件不能调用任何 echo.Context 的 API。

After router

这个级别的中间件运行在路由处理完请求之后,可以调用所有的 echo.Context API。

下面的这几个内建中间件应该被注册在这一级别:

  • BodyLimit

  • Logger

  • Gzip

  • Recover

  • BasicAuth

  • JWTAuth

  • Secure

  • CORS

  • Static

Group

当在路由中创建一个组的时候,可以为这个组注册一个中间件。例如,给 admin 这个组注册一个 BasicAuth 中间件。

e := echo.New()admin := e.Group("/admin", middleware.BasicAuth())

也可以在创建组之后用 admin.Use()注册该中间件。

Route 

当你创建了一个新的路由,可以选择性的给这个路由注册一个中间件。

e := echo.New()e.GET("/", <Handler>, <Middleware...>)

Middleware 的实现

// MiddlewareFunc defines a function to process middleware.MiddlewareFunc func(HandlerFunc) HandlerFunc// HandlerFunc defines a function to server HTTP requests.HandlerFunc func(Context) error

可以看到 middleware 类型是一个匿名函数,参数和返回值的类型一样,都是 HandlerFunc 类型。

HandlerFunc 类型,是 echo 框架处理业务逻辑的 Handler 结构,如下:

e.GET("/v1/test/metrics", func(c echo.Context) error      return hs.apiQueryMetrics(c))

这样的设计,是偶然吗?

其实不是,我们已 middleware.Gzip() 为例:

e.Use(middleware.Gzip())

Use 的实现是,将 Gzip() 加入 echo 的 middleware 数组:

// Use adds middleware to the chain which is run after router.func (e *Echo) Use(middleware ...MiddlewareFunc) e.middleware = append(e.middleware, middleware...)

middleware.Gzip() 的参数是 next echo.HandlerFunc 下一个 middleware 的 返回值:

// GzipWithConfig return Gzip middleware with config.// See: `Gzip()`.func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc // Defaultsif config.Skipper == nil   config.Skipper = DefaultGzipConfig.Skipperif config.Level == 0   config.Level = DefaultGzipConfig.Levelreturn func(next echo.HandlerFunc) echo.HandlerFunc   return func(c echo.Context) error    if config.Skipper(c)     return next(c)      res := c.Response()   res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)   if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme)     res.Header().Add(echo.HeaderContentEncoding, gzipScheme) // Issue #806    rw := res.Writer    w, err := gzip.NewWriterLevel(rw, config.Level)    if err != nil      return err        defer func()      if res.Size == 0       if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme        res.Header().Del(echo.HeaderContentEncoding)            // We have to reset response to it's pristine state when      // nothing is written to body or error is returned.      // See issue #424, #407.      res.Writer = rw      w.Reset(ioutil.Discard)          w.Close()    ()    grw := &gzipResponseWriterWriter: w, ResponseWriter: rw    res.Writer = grw      return next(c)  

我们了解 Use()、Gzip() 之后,在来看一下 middleware 的执行部分:

func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) name := handlerName(handler)e.router.Add(method, path, func(c Context) error   h := handler  // Chain middleware  for i := len(middleware) - 1; i >= 0; i--    h = middleware[i](h)    return h(c))r := &Route  Method:  method,  Path:    path,  Handler: name,e.router.routes[method+path] = r

关注其中的 for 循环,通过循环,倒序将 middleware 数组中的 MiddlewareFunc 合并成一个 echo.HandlerFunc 去执行。在执行过程中,按照循环的倒序去依次执行 middlewareFunc 定义的函数。

这种设计类似算法中的 递归思想,通过定义 参数和返回相同类型的约束,使函数可以完成递归的实现;并将函数类型与 echo 框架的处理 handler 耦合,这样完美的实现了 handler 的前后代理,在设计模式上,这种设计被称为 代理模式。

echo 的实现包含大量的优秀源码及设计思路,是一个指导个人成长的优秀教材。

关注我,了解更多源码设计及实现细节...

推荐阅读:

百度信息流和搜索双引擎业务中的 KV 存储实践...



#架构|高可用|高性能|高并发|高容错|HTTP|TLS|网络|加密算法|面试|同步|异步#

以上是关于echo 框架中的 middleware 设计深度解析的主要内容,如果未能解决你的问题,请参考以下文章

中科院计算所:潘汀——深度学习框架设计中的关键技术及发展趋

饮水思源--浅析深度学习框架设计中的关键技术

django 自定义中间件 middleware

laravel框架的中间件middleware的详解

python - scrapy 爬虫框架(创建, 持久化, 去重, 深度, cookie)

python学习之路web框架续