在使用 Go 编写复杂的服务时,您将遇到一个典型的主题是中间件。这个话题在网上被讨论了一次又一次。本质上,中间件允许我们做了如下事情:
ServeHTTP
这些与 express.js 中间件所做的工作非常类似。我们探索了各种库,找到了接近我们想要的现有解决方案,但是他们要么有不要的额外内容,要么不符合我们的品位。显然,我们可以在 express.js 中间件的启发下,写出 20 行代码以下的更清晰的易用的 api(Installation API)
在设计抽象时,我们首先设想如何编写中间件函数(下文开始称为拦截器),答案非常明显:
func NewElapsedTimeInterceptor() MiddlewareInterceptor {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
startTime := time.Now()
defer func() {
endTime := time.Now()
elapsed := endTime.Sub(startTime)
// 记录时间消耗
}()
next(w, r)
}
}
func NewRequestIdInterceptor() MiddlewareInterceptor {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
if r.Headers.Get("X-Request-Id") == "" {
r.Headers.Set("X-Request-Id", generateRequestId())
}
next(w, r)
}
}
它们看起来就像 http.HandlerFunc ,但有一个额外的参数 next ,该函数(参数)会继续处理请求链。这将允许任何人像编写类似 http.HandlerFunc 的简单函数一样写拦截器,它可以拦截调用,执行所需操作,并在需要时传递控制权。
接下来,我们设想如何将这些拦截器连接到 http.Handler 或 http.HandlerFunc 中。为此,首先要定义 MiddlewareHandlerFunc ,它只是 http.HandlerFunc 的一种类型。(type MiddlewareHandlerFunc http.HandlerFunc)。这将允许我们在 http.HandlerFunc 栈上之上构建一个更好的 API。现在给定一个 http.HandlerFunc 我们希望我们的链式 API 看起来像这样:
func HomeRouter(w http.ResponseWriter, r *http.Request) {
// 处理请求
}
// ...
// 在程序某处注册 Hanlder
chain := MiddlewareHandlerFunc(HomeRouter).
Intercept(NewElapsedTimeInterceptor()).
Intercept(NewRequestIdInterceptor())
// 像普通般注册 HttpHandler
mux.Path("/home").HandlerFunc(http.HandlerFunc(chain))
将 http.HandlerFunc 传递到 MiddlewareHandlerFunc ,然后调用 Intercept 方法注册我们的Interceptor 。 Interceptor 的返回类型还是 MiddlewareHandlerFunc ,它允许我们再次调用 Intercept 。
使用 Intercept 组合需要注意的一件重要事情是执行的顺序。由于 chain(responseWriter, request)是间接调用最后一个拦截器,拦截器的执行是反向的,即它从尾部的拦截器一直返回到头部的处理程序。这很有道理,因为你在拦截调用时,拦截器应该要在真正的请求处理器之前执行。
虽然这种反向链系统使抽象更加流畅,但事实证明,大多数情况下 s 我们有一个预编译的拦截器数组,能够在不同的 handlers 之间重用。同样,当我们将中间件链定义为数组时,我们自然更愿意以它们执行顺序声明它们(而不是相反的顺序)。让我们将这个数组拦截器称为中间件链。我们希望我们的中间件链看起来有点像:
// 调用链或中间件可以按下标的顺序执行
middlewareChain := MiddlewareChain{
NewRequestIdInterceptor(),
NewElapsedTimeInterceptor(),
}
// 调用所有以 HomeRouter 结尾的中间件
mux.Path("/home").Handler(middlewareChain.Handler(HomeRouter))
一旦我们设计好抽象的概念,实现就显得简单多了
package middleware
import "net/http"
// MiddlewareInterceptor intercepts an HTTP handler invocation, it is passed both response writer and request
// which after interception can be passed onto the handler function.
type MiddlewareInterceptor func(http.ResponseWriter, *http.Request, http.HandlerFunc)
// MiddlewareHandlerFunc builds on top of http.HandlerFunc, and exposes API to intercept with MiddlewareInterceptor.
// This allows building complex long chains without complicated struct manipulation
type MiddlewareHandlerFunc http.HandlerFunc
// Intercept returns back a continuation that will call install middleware to intercept
// the continuation call.
func (cont MiddlewareHandlerFunc) Intercept(mw MiddlewareInterceptor) MiddlewareHandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
mw(writer, request, http.HandlerFunc(cont))
}
}
// MiddlewareChain is a collection of interceptors that will be invoked in there index order
type MiddlewareChain []MiddlewareInterceptor
// Handler allows hooking multiple middleware in single call.
func (chain MiddlewareChain) Handler(handler http.HandlerFunc) http.Handler {
curr := MiddlewareHandlerFunc(handler)
for i := len(chain) - 1; i >= 0; i-- {
mw := chain[i]
curr = curr.Intercept(mw)
}
return http.HandlerFunc(curr)
}
因此,在不到 20 行代码(不包括注释)的情况下,我们就能够构建一个很好的中间件库。它几乎是简简单单的,但是这几行连贯的抽象实在是太棒了。它使我们能够毫不费力地编写一些漂亮的中间件链。希望这几行代码也能激发您的中间件体验。
原文 https://studygolang.com/articles/25913
如果让你列出过去十年出现的重要编程语言,几乎可以肯定Go和Rust榜上有名。同样,如果让你坐下来想想如今哪种编程语言最适合开发对微服务友好且安全的框架或应用程序,你可能发觉在Go和Rust之间难以决定。
新学习go语言的人可能遇到常见的错误,其中有两个比较常见的错误,需要单独拿出来说下,为什么要单独说呢,因为这两个错误跟其他语言不同,是因为go本身的设计造成的。
Golang 的 1.13 版本 与 1.14 版本对 defer 进行了两次优化,使得 defer 的性能开销在大部分场景下都得到大幅降低,其中到底经历了什么原理?这是因为这两个版本对 defer 各加入了一项新的机制,使得 defer 语句在编译时
在用 Go 编程时,总会遇到各种奇奇怪怪的错误,国内外已经有许多小伙伴总结过(参考链接见参考),感觉都能凑一桌了。希望对大家有所帮助。
本文将介绍展示如何使用 Go 编程语言 来复制文件。在 Go 中复制文件的方法有很多,我只介绍三种最常见的:使用 Go 库中的 io.Copy() 函数调用、一次读取输入文件并将其写入另一个文件
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!