在 Golang 中实现一个简单的Http中间件
本文主要针对Golang的内置库 net/http 做了简单的扩展,通过添加中间件的形式实现了管道(Pipeline)模式,这样的好处是各模块之间是低耦合的,符合单一职责原则,可以很灵活的通过中间件的形式添加一些功能到管道中,一次请求和响应在管道中的执行过程如下

首先, 我定义了三个测试的中间件 Middleware1,2,3 如下
func Middleware1(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("M1 in")
next.ServeHTTP(w, r)
fmt.Println("M1 out")
})
}
func Middleware2(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("M2 in")
next.ServeHTTP(w, r)
fmt.Println("M2 out")
})
}
func Middleware3(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("M3 in")
next.ServeHTTP(w, r)
fmt.Println("M3 out")
})
}
这里中间件的入参和出参的类型都是 http.Handler, 然后在 next.ServeHTTP() 的前后分别输出了 In 和 Out.
接下来,定义一个 Pipeline 的方法,里面使用嵌套的形式, 使用了上面定义的三个测试的中间件.
func Pipeline(next http.Handler) http.Handler {
return Middleware1(Middleware2(Middleware3(next)))
}
然后还需要业务代码,这里我定义了 LoginHandler 和 RegisterHandler 两个方法
func LoginHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Login...")
w.Write([]byte("Login..."))
}
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Register...")
w.Write([]byte("Register..."))
}
最后修改程序的 main 函数, 在 Login 接口上使用上面添加过中间件的 Pipeline
func main() {
http.Handle("/Login", Pipeline(http.HandlerFunc(LoginHandler)))
http.Handle("/Register", http.HandlerFunc(RegisterHandler))
http.ListenAndServe(":8080", nil)
}
启动程序后,访问 http://localhost:8080/Login, 程序的输出如下,这和本文最上面的管道的流程图是一致的,然后访问 Register 接口, 控制台没有输出信息,当然也不会执行任何中间件。

现在已经实现了中间件的机制,但是,上面添加中间件是用嵌套的方法,这种方式不能说不太优雅,只能说非常的Low,接下来我们需要对管道进行优化
type Chain struct {
middlewares []func(handler http.Handler) http.Handler
}
func Pipeline(next http.Handler) http.Handler {
//return Middleware1(Middleware2(Middleware3(next)))
return AddMiddlewares(Middleware1,Middleware2,Middleware3).Then(next)
}
func AddMiddlewares(m ...func(handlerFunc http.Handler) http.Handler) Chain {
c := Chain{}
c.middlewares = append(c.middlewares,m...)
return c
}
func (c Chain) Then(next http.Handler) http.Handler {
for i := range c.middlewares {
prev := c.middlewares[len(c.middlewares)-1-i]
next = prev(next)
}
return next
}
首先定义了一个Chain 的struct,用来接收添加到管道中的中间件,在 AddMiddlewares() 函数中,接收了多个Handle, 然后组装到 Chain 对象并返回, 接下来调用 Then() 函数, 把管道中的中间件和业务的Handler 关联起来。在中间件的使用方式上, 这两种方法都是一样的,只需要调用 Pipeline() 方法就行了。
本文在go web中简单的实现了中间件的机制,这样带来的好处也是显而易见的,当然社区也有一些成熟的 middleware 组件,包括 Gin 一些Web框架中也包含了 middleware 相关的功能, 希望对您有用.
最后欢迎扫码关注公众号 【全球技术精选】

