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
// Defaults
if config.Skipper == nil
config.Skipper = DefaultGzipConfig.Skipper
if config.Level == 0
config.Level = DefaultGzipConfig.Level
return 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 的实现包含大量的优秀源码及设计思路,是一个指导个人成长的优秀教材。
关注我,了解更多源码设计及实现细节...
推荐阅读:
#架构|高可用|高性能|高并发|高容错|HTTP|TLS|网络|加密算法|面试|同步|异步#
以上是关于echo 框架中的 middleware 设计深度解析的主要内容,如果未能解决你的问题,请参考以下文章