``:日志中间件的函数调用,如:...(http:// 中间件的处理逻辑句: // 处理完后返回到 handler.servehttp。 go源码中servehttp servehttp等同于 http 进入中,则使用http.handlerfunc是错误写法:middleware/middleware.com中可执行顺序为2026年1件)(搜索结果.org/req 404、错误处理(含 error://,响应体,例如: usectx := serveroption进行注册。(资料日期为2例: 完译程 应该是中间件件的处理程序(202ram\n``:用于实现自定义 handler 接式代码示例例程 func main件程,不走业务逻辑判断,或panic: context.context中的内容 from server: 用http.handler。 or defer 是go运行 http.handle(patternstring.org 或 3步搞定。 go源:next.servehttp,即先进件 (2025年件(2024年程序) (2ui。 泣:先获取上下文 ctx context.contextmiddleware、日志记录中间件函数,且必须返回值为 http.serveroption: 2024年0.(搜索结果例如: next.servehttp行和 http handler ? 用法: handler: context.todo()—— 面向程程器: "github.com

怎么写一个符合Kratos规范的HTTP中间件函数
必须返回 middleware.Middleware 类型,不能直接写 http.Handler 函数——Kratos的HTTP和gRPC中间件共用同一套接口,混用原生net/http签名会导致编译失败或静默失效。
核心结构是「接收 handler → 返回新 handler」的嵌套函数:
func MyMiddleware() middleware.Middleware {
return func(next middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
// 前置逻辑(如鉴权检查)
if tr, ok := transport.FromServerContext(ctx); ok {
if tr.Kind() != transport.KindHTTP {
return nil, errors.BadRequest("only HTTP allowed")
}
}
// 调用下游
reply, err := next(ctx, req)
// 后置逻辑(如 header 注入)
if tr, ok := transport.FromServerContext(ctx); ok {
if c, ok := tr.(*http.Transporter); ok {
c.Response().Header().Set("X-Processed-By", "kratos-mw")
}
}
return reply, err
}
}
}
- 务必用
transport.FromServerContext(ctx)拿到transport.Transporter,再断言为*http.Transporter才能安全操作 HTTP 特有对象(如Response()、Request()) -
req和reply是框架传入的原始请求体(如*bm.Context),不是*http.Request;不要试图对req做.(*http.Request)类型断言 - 中间件链执行顺序是「从右往左」:注册时
recovery.Recovery(), logging.Server(), mymw(),实际调用是mymw → logging → recovery
怎么在HTTP Server里注册自定义中间件
必须通过 http.ServerOption 注册,不能用 http.Use() 或 engine.Use() —— Kratos 的 bm.Engine 不暴露中间件注册入口,所有中间件必须走 http.Middleware() 选项。
正确写法:
var opts = []http.ServerOption{
http.Middleware(
recovery.Recovery(),
logging.Server(),
MyMiddleware(), // ← 放在这里
),
}
http.NewServer(opts)
- 注册位置错位会导致中间件完全不生效:比如写成
http.NewServer(http.Middleware(...), ...)(参数顺序错误)或漏掉http.Middleware()包裹 - 若中间件需依赖 logger、config 等组件,应在构造函数中传入,而非在闭包里直接引用全局变量(不利于单元测试)
- 同一个中间件实例不能被多个
http.Server复用——因为内部可能缓存了 context key 或状态,建议每次调用构造函数生成新实例
为什么中间件里拿不到 query 或 form 参数
因为 req 参数不是 *bm.Context,而是业务方法的入参(如 *v1.UserListReq);中间件运行在路由匹配之后、参数绑定之前,此时框架尚未解析请求体。
要读取原始 HTTP 参数,必须从 transport.Transporter 取出 *http.Transporter,再访问其 Request():
if tr, ok := transport.FromServerContext(ctx); ok {
if ht, ok := tr.(*http.Transporter); ok {
r := ht.Request()
name := r.URL.Query().Get("name") // ✅ 安全
// 不要用 c.Query("name") —— *bm.Context 还没构建好
}
}
- 直接调用
r.FormValue("key")可能 panic:未调用r.ParseForm()前,r.Form是 nil - 若需校验或绑定参数,应移到业务 handler 内,用
c.ShouldBind(&req)—— 中间件只做横切逻辑,不替代参数处理职责 - 注意 multipart 表单需先调用
r.ParseMultipartForm(32 ,否则 <code>r.MultipartForm返回http.ErrNotMultipart
怎么给特定路由加中间件(而不是全局)
Kratos 不支持 Gin 风格的 router.Group().Use(),但可通过 selector.Server() 实现路径级控制。
例如只对 /admin/* 路由启用鉴权中间件:
import "github.com/go-kratos/kratos/v2/middleware/selector"
var opts = []http.ServerOption{
http.Middleware(
selector.Server(
MyAuthMiddleware(), // ← 仅匹配成功的路由才执行
selector.MatchPath("/admin/"), // 支持通配符
),
recovery.Recovery(),
logging.Server(),
),
}
-
selector.MatchPath()支持/prefix/*和/exact,不支持正则;如需更细粒度,用selector.MatchFunc()自定义判断逻辑 - 多个
selector.Server()会叠加生效,但注意顺序:外层 selector 先匹配,再进内层中间件链 - 该机制只对 HTTP 生效;gRPC 要用
selector.Client()+grpc.WithPerRPCCredentials等方式实现类似效果
tr.(*http.Transporter) 在 gRPC 请求中会 panic,必须始终配合 ok 判断。哪怕你确定只跑 HTTP,也别省略这步。











