中间件#
中间件#
Huma 支持两种中间件变体:
- 路由器特定 - 在路由器级别工作,即在路由器无关中间件之前。您可以使用为您的路由器实现的任何中间件。
- 路由器无关 - 在 Huma 处理链中运行,即在调用路由器特定中间件之后。
graph LR
Request([Request])
RouterSpecificMiddleware[Router-Specific Middleware]
HumaMiddleware[Huma Middleware]
OperationHandler[Operation Handler]
Request --> RouterSpecificMiddleware
RouterSpecificMiddleware --> HumaMiddleware
subgraph Huma
HumaMiddleware --> OperationHandler
end
路由器特定#
每个路由器实现都有自己的中间件,您可以在创建 Huma API 实例之前像往常一样使用它们。
Chi 路由器示例:
router := chi.NewMux()
router.Use(jwtauth.Verifier(tokenAuth))
api := humachi.New(router, huma.DefaultConfig("My API", "1.0.0"))
Fiber 路由器示例:
app := fiber.New()
app.Use(logger.New())
api := humafiber.New(app, huma.DefaultConfig("My API", "1.0.0"))
Huma v1
Huma v1 中间件与 Chi v4 兼容,因此如果您使用该路由器与 Huma v2,您可以继续使用 Huma v1 中间件。请参阅 humachi.NewV4。
路由器无关#
您可以编写自己的 Huma 中间件,而无需依赖特定的路由器实现。这使用路由器无关的 huma.Context 接口,该接口将请求和响应属性暴露给您的中间件。
示例:
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// 在响应上设置自定义标头。
ctx.SetHeader("My-Custom-Header", "Hello, world!")
// 调用链中的下一个中间件。这最终也会调用操作处理程序。
next(ctx)
}
func NewHumaAPI() huma.API {
// ...
api := humachi.New(router, config)
api.UseMiddleware(MyMiddleware)
// 在 UseMiddleware() 之后注册处理程序,以使中间件生效
huma.Get(api, "/greeting/{name}", handler.GreetingGetHandler)
}
解包装#
虽然通常不推荐,但如果您需要访问底层路由器特定的请求和响应对象,您可以使用创建 API 实例时使用的路由器特定适配器包来 Unwrap() 它们(例如,对于 Chi 使用 humachi.Unwrap(),对于 Go 的 http 包使用 humago.Unwrap()):
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// 解包装请求和响应对象。
r, w := humago.Unwrap(ctx)
// 对请求和响应对象执行某些操作。
otherMiddleware(func (_ http.Handler) {
// 注意,这假设请求/响应是在原地修改的。
next(ctx)
}).ServeHTTP(w, r)
}
这在将大型现有项目迁移到 Huma 时可能很有用,因为您可以通过在 huma.Operation.Middleware 字段上对单个操作应用路由器特定中间件来实现路由器无关中间件。
上下文值#
huma.Context 接口提供 Context() 方法来检索底层请求 context.Context 值。这可用于在中间件和操作处理程序中检索上下文值,例如请求范围的日志记录器、指标或用户信息。
您还可以包装 huma.Context 以提供额外的或覆盖功能。提供了一些实用工具,包括 huma.WithValue:
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// 包装上下文以添加一个值。
ctx = huma.WithValue(ctx, "some-key", "some-value")
// 调用链中的下一个中间件。这最终也会调用操作处理程序。
next(ctx)
}
然后您可以在处理程序上下文中获取该值:
huma.Get(api, "/greeting/{name}", func(ctx context.Context, input *struct{
Name string `path:"name" maxLength:"30" example:"world" doc:"Name to greet"`
}) (*GreetingOutput, error) {
// "some-value"
ctx.Value("some-key")
resp := &GreetingOutput{}
resp.Body.Message = fmt.Sprintf("Hello, %s!", input.Name)
return resp, nil
})
Cookie#
您可以使用 huma.Context 接口以及 huma.ReadCookie 或 huma.ReadCookies 从中间件中访问 Cookie,并且也可以通过在响应中添加 Set-Cookie 标头来写入 Cookie:
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// 按名称读取 Cookie。
sessionCookie := huma.ReadCookie(ctx, "session")
fmt.Println(sessionCookie)
// 从请求中读取所有 Cookie。
cookies := huma.ReadCookies(ctx)
fmt.Println(cookies)
// 在响应中设置 Cookie。使用 `ctx.AppendHeader` 不会覆盖任何现有标头,例如如果其他中间件也可能设置标头,或者如果此代码在 `next` 调用之后移动并且操作可能设置相同的标头。您还可以多次调用 `ctx.AppendHeader` 来写入多个 Cookie。
cookie := http.Cookie{
Name: "session",
Value: "123",
}
ctx.AppendHeader("Set-Cookie", cookie.String())
// 调用链中的下一个中间件。这最终也会调用操作处理程序。
next(ctx)
}
错误#
如果您的中间件遇到错误,您可以跳过对 next 的调用并写入错误响应,从而停止后续中间件或操作处理程序的处理。
huma.WriteErr(api, ctx, status, message, ...error) 函数可用于写入符合客户端驱动的内容协商的结构化错误响应:
func MyMiddleware(ctx huma.Context, next func(ctx huma.Context)) {
// 如果查询参数 "error=true",则返回错误
if ctx.Query("error") == "true" {
huma.WriteErr(api, ctx, http.StatusInternalServerError,
"Some friendly message", fmt.Errorf("error detail"),
)
return
}
// 否则,继续正常处理。
next(ctx)
})
Error Details
huma.ErrorDetail 结构体可用于提供有关错误的更多信息,例如错误的位置和看到的错误值。
操作#
您还可以通过设置 huma.Operation.Middlewares 字段为单个操作添加路由器无关中间件。此中间件将在路由器特定中间件之后和操作处理程序之前运行。
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// 调用链中的下一个中间件。这最终也会调用操作处理程序。
next(ctx)
}
func main() {
// ...
api := humachi.New(router, config)
huma.Register(api, huma.Operation{
OperationID: "demo",
Method: http.MethodGet,
Path: "/demo",
Middlewares: huma.Middlewares{MyMiddleware},
}, func(ctx context.Context, input *MyInput) (*MyOutput, error) {
// TODO: implement handler...
return nil, nil
})
}
全局中间件也可以通过在中间件中检查请求上下文的 URL 或使用类似 huma.Operation.Metadata 的事物来仅为某些路径运行,以使用自定义设置触发中间件逻辑。如何结构化您的中间件和操作由您决定。
深入了解#
- 参考
huma.Context路由器无关的请求/响应上下文huma.Middlewares中间件列表huma.ReadCookie从请求中读取命名 Cookiehuma.ReadCookies从请求中读取 Cookiehuma.WriteErr写入错误响应的函数huma.APIAPI 实例