Iris中间件必须是func(ctx iris.Context)类型,显式调用ctx.Next()才能继续执行,中断用ctx.StopExecution();支持闭包封装配置(如JWT密钥、跳过路径);注册位置决定作用域(全局app.Use或分组api.Use);数据传递用ctx.Values(),耗时统计用ctx.Record()。

中间件函数签名必须返回 iris.Context
Iris 的中间件本质是函数,不是闭包或结构体方法。它接收一个 ctx iris.Context 参数,且**必须显式调用 ctx.Next()** 才会继续执行后续处理器,否则请求就停在那里了。
常见错误是写成类似 HTTP 中间件的 func(http.ResponseWriter, *http.Request) 形式,或者忘了调用 ctx.Next() 导致路由永远不响应。
- 正确签名:
func(ctx iris.Context) - 必须在逻辑合适位置调用
ctx.Next()(比如鉴权通过后、日志记录后) - 若想中断流程并返回,直接调用
ctx.StopExecution()或ctx.StatusCode(401)+ctx.JSON(...)即可,无需 return - 不要用
return代替ctx.StopExecution()—— Iris 不依赖返回值控制流程
如何实现带参数的自定义拦截器(如 JWT 验证中间件)
Go 没有“高阶中间件”语法糖,但可以用闭包封装配置。比如 JWT 密钥、白名单路径这些,不能硬编码在函数体内,得靠外层函数返回真正的中间件函数。
典型场景:只对 /api/ 下的路由校验 token,跳过 /api/login 和静态资源。
立即学习“go语言免费学习笔记(深入)”;
- 外层函数接收配置参数,返回
iris.Handler(即func(iris.Context)) - 闭包内捕获配置,避免每次请求都重复解析或初始化
- 示例中检查
ctx.Request().URL.Path是否在豁免列表,再解析Authorizationheader - 注意:token 解析失败时调用
ctx.StopExecution(),否则会继续往下走
func JWTAuth(secret string, skipPaths []string) iris.Handler {
return func(ctx iris.Context) {
path := ctx.Request().URL.Path
for _, p := range skipPaths {
if path == p || strings.HasPrefix(path, p+"/") {
ctx.Next()
return
}
}
<pre class="brush:php;toolbar:false;"> tokenStr := ctx.GetHeader("Authorization")
if tokenStr == "" {
ctx.StatusCode(401)
ctx.JSON(map[string]string{"error": "missing token"})
ctx.StopExecution()
return
}
// ... 解析 token,验证 signature & exp
if !valid {
ctx.StatusCode(401)
ctx.JSON(map[string]string{"error": "invalid token"})
ctx.StopExecution()
return
}
ctx.Next() // ✅ 放在最后,确保前面都通过
}}
全局注册 vs 路由组注册:影响作用域和执行顺序
中间件注册位置决定它是否生效、何时执行。Iris 不支持“中间件链自动合并”,顺序完全由注册方式决定。
-
app.Use(middlewareA, middlewareB):全局中间件,所有路由(包括静态文件)都会经过,按注册顺序执行 -
api := app.Party("/api")+api.Use(middlewareC):仅该分组下路由触发,且会在全局中间件之后、具体 handler 之前执行 - 多个
Use()调用会叠加,但同一组内多次Use()的顺序就是代码书写顺序 - 错误实践:在
Party内部又调用app.Use(...)—— 这会让中间件变成全局,容易误伤其他分组
ctx.Values() 传参和 ctx.Record() 日志埋点容易被忽略的细节
中间件之间传递数据,不能靠局部变量或全局 map,必须用 ctx.Values().Set();而调试时想看中间件耗时,ctx.Record() 是唯一可靠方式。
-
ctx.Values()是 request-scoped 的,生命周期与当前请求一致,安全并发 - 避免用
ctx.Set()—— 它只是给 context 做标记,不存结构化数据;要用ctx.Values().Set("user_id", 123),取用时ctx.Values().Get("user_id") -
ctx.Record()启用后,ctx.Application().Logger().Infof("cost: %v", ctx.GetRecord().Duration())才能拿到真实耗时,否则time.Since()在中间件里测不准(因为没覆盖完整生命周期) - 别在中间件里直接
panic,Iris 有内置 recover 中间件,但默认不开启;如需 panic 捕获,得手动加app.Use(recover.New())
真正难的不是写一个中间件,而是让多个中间件在不同分组下按预期顺序协作,同时不互相污染 ctx.Values() 或掩盖错误。参数校验、鉴权、日志这三类中间件一旦顺序错乱,问题往往延迟暴露,排查成本远高于编写成本。


















