中间件函数签名必须为func(ctx iris.context),否则编译失败或请求卡住;需显式调用ctx.next()继续流程,用ctx.stopexecution()终止;耗时统计须手动time.now(),不可依赖ctx.getduration()。

中间件函数签名必须是 iris.Handler
Iris 的中间件本质是一个接收 context.Context 参数的函数,返回类型必须是 iris.Handler(即 func(ctx iris.Context))。写成其他签名(比如漏掉 ctx 参数、返回 error 或不调用 ctx.Next())会导致编译失败或请求卡住。
常见错误现象:cannot use xxx (type func() error) as type iris.Handler;或者请求一直 pending,没响应。
- 必须以
func(ctx iris.Context)开头,不能省略参数名或类型 - 若需提前终止流程(如鉴权失败),调用
ctx.StopExecution(),而非return - 想继续执行后续处理器(包括路由处理函数),必须显式调用
ctx.Next()
耗时统计中间件要手动记录 time.Time 并写入响应头
Iris 不提供自动请求开始时间戳,得在中间件开头调用 time.Now(),结尾算差值。直接用 ctx.GetDuration() 是无效的——那个方法只读取上下文里已存的 key,不是内置计时器。
示例实现:
func RequestTimeMiddleware() iris.Handler {
return func(ctx iris.Context) {
start := time.Now()
ctx.Next() // 继续执行后续
duration := time.Since(start)
ctx.Header("X-Response-Time", duration.String())
}
}
- 别在
ctx.Next()前写响应(如ctx.JSON()),否则后续逻辑不会执行 - 如果想记录到日志,建议用
ctx.Application().Logger().Infof(),避免并发写 stdout - 注意
duration.String()输出带单位(如123.456ms),若需纳秒级数值做监控,用duration.Nanoseconds()
注册中间件时注意作用域:全局、路由组、单路由
中间件生效范围取决于你把它挂在哪里。写错位置会导致“明明写了中间件却没生效”。
- 全局生效:
app.Use(RequestTimeMiddleware())—— 所有路由都走 - 某组路由生效:
admin := app.Party("/admin"); admin.Use(RequestTimeMiddleware()) - 仅某个路由生效:
app.Get("/health", RequestTimeMiddleware(), healthHandler)
容易踩的坑:把中间件函数名写成 RequestTimeMiddleware(不加括号)—— 这会传入函数本身,而非调用后返回的 iris.Handler,导致 panic:cannot use RequestTimeMiddleware (type func() iris.Handler) as type iris.Handler。
高并发下避免字符串拼接和频繁日志 I/O
耗时中间件看似简单,但在 QPS 上千时,duration.String() 和 ctx.Header() 调用会触发内存分配;如果还加了 fmt.Printf 或 log.Println,可能成为瓶颈。
- 生产环境建议关闭非必要响应头(如只在调试阶段开
X-Response-Time) - 日志输出用结构化方式,例如:
ctx.Application().Logger().Infow("request", "path", ctx.Path(), "duration_ms", duration.Milliseconds()) - 如果要做采样统计(比如只记录 >100ms 的请求),在
ctx.Next()后加条件判断,避免无意义计算
真正难的不是写出来,而是想清楚它会在每个请求路径上被调多少次、是否影响连接复用、有没有隐式锁竞争——这些在本地跑通不代表线上不出问题。











