自定义 Endpoint
IEndpoint 接口
#[async_trait]
pub trait IEndpoint: Send + Sync {
async fn handle(&self, ctx: &mut dyn IHttpContext) -> Result<()>;
}
内置实现
| 类型 | 用途 | |------|------| | `RequestEndpoint` | 标准 IRequest → Handler 调度 | | `ControllerEndpoint` | 自定义函数式 endpoint(接受闭包) | | `StaticJsonEndpoint` | 返回固定 JSON | | `StaticHtmlEndpoint` | 返回固定 HTML | | `StubEndpoint` | 由路由宏在编译期注册,绑定请求并调用 Handler |
自定义 Endpoint 示例
pub struct WebhookEndpoint {
secret: String,
}
#[async_trait]
impl IEndpoint for WebhookEndpoint {
async fn handle(&self, ctx: &mut dyn IHttpContext) -> Result<()> {
let signature = ctx.request().header("X-Signature").unwrap_or("");
if !verify_signature(signature, &self.secret) {
ctx.response_mut().set_status(401);
return Ok(());
}
// 处理 webhook payload
ctx.response_mut().set_status(200);
Ok(())
}
}
注册自定义 Endpoint
`Host::build()` 会在内部构建路由表,`HostBuilder` 目前**没有**公开往路由器里追加 `IEndpoint` 的接口(`Host::router` 是私有字段)。因此自定义 `IEndpoint` 主要作为 框架内部扩展点,而不是应用层 API。
应用层需要「完全掌控响应」时,用下面两种方式之一,二者都是框架的一等能力:
1. **返回 `ResponseData`**(推荐):路由仍然是普通的 `#[get]` / `#[post]` 端点, Handler 通过 `ResponseData` 决定状态码、响应头与响应体。 见[文件上传与下载](../05-request-pattern/file-upload-download.md)。
#[get("/api/files/{id}/download")]
impl IRequest<ResponseData> for DownloadFileRequest {}
2. **写中间件**:`IMiddleware` 拿得到 `&mut dyn IHttpContext`,可以在路由之前 短路请求、直接写响应体,例如 `/robots.txt`、`/sitemap.xml` 这类非 API 路径。
何时使用
| 场景 | 推荐 | |------|------| | 标准 REST API | `RequestEndpoint`(默认,无需自定义) | | 文件下载 / 自定义状态码与响应头 | `ResponseData` | | Webhook 验签 | 自定义 `IEndpoint`(框架内扩展)或中间件 | | 固定响应(健康检查) | `StaticJsonEndpoint` | | 服务端渲染 HTML | `StaticHtmlEndpoint` 或中间件 |
小结
大多数场景使用默认 `RequestEndpoint` 即可;需要掌控响应时优先返回 `ResponseData`, 需要在路由之前拦截时写中间件。
下一节:[样式与约定封装](style-patterns.md)