起步
Iris 的中间件设计秉承了 handler,或者,你就可以说它是 handler。这也是我极为喜欢 Iris 的一个原因,竭尽全力降低使用者的记忆负担——又少记一个函数签名。
中间件粗略在 Iris - 入门食用指南 里说过了。还不够,因为使用中间件需要牢记两个点:1. 中间件的作用域; 2. 中间件的执行顺序。
私有中间件
Iris wiki 没有明确提出私有中间件这一概念(或许是我看得不够仔细),但明确了“全局”,所以把非全局称为私有并不为过。不清楚是不是我开了这个先河。
现在,我们回顾路由注册方法的函数签名。拿 Get 为示例:
func (api *APIBuilder) Get(relativePath string, handlers ...context.Handler) *Route
能够看到第二个参数 handlers 的类型是:...context.Handler
,表示不定长传参,再通俗一点说:允许传递零个、或者一个、或者多个类型为 context.Handler 的实参。
func before(ctx iris.Context) {
// 打印日志
log.Println("this a middleware before handler")
// 传递信息
ctx.Values().Set("signal", 512)
ctx.Next()
}
func after(ctx iris.Context) {
// 打印日志
log.Println("this a middleware after handler")
// 接受信息
signal := ctx.Values().Get("signal")
log.Printf("recive signal, value: %v\n", signal)
ctx.Next()
}
func indexHandler(ctx iris.Context) {
log.Println("handle something")
ctx.Next()
}
func main() {
app := iris.Default()
// 注册 before, after 中间件,注册 handler
app.Get("/", before, indexHandler, after)
app.Run(iris.Addr(":8080"))
}
before()
、after()
是作为中间件的存在,但是你也看到了,它们与indexHandler()
没有区别。- 同一个路由的多个 handler,一般借助
ctx.Values()
传递信息。 - 除最后一个 handler 外,其他 handler 结束都必须调用
ctx.Next()
。谨防遗忘,你可以每个 handler 都加上 ctx.Next()
最后请注意,上述方式注册的中间件,只对对应路由有效。
全局中间件
全局中间件,顾名思义,对所有路由都有效的中间件。
用法也极为简单:
func before (ctx iris.Context) {
...
ctx.Next()
}
func after (ctx iris.Context) {
...
ctx.Next()
}
func main() {
...
// 注册中间件
app.UseGlobal(before)
app.DoneGlobal(after)
...
}
UseGlobal
注册的中间件总是在 执行handler 之前执行。DoneGlobal
注册的中间件总是在 执行handler 之后执行。- 不能遗忘的 ctx.Next()
分组中间件
Iris - 入门食用指南 一节提到注册中间件用 app.Use()
,对应还有 app.Done()
。这一组方法是为分组路由注册中间件。如果你不知道分组路由是什么,可以参看我写过的一节内容:分组路由(Grouping Routes)。对于分组路由来说,它们没有 UseGlobal 方法,也没有 DoneGlobal 方法,于是 Use 和 Done 成为替代品。
由于根路由(app)也有 Use 和 Done 方法,因此存在以下代码排布方式:
app := iris.Default()
app.Use(...)
app.Done(...)
app.Get("/", ...) // 注册路由
app.UseGlobal(...)
app.DoneGlobal(...)
app.Run(iris.Addr(":8080"))
此时调用顺序为:UseGlobal 注册中间件 -> Use 注册中间件 -> 路由 handlers(包含私有中间件) -> Done 注册中间件 -> DoneGlobal 注册中间件。
注意: Use 和 Done 注册的中间件,只对在 Use 和 Done 之后注册的路由生效。
也就是说,以下注册方式不会导致程序异常,但访问 “/” 时,注册的中间件不会被执行:
app.Get("/", ...) // 注册路由
app.Use(...) // 不会被执行
app.Done(...) // 不会被执行
对 Use 和 Done 注册的中间件来说,并非总是需要调用 ctx.Next() 达到执行后面 handler 的效果。也可以配置后使其强制执行后面的 handler:
app.SetExecutionRules(iris.ExecutionRules{
Begin: iris.ExecutionOptions{Force: true},
Main: iris.ExecutionOptions{Force: true},
Done: iris.ExecutionOptions{Force: true},
})
- Begin 设置 true 以后,所有 Use 注册的中间都会被执行,所有 handlers 被执行,如果最后一个 handler 没有调用 ctx.Next(),执行链终止。
- Main 设置 true 以后,如果最后一个 handler 没有调用 ctx.Next(),依旧会执行第一个 Done 注册的中间件,如果该中间件没有调用 ctx.Next(),执行链终止。
- Done 设置 true 以后,如果第一个 Done 注册的中间件没有调用 ctx.Next(),还是会执行后面 Done 注册的中间件。
分不清就都设置为 true 吧。
最后强调一下,不论 UseGlobal、DoneGlobal 还是 Use、Done,都可以一次性注册多个中间件,因为它们接收的参数类型都是 ...context.Handler 。
还不快抢沙发