处理函数标注与声明
函数标注为服务器端处理函数专用,用于指定处理函数的HTTP方法和路径。当前支持六种,分别为:
http_get:GET方法http_post:POST方法http_put:PUT方法http_delete:DELETE方法http_head:HEAD方法http_options:OPTIONS方法
这六种除了描述处理方法外,其他特性完全一致。通过修改标注名即可实现不同的HTTP方法。
标注用于指定处理函数的HTTP方法和路径,直接传递路径即可。示例:
#[potato::http_get("/hello")]
async fn hello() -> HttpResponse {
HttpResponse::html("hello world")
}
如需用户会话管理,请使用 SessionCache(详见 09_cache.md)。
参数
参数可以直接接受请求对象,也可以定义自定义请求参数,这请求参数将要求HTTP请求的query string或者body附带此值。示例请求对象:
#[potato::http_get("/hello")]
async fn hello(req: &mut HttpRequest) -> HttpResponse {
HttpResponse::html("hello world")
}
#[potato::http_get("/hello")]
async fn hello2(req: &mut HttpRequest) -> anyhow::Result<HttpResponse> {
let addr = req.get_client_addr().await?;
Ok(HttpResponse::html(format!("hello client: {addr:?}")))
}
下面是一个websocket服务器端示例代码:
#[potato::http_get("/ws")]
async fn websocket(req: &mut HttpRequest) -> anyhow::Result<()> {
let mut ws = req.upgrade_websocket().await?;
ws.send_ping().await?;
loop {
match ws.recv().await? {
WsFrame::Text(text) => ws.send_text(&text).await?,
WsFrame::Binary(bin) => ws.send_binary(bin).await?,
}
}
}
另外就是处理函数的参数了。除了前文提到的鉴权用的参数外,剩余的均要求通过请求的query里或body里附带。示例:
#[potato::http_get("/hello_user")]
async fn hello_user(name: String) -> HttpResponse {
HttpResponse::html(format!("hello {name}"))
}
#[potato::http_post("/upload")]
async fn upload(file1: PostFile) -> HttpResponse {
HttpResponse::html(format!("file[{}] len: {}", file1.filename, file1.data.len()))
}
返回类型
处理函数支持多种返回类型:
HttpResponse- 直接返回 HTTP 响应anyhow::Result<HttpResponse>- 返回可能出错的 HTTP 响应()- 无返回值,自动响应 "ok"Result<()>- 返回可能出错的操作String/&'static str- 返回字符串,自动通过HttpResponse::html()包装anyhow::Result<String>/anyhow::Result<&'static str>- 返回可能出错的字符串
示例:
// 返回 String
#[potato::http_get("/string")]
async fn string_handler() -> String {
"<h1>Hello</h1>".to_string()
}
// 返回 &'static str
#[potato::http_get("/static")]
async fn static_handler() -> &'static str {
"<h1>Static</h1>"
}
// 返回 Result<String>
#[potato::http_get("/result")]
async fn result_handler(success: bool) -> anyhow::Result<String> {
if success {
Ok("<h1>Success</h1>".to_string())
} else {
anyhow::bail!("Error")
}
}
响应头标注
可通过 #[header(...)] 为处理函数添加响应头,支持标准头和自定义头:
// 标准头(使用下划线命名)
#[potato::http_get("/api")]
#[header(Cache_Control = "no-cache")]
async fn api_handler() -> HttpResponse {
HttpResponse::text("response")
}
// 自定义头(使用 Custom 语法)
#[potato::http_get("/custom")]
#[header(Custom("X-Custom-Header") = "custom-value")]
async fn custom_header() -> String {
"custom header".to_string()
}
// 多个header混合使用
#[potato::http_get("/multi")]
#[header(Cache_Control = "no-store")]
#[header(Custom("X-Custom") = "value")]
async fn multi_headers() -> String {
"multiple headers".to_string()
}
错误处理
通过 #[potato::handle_error] 定义全局错误处理函数,统一处理所有handler中的异常。
#[potato::handle_error]
async fn handle_error(req: &mut HttpRequest, err: anyhow::Error) -> HttpResponse {
HttpResponse::text(format!("Error: {}", err))
}
- 支持
async fn和普通fn - 参数固定为
(req: &mut HttpRequest, err: anyhow::Error) - 返回值必须为
HttpResponse - 未定义时使用默认处理器(返回500状态码和错误详情)
预处理与后处理
可以在处理函数上叠加 preprocess、postprocess 标注,用于在处理函数前后执行固定签名的钩子函数。
preprocess 与 postprocess 都支持 async fn 或普通 fn。
预处理函数签名固定为:
#[potato::preprocess]
fn pre(req: &mut potato::HttpRequest) -> ...
可选返回类型:anyhow::Result<Option<potato::HttpResponse>>、Option<potato::HttpResponse>、anyhow::Result<()>、()
后处理函数签名固定为:
#[potato::postprocess]
fn post(req: &mut potato::HttpRequest, res: &mut potato::HttpResponse) -> ...
可选返回类型:anyhow::Result<()>、()
示例:
#[potato::preprocess]
async fn pre1(req: &mut potato::HttpRequest) -> anyhow::Result<Option<potato::HttpResponse>> {
Ok(None)
}
#[potato::postprocess]
async fn post1(req: &mut potato::HttpRequest, res: &mut potato::HttpResponse) -> anyhow::Result<()> {
Ok(())
}
#[potato::http_get("/hello")]
#[potato::preprocess(pre1)]
#[potato::postprocess(post1)]
#[potato::postprocess(post2)]
async fn hello() -> potato::HttpResponse {
potato::HttpResponse::html("hello world")
}
同一个 handler 上可重复标注多行,例如:
#[potato::preprocess(pre1)]
#[potato::preprocess(pre2, pre3)]
#[potato::postprocess(post1)]
#[potato::postprocess(post2)]
preprocess按声明顺序执行;若某个预处理返回HttpResponse,会跳过实际 handler,但仍会继续执行postprocess。postprocess按声明顺序执行,接收最终HttpResponse并可原地修改。preprocess/postprocess支持拆分多行声明,顺序按"从左到右、从上到下"执行。
CORS标注
通过 #[potato::cors(...)] 为处理函数启用CORS(跨域资源共享)支持。该标注会自动添加CORS响应头,并为PUT/POST/DELETE方法自动生成HEAD方法支持。
基本用法
// 最简用法:使用最小限制默认值
// - Access-Control-Allow-Origin: *
// - Access-Control-Allow-Headers: *
// - Access-Control-Allow-Methods: 自动计算
// - Access-Control-Max-Age: 86400
#[potato::http_get("/api/data")]
#[potato::cors]
async fn get_data() -> &'static str {
"data"
}
自定义配置
// 限制来源和方法
#[potato::http_post("/api/create")]
#[potato::cors(
origin = "https://example.com",
methods = "GET,POST,PUT,DELETE",
headers = "Content-Type,Authorization",
max_age = "3600"
)]
async fn create_item() -> &'static str {
"created"
}
// 允许携带凭证(cookies)
#[potato::http_put("/api/update")]
#[potato::cors(
origin = "https://secure.example.com",
methods = "GET,PUT",
credentials = true
)]
async fn update_item() -> &'static str {
"updated"
}
参数说明
origin: 允许的来源域名,默认*(允许所有)methods: 允许的方法列表,默认自动计算(扫描所有已注册方法)headers: 允许的请求头,默认*(允许所有)max_age: 预检请求缓存时间(秒),默认86400(24小时)credentials: 是否允许携带凭证(cookies),默认falseexpose_headers: 允许浏览器访问的响应头
智能特性
- 自动HEAD支持:PUT/POST/DELETE方法标注cors后,自动生成对应的HEAD方法处理
- 自动OPTIONS处理:CORS预检请求自动返回正确的允许方法列表
- 方法补充:指定的methods会自动补充HEAD和OPTIONS
- 凭证模式约束:当
credentials=true时,origin不能使用*,必须指定具体域名
请求体大小限制标注
通过 #[potato::limit_size(...)] 为处理函数设置请求体大小限制,返回 413 Payload Too Large。
基本用法
// 限制 body 为 10MB
#[potato::http_post("/upload")]
#[potato::limit_size(10 * 1024 * 1024)]
async fn upload(req: &mut HttpRequest) -> HttpResponse {
HttpResponse::text("uploaded")
}
分别限制 header 和 body
// Header 512KB, Body 50MB
#[potato::http_post("/large-upload")]
#[potato::limit_size(header = 512 * 1024, body = 50 * 1024 * 1024)]
async fn large_upload(req: &mut HttpRequest) -> HttpResponse {
HttpResponse::text("large uploaded")
}
优先级
- Handler 注解 > 中间件
use_limit_size> 全局配置(默认 100MB) - 注解仅对当前 handler 生效,覆盖全局和中间件限制
传输速率限制
通过 use_transfer_limit 中间件限制连接的数据传输速率(单位:bits/sec)。
支持情况:
- ✅ HTTP/1.1:完全支持,基于 TCP 流的令牌桶算法实现
- ❌ HTTP/2:不支持(使用 h2 框架,未实现速率限制)
- ❌ HTTP/3:不支持(使用 QUIC/h3 框架,未实现速率限制)
server.configure(|ctx| {
// 入站 10 Mbps,出站 20 Mbps
ctx.use_transfer_limit(10_000_000, 20_000_000);
ctx.use_handlers();
});
- 入站速率:限制接收请求数据的速度
- 出站速率:限制发送响应数据的速度
- 超出限制时自动延迟传输
- 仅对 HTTP/1.1 连接生效