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 := &gzipResponseWriter{Writer: 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 设计深度解析相关推荐
- DDD领域驱动设计深度解析
目录 DDD领域驱动设计深度解析 DDD凝聚了软件工程的智慧 DDD领域驱动设计的历史 什么是领域 Domain 领域驱动设计 领域驱动设计几大原则详解 领域驱动模型的概念 领域驱动设计的挑战 DDD ...
- Go实战--golang中使用echo框架中JSONP(labstack/echo)
生命不止,继续 go go go !!! 继续,echo web框架,今天就聊一聊JSONP. JSONP 1.什么是JSONP? JSONP (JSON with padding) is used ...
- cmstop框架中的js设计content.js
控制cmstop框架中action的js 内容模块 找出当前页面的js的思路 01先找显示页面的当前文件.在页面文件中-->找(编辑,删除)按钮-->找获取这个按钮的js选择器 02看加载 ...
- 游戏中的颜色:深度解析游戏设计工具
有关颜色的知识横跨好几个学科,比如物理学.生物学.心理学.艺术和设计.对于美术师们来说,颜色是创造感情非常有用的工具,对于游戏设计师们来说,它是强调功能最有用的方式,对于营销者们来说,可以用颜色对产品 ...
- Java集合框架中隐藏的设计套路
我们的世界不应该只有"胡萝卜" 进入正题之前容我先扯点别的. 最近突然想到了一个驴子和胡萝卜不得不说的故事.说是一个人坐在驴子背上,用一根长杆绑着一根胡萝卜,然后把胡萝卜悬到驴子的 ...
- 框架中的高大上设计为了啥?
源码中经常看到调用本类中的抽象方法,运行时才知道是哪个实现类.但是我们看源码时不知道是哪个实现类,需要debug进行查看到是哪个实现类!!!! 源码中为啥会用好多设计模式如:模板,委派,装饰者,责任链 ...
- Go实战--golang中使用echo框架中的HTTP/2、Server Push(labstack/echo、golang.org/x/net/http2)
生命不止,继续 go go go !!! 继续echo web框架,今天搞一下http2. HTTP2 What is HTTP/2? HTTP/2 is a replacement for how ...
- python中scrapy的middleware是干嘛的_Python之爬虫(十九) Scrapy框架中Download Middleware用法...
这篇文章中写了常用的下载中间件的用法和例子. Downloader Middleware处理的过程主要在调度器发送requests请求的时候以及网页将response结果返回给spiders的时候,所 ...
- OpenCV中waitKey()函数的深度解析
while (char(waitKey(1)) != 'q') {} 相信不少,都是冲着这句代码来的,是不是无法退出? 注意:鼠标必须激活当前窗口, 即鼠标要点一下窗口(图像),不然要是放在cmd窗口 ...
最新文章
- Eclipse编辑jsp、js文件时,经常出现卡死现象解决汇总
- Codeforces Gym 100187M M. Heaviside Function two pointer
- VS2019正确创建C++步骤以及扩展插件美化你的VS2019的IDE开发环境
- VUEJS-checkbox全选全不选
- 程序员成熟的标志《程序员成长路线图:从入门到优秀》
- pgsql中float4导致java程序精度丢失_Java基础系列02
- 09-03-06 FreeEIM 姗姗来迟
- 21南阳理工oj新生赛Round#5--这是一道二分题
- 【嵌入式Linux】嵌入式Linux驱动开发基础知识之第一个驱动
- 剑指offer之使数组的奇数在偶数前面
- 《Java并发编程的艺术》第3章 Java内存模型
- 什么是ERPS?ERPS有什么作用?
- 代码覆盖率分析 - VectorCAST/CBA
- springboot+vue线上教学平台(源码+文档)
- matlab汽车驱动力与行驶阻力,用matlab绘制汽车驱动力 行驶阻力平衡图
- 平台网络安全能力知多少
- knockout学习笔记
- android 授予root权限,关于android手机获得ROOT权限问题
- 快加入「我的最爱」吧 Python 开发者不容错过的30 个Github 开源专案(下)
- wd移动硬盘测试软件,让你的秘密高枕无忧 WD My Passport随行版移动硬盘评测
热门文章
- MobileNeXt:Rethinking Bottleneck Structure for Efficient Mobile Network Design
- 2022考研复试时间轴及注意事项!重要哟!
- 8位单片机(51 STC8)C语言处理32位unsigned long型数据之计算出错
- 大数据非万能 却是商业定海神针
- dotnet发布运行
- 中秋节快乐,给大家准备了用Python和c++的感谢与祝福代码!(有一些是借鉴的)
- 移动硬盘上的linux系统安装软件下载,UNetbootin Linux
- java 接收 xml_关于java后台如何接收xml格式的数据
- 父与子的编程游戏——滑雪者游戏
- Ajax与Axios的区别