前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习Golang的HTTP中间件机制

学习Golang的HTTP中间件机制

作者头像
LA0WAN9
发布2021-12-14 08:48:24
3350
发布2021-12-14 08:48:24
举报
文章被收录于专栏:火丁笔记

因为 Golang 内置的 net/http 天生就支持 HTTP 中间件机制,所以即便不用 gin 之类的 Web 框架,我们也可以写出扩展性很好的 Web 应用。

假如你不了解 Golang 的 HTTP 中间件机制的话,那么可以把它看成是一个洋葱:

洋葱
洋葱

通过洋葱看中间件

每一个中间件都是一层洋葱皮,其中每一个中间件都可以改变请求和响应,我们可以很自然的把不同的逻辑放到不同的洋葱皮里,更代码更符合单一职责原则:

代码语言:javascript
复制
package main

import (
	"net/http"
)

func foo(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("foo("))
		next(w, r)
		w.Write([]byte(")"))
	}
}

func bar(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("bar("))
		next(w, r)
		w.Write([]byte(")"))
	}
}

func test(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("test"))
}

func main() {
	http.Handle("/", foo(bar(test)))
	http.ListenAndServe(":8080", nil)
}

运行结果显示如下,它形象的说明了中间件的执行过程:

代码语言:javascript
复制
foo(bar(test))

联想一下洋葱的结构,基本就能明白 Golang 的 HTTP 中间件机制了,不过不爽的是不易维护,假如中间件很多的话,视觉上会呈现出复杂的嵌套,比如:

代码语言:javascript
复制
middleware(
    middleware(
        middleware(
            middleware(
                middleware(
                    handler
                )
            )
        )
    )
)

我可不想维护这样的代码,下面看看如何简化编码方式:

代码语言:javascript
复制
package main

import "net/http"

func foo(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("foo("))
		next.ServeHTTP(w, r)
		w.Write([]byte(")"))
	})
}

func bar(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("bar("))
		next.ServeHTTP(w, r)
		w.Write([]byte(")"))
	})
}

func test(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("test"))
}

type pipeline struct {
	middlewares []middleware
}

type middleware func(http.Handler) http.Handler

func newPipeline(ms ...middleware) pipeline {
	return pipeline{ms}
}

func (p pipeline) pipe(ms ...middleware) pipeline {
	return pipeline{append(p.middlewares, ms...)}
}

func (p pipeline) process(h http.Handler) http.Handler {
	for i := range p.middlewares {
		h = p.middlewares[len(p.middlewares)-1-i](h)
	}

	return h
}

func main() {
	http.Handle("/", newPipeline().pipe(foo, bar).process(http.HandlerFunc(test)))
	http.ListenAndServe(":8080", nil)
}

需要说明的是,开始的代码使用的是 http.HandlerFunc 的形式,后面的代码参考大家的意见改成了更通用的 http.Handler 接口的形式,下面对比一下调用方式:

  • 修改前:foo(bar(test))
  • 修改后:newPipeline().pipe(foo, bar).process(test)

虽然表面上看代码更长了,但是通过使用 pipeline,我们把原本嵌套的结构改成了链式的结构,这不仅提高了代码的可维护性,而且也提高了代码的可复用性,设想一下,你有很多路由,它们有很多公共的中间件,利用 Pipeline,很简单就可以完成复用,类似的开源项目有很多,比如:「Alice – Painless middleware chaining for Go」,我就不多说了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-01-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档