Iris - 中间件

Iris 2020-01-31 3531 字 1196 浏览 点赞

起步

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

参考



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论