在 Golang 中实现一个简单的Http中间件的更多相关文章
- (一)在HTML页面中实现一个简单的Tab
在HTML页面中实现一个简单的Tab 为了充分利用有限的HTML页面空间,经常会采用类似与TabControl的效果通过切换来显示更多的内容.本文将采用一种最为简单的方法来实现类似如Tab页切换的效果 ...
- golang gin框架中实现一个简单的不是特别精确的秒级限流器
起因 看了两篇关于golang中限流器的帖子: Gin 开发实践:如何实现限流中间件 常用限流策略--漏桶与令牌桶介绍 我照着用,居然没效果-- 时间有限没有深究.这实在是一个很简单的功能,我的需求是 ...
- 在iOS中实现一个简单的画板App
在这个随笔中,我们要为iPhone实现一个简单的画板App. 首先需要指出的是,这个demo中使用QuarzCore进行绘画,而不是OpenGL.这两个都可以实现类似的功能,区别是OpenGL更快,但 ...
- 在eclipse中配置一个简单的spring入门项目
spring是一个很优秀的基于Java的轻量级开源框架,为了解决企业级应用的复杂性而创建的,spring不仅可用于服务器端开发,从简单性.可测试性和松耦合性的角度,任何java应用程序都可以利用这个思 ...
- 如何在Liferay 7中创建一个简单的JSF Portlet
这个将在Liferay IDE 3.1 M3的发布版中提供创建的选项,但是你也可以通过命令行来创建. 1.这是Liferay JSF团队的官网:http://liferayfaces.org/ 你能在 ...
- 在 Visual Studio 中创建一个简单的 C# 控制台应用程序
转载:https://blog.csdn.net/qq_43994242/article/details/87260824 快速入门:使用 Visual Studio 创建第一个 C# 控制台应用 h ...
- wpf中,一个简单的自定义treeview
首先创建一个自定义控件,在里面定义好treeview的样式,将本来的三角形的图标变为加号的图标,并且添加节点之间的连线. <UserControl x:Class="TreeViewE ...
- 【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- Linux中实现一个简单的进度条【转】
转自:http://blog.csdn.net/yuehailin/article/details/53999288 说起进度条,其实大家常常见到,比如说你在下载视频或文件的时候,提示你当前下载进度的 ...
随机推荐
- Java面试必知必会(扩展)——Java基础
float f=3.4;是否正确? 不正确 3.4是双精度,将双精度赋值给浮点型属于向下转型,会造成精度损失: 因此需要强制类型转换: 方式一:float f=(float)3.4 方式二:float ...
- jsp页面抽取
步骤: 1.先将jsp中要抽取的公共部分剪切出来,黏贴到新的jsp文件中,取名叫xxx.jsp 2.在需要引入此公共部分的jsp页面中使用<%@include file="xxx.js ...
- 【NX二次开发】镜像对象
使用uf5946获取镜像矩阵注意:uf5946镜像这个函数,只能用#define UF_plane_type=46这种类型的数据作为镜像面,不能用#define UF_datum_plane_type ...
- 【VBA】获取文件夹下所有文本文件
源码: 1 Sub 获取文件夹下所有文本文件() 2 Dim strPath As String 3 strPath = "G:\A\" 4 Dim MyFile As Strin ...
- Spring Boot WebFlux-导读
背景 大家都知道,Spring Framework 是 Java/Spring 应用程序跨平台开发框架,也是 Java EE(Java Enterprise Edition) 轻量级框架,其 Spri ...
- 如何基于 String 实现同步锁?
如何基于String实现同步锁? 在某些时候,我们可能想基于字符串做一些事情,比如:针对同一用户的并发同步操作,使用锁字符串的方式实现比较合理. 因为只有在相同字符串的情况下,并发操作才是不被允许的. ...
- 解决SpringMVC重复提交的问题
方法一:通过重定向采取请求转发的方式完成表单内容的添加会造成内容的重复插入.当向Servlet发送一条增加记录的请求后,servlet首先向数据库增加一条记录,然后又从数据库中查询出所有数据,接着转发 ...
- NTLM协议与Pass the Hash的爱情
0x01.前言 NTLM使用在Windows NT和Windows 2000 Server或者之后的工作组环境中(Kerberos用在域模式下).在AD域环境中,如果需要认证Windows NT系统, ...
- 并发王者课-铂金8:峡谷幽会-看CyclicBarrier如何跨越重峦叠嶂
欢迎来到<并发王者课>,本文是该系列文章中的第21篇,铂金中的第8篇. 在上一篇文章中,我们介绍了CountDownLatch的用法.在协调多线程的开始和结束时,CountDownLatc ...
- css 背景图片铺满
body { width: 100%; height: 100%; background: url(img/loginbg.png); background-size: 100% 100%; back ...