全书目录

第三十九篇:Axum 完全指南 —— Rust 类型关口宇宙

第六章:Extractor 才是 Axum 的灵魂 —— 它是整座海关最重要的验货设备

1 分钟 215 字 第 752 / 962 个阅读单元

如果只用一句话概括 Axum,很多老手会直接说:

Axum 是以 Extractor 为中心的框架。

因为 handler 之所以能写得干净,就是靠 extractor 先把请求拆好了。

最常见的几种提取器:

rust
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>,
) {
    // ...
}

你可以把它们翻译成:

text
Extractor 验货设备
├── Path<T>     -> 从门牌号里读材料
├── Query<T>    -> 从随车附注里读材料
├── HeaderMap   -> 从请求头里读证件
├── State<T>    -> 从总关底册里取共享信息
└── Json<T>     -> 打开车厢,按 JSON 申报单验货

核心关系图如下:

text
请求进入
  │
  ▼
Extractor 依次执行
  │
  ├── 只读 request parts 的 -> FromRequestParts
  └── 会消耗 body 的       -> FromRequest
  ▼
材料齐全
  │
  ▼
Handler 开始办事

这里有一条必须死记的规则:

#提取器按参数从左到右执行,消耗请求体的提取器必须放最后

因为请求体本质上是流,只能消费一次。

所以这是对的:

rust
async fn create_permit(
    State(state): State<AppState>,
    Path(zone): Path<String>,
    Json(payload): Json<CreatePermit>,
) {
    // ...
}

这是错的:

rust
async fn create_permit(
    Json(payload): Json<CreatePermit>,
    State(state): State<AppState>,
) {
    // body 提取器不该放前面
}

你可以把它想成:

text
先看门牌和证件
再开箱验货

而不是:

text
先把整车货全拆了
再回头问“你是从哪道门进来的?”