如果只用一句话概括 Axum,很多老手会直接说:
Axum 是以 Extractor 为中心的框架。
因为 handler 之所以能写得干净,就是靠 extractor 先把请求拆好了。
最常见的几种提取器:
use axum::{
extract::{Path, Query, State},
http::HeaderMap,
Json,
};
use serde::Deserialize;
#[derive(Deserialize)]
struct Pager {
page: usize,
per_page: usize,
}
async fn inspect(
Path(id): Path<u64>,
Query(pager): Query<Pager>,
headers: HeaderMap,
State(state): State<AppState>,
Json(payload): Json<CreatePermit>,
) {
// ...
}你可以把它们翻译成:
Extractor 验货设备
├── Path<T> -> 从门牌号里读材料
├── Query<T> -> 从随车附注里读材料
├── HeaderMap -> 从请求头里读证件
├── State<T> -> 从总关底册里取共享信息
└── Json<T> -> 打开车厢,按 JSON 申报单验货核心关系图如下:
请求进入
│
▼
Extractor 依次执行
│
├── 只读 request parts 的 -> FromRequestParts
└── 会消耗 body 的 -> FromRequest
▼
材料齐全
│
▼
Handler 开始办事这里有一条必须死记的规则:
#提取器按参数从左到右执行,消耗请求体的提取器必须放最后
因为请求体本质上是流,只能消费一次。
所以这是对的:
async fn create_permit(
State(state): State<AppState>,
Path(zone): Path<String>,
Json(payload): Json<CreatePermit>,
) {
// ...
}这是错的:
async fn create_permit(
Json(payload): Json<CreatePermit>,
State(state): State<AppState>,
) {
// body 提取器不该放前面
}你可以把它想成:
先看门牌和证件
再开箱验货而不是:
先把整车货全拆了
再回头问“你是从哪道门进来的?”