Handler 注册策略

推荐方式:inventory + HandlerCache(HTTP 唯一路径)

HTTP 请求分发使用 **编译时 inventory 收集**,不通过 DI 查找 `dyn IRequestHandler`:

| 宏 | 收集内容 | |----|---------| | `#[get]` / `#[post]` 等 | `RouteEntry` — 路由表项 | | `#[handler]` / `#[handler(inject)]` | `HandlerRegistration` — Handler 工厂 | | endpoint 宏生成 | `RouteDispatch` — HTTP → Mediator 桥接 |

`HostBuilder::build()` 将三者关联;配置不一致时 **启动 panic**(orphan route/handler)。

三种 Handler 声明方式

| 方式 | 适用场景 | 依赖注入 | |------|---------|---------| | `#[handler]` | Handler 实现 `Default` | 无依赖 | | `#[handler(inject)]` + `#[derive(Inject)]` | Handler 有 DI 依赖 | rust-dix 自动注入 | | 手动 `singleton` 注册 | 非 HTTP Mediator 场景 | 手动控制 |

方式一:#[handler] 零配置

#[derive(Default)]
struct HelloHandler;

#[handler]
#[async_trait]
impl IRequestHandler<HelloRequest, String> for HelloHandler { ... }

要求: - Handler struct 实现 `Default` - 无构造函数参数

方式二:register_handlers!(已弃用,非 HTTP 主路径)

> **Deprecated:** HTTP 请使用 `#[handler]` / `#[handler(inject)]`。此宏仅保留给非 HTTP Mediator 或手动 DI 注册。

Host::builder()
    .register(|svc| {
        register_handlers!(svc, [HelloHandler, OtherHandler])
    })

此方式向 DI 注册 `dyn IRequestHandler<…>`,**HTTP RouteDispatch 不使用此查找**。保留用于显式 Mediator 调用或非 HTTP 场景。

方式三:#[derive(Inject)] + #[handler(inject)](推荐)

#[derive(Inject)]
struct BlogHandler {
    blog: Arc<dyn IBlogService>,
}

#[handler(inject)]
#[async_trait]
impl IRequestHandler<GetBlogRequest, BlogModel> for BlogHandler { ... }

- `#[derive(Inject)]` 生成 rust-dix 构造器 - `#[handler(inject)]` 标记走注入路径而非 `Default` - Handler 依赖通过 DI 解析,注册仍走 inventory

诊断

cargo run -p my-host -- --doctor

输出路由表、orphan 路由/Handler。build 阶段也会 fail-fast。

决策流程

flowchart TD
    A[新增 Handler] --> B{有 DI 依赖?}
    B -->|否| C["#[handler] + Default"]
    B -->|是| D["#[derive(Inject)] + #[handler(inject)]"]
    C --> E[确保 contracts 有对应路由宏]
    D --> E

简单 Handler 用 `#[handler]`;生产项目推荐 `#[derive(Inject)]` + `#[handler(inject)]`,保持 `main.rs` 简洁。