Skip to content

框架适配器

每个适配器都会为每个请求创建恰好一个请求作用域,将其暴露在框架原生的位置,并在框架安全的完成时机释放它 —— 同时保留应用所拥有的具体容器类型,因此 request.di 是完全带类型的,而不是 any 或某个基类容器。

这就是适配器的全部职责。适配器只是轻薄的生命周期胶水代码:让 @inferdi/inferdi 保持零依赖的那套设计,同样把装饰器、控制器扫描、处理器参数注入和路由发现挡在内核之外。你选择接入的是框架的请求生命周期,而不是框架对依赖注入的理解。

软件包

软件包框架作用域位置仅根模式
@inferdi/fastifyFastify v5request.di支持
@inferdi/honoHono v4c.var.di不支持
@inferdi/koaKoa v3ctx.state.di不支持
@inferdi/expressExpress 5req.di不支持
@inferdi/elysiaElysia v1context.di支持

通用生命周期契约

在作用域模式下,每个适配器对每个请求都会执行相同的步骤:

  1. 在请求开始时,从根容器创建作用域(createScope,默认为 root.createScope())。
  2. 在 setup 运行之前,将其暴露在框架原生的位置(request.dictx.state.dic.var.di 或 Elysia 上下文键),这样 setup 失败以及你的清理钩子都能观察到同一个槽位。
  3. setupScope 配置作用域,以填充由请求派生的状态 —— 请求 id、已认证用户、客户端 IP。它可以是异步的。
  4. 处理请求:路由处理器和框架的错误处理器从暴露的作用域解析服务。
  5. 在框架安全的完成时机释放作用域(disposeScope,默认为 scope.dispose()),除非所有权已被转移。

共享选项

选项默认值用途
container必填暴露给应用的根容器。适配器从不释放它(Fastify 的可选 disposeRootOnClose 除外)。
createScoperoot.createScope()构建每个请求的作用域。可以是异步的。
setupScope在处理器运行前填充作用域。可以是异步的。
disposeScopescope.dispose()自定义清理。可以是同步或异步的。
autoDisposetruefalse,或返回 false 的谓词,将释放交给你的代码处理。
onDisposeError各适配器各自的接收端接收请求作用域的释放失败:Fastify 用 request.log.error,Koa 用 ctx.app.emit('error'),其余用 console.error
skipInferdiDispose(...)将某个请求标记为由应用拥有,用于流式或后台工作。

错误与所有权规则

  • setup 失败只暴露原始错误。 如果 setupScope 抛出,适配器会释放尚未构建完成的作用域并重新抛出该错误。此次清理过程中发生的清理失败会交给 onDisposeError(或接收端),绝不会被聚合进所暴露的错误中。
  • 失败的请求仍然会释放。 skipInferdiDispose 仅在响应成功时抑制清理;错误路径无论如何都会释放。Express 是例外 —— 它的回调式中间件无法观察到一个已被处理的路由错误,因此一个被跳过且失败的 Express 请求仍归应用所有。
  • autoDispose: falseskipInferdiDispose 会转移所有权。 此后由你的代码负责在正确的框架边界处释放作用域。
  • 响应产生之后发生的清理错误会被路由到接收端并吞掉。 此时响应已经发送,因此一个迟到的清理失败绝不会破坏它。

重要差异

适配器差异
FastifyonResponse 中释放;中止清理使用 onRequestAbort;可通过 disposeRootOnClose 选择启用根容器释放。
Honoawait next() 之后释放;流式辅助函数可能在流工作完成之前就返回,因此流式路由通常需要 skipInferdiDispose
Koa等待 Node 响应的 finishclose,因此普通的流式响应体不需要跳过。
Express无法从回调式中间件检测到一个已被处理的下游路由错误;一个被跳过且失败的请求仍归应用所有。
Elysia清理绑定到 onAfterResponse;如果该钩子从未触发,适配器就无法释放作用域所持有的资源。