中间件没生效几乎总是因注册位置错、上下文未透传或Handler类型不匹配:必须用http.WithMiddleware()在http.NewServer()时注册;自定义中间件需手动透传context.WithValue()至r.Context();禁用transport.HTTPContext();且不可混用Gin/原生中间件。

中间件没生效,几乎总是因为注册位置错、上下文没透传、或 HTTP Handler 类型不匹配——Kratos 不会报错,但请求直接绕过你的逻辑。
中间件注册顺序不对:HTTP 路由前没挂上
Kratos 的 http.Server 是封装层,中间件必须在 http.NewServer() 时通过 http.WithMiddleware() 显式传入,不能靠 http.HandleFunc() 或全局 http.DefaultServeMux 注册。
- 错误写法:
http.HandleFunc("/api", myMiddleware(http.HandlerFunc(handler)))—— 这走的是原生 net/http,Kratos 的 transport、trace、context 都失效 - 正确写法:在
internal/server/http.go中,http.NewServer()的 opts 里加http.WithMiddleware(myMiddleware) - 多个中间件注意顺序:先执行的 middleware 拿到的是原始
*http.Request,后执行的才能看到前一个注入的 ctx.Value
自定义中间件里取不到 ctx.Value:没手动透传 context
Kratos 的中间件接收的是标准 *http.Request,它的 r.Context() 和你在 handler 里用 transport.HTTPRequestContext(r) 拿到的不是同一个对象——你往里面塞的值不会自动继承。
- 必须显式调用:
r = r.WithContext(context.WithValue(r.Context(), key, val)),再传给next.ServeHTTP(w, r) - 别用
transport.HTTPContext():它返回的是空 context,塞任何值都无效 - 如果用了 Kratos 官方中间件(如
logging.Server),它们内部已处理透传;但你写的自定义中间件,这一步绝不能省
中间件类型不匹配:传了原生 http.Handler 给 Kratos Server
Kratos 的 http.Server 要求中间件签名是 func(http.Handler) http.Handler,且最终 Handler 必须能被 transport.HTTPRequestContext() 正确解析。混用原生中间件极易失败。
- 错误现象:日志里完全不打印中间件日志,
curl直接返回 200,你的中间件像不存在 - 检查点:确认中间件函数没有返回
http.HandlerFunc(...)包裹的匿名函数,而是直接返回闭包函数体 - 典型正确定义:
func MyAuthMiddleware() http.Middleware { return func(next http.Handler) http.Handler { ... } } - 别把 Gin/echo 的中间件直接搬过来——它们的 Handler 签名和 context 封装机制完全不同
最常被忽略的是:中间件里修改了 r.URL.Path 或 r.Header 后,没重新赋值回 r 就调 next.ServeHTTP,导致后续 handler 看到的还是原始请求。Kratos 不做深拷贝,所有 request 修改都得显式写回去。











