目录
什么是Axum提取器
一个处理函数(handler)是一个异步函数,它以任意数量的提取器(`extract`)作为参数。
提取器(extract)是实现了 FromRequest 或 FromRequestParts 的类型。
例如,Json是一个提取器,用于消耗请求主体并将其反序列化为某种目标类型:
use axum::{
extract::Json,
routing::post,
handler::Handler,
Router,
};
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
email: String,
password: String,
}
async fn create_user(Json(payload): Json<CreateUser>) {
// ...
}
let app = Router::new().route("/users", post(create_user));
常见提取器
一些常用的提取器包括:
use axum::{
extract::{Request, Json, Path, Extension, Query},
routing::post,
http::header::HeaderMap,
body::{Bytes, Body},
Router,
};
use serde_json::Value;
use std::collections::HashMap;
// `Path`提供了**路径参数**并对其进行反序列化。
async fn path(Path(user_id): Path<u32>) {}
// `Query`会提供**查询参数**并对其进行反序列化。
async fn query(Query(params): Query<HashMap<String, String>>) {}
// `HeaderMap`提供了所有标头信息。
async fn headers(headers: HeaderMap) {}
// `String`消耗**请求正文**并确保它是有效的utf-8
async fn string(body: String) {}
// `Bytes`提供**原始请求正文**。
async fn bytes(body: Bytes) {}
// 我们已经使用了`Json`来解析**请求体**作为json
async fn json(Json(payload): Json<Value>) {}
// `Request`提供了**整个请求**,以实现最大控制。
async fn request(request: Request) {}
// `Extension` 从**请求扩展**中提取数据
// 这通常**用于与处理程序共享状态**
async fn extension(Extension(state): Extension<State>) {}
#[derive(Clone)]
struct State { /* ... */ }
let app = Router::new()
.route("/path/:user_id", post(path))
.route("/query", post(query))
.route("/string", post(string))
.route("/bytes", post(bytes))
.route("/json", post(json))
.route("/request", post(request))
.route("/extension", post(extension));
应用多个提取器
您还可以应用多个提取器:
use axum::{
extract::{Path, Query},
routing::get,
Router,
};
use uuid::Uuid;
use serde::Deserialize;
let app = Router::new().route("/users/:id/things", get(get_user_things));
#[derive(Deserialize)]
struct Pagination {
page: usize,
per_page: usize,
}
impl Default for Pagination {
fn default() -> Self {
Self { page: 1, per_page: 30 }
}
}
async fn get_user_things(
Path(user_id): Path<Uuid>,
pagination: Option<Query<Pagination>>,
) {
let Query(pagination) = pagination.unwrap_or_default();
// ...
}
提取器的顺序
提取器始终按照函数参数的顺序运行,即从左到右。
请求体是一个只能消耗一次的异步流。 因此,您只能有一个消耗请求体的提取器。
Axum 通过要求这样的提取器作为处理程序接受的最后一个参数来强制执行这一点。
示例
use axum::{extract::State, http::{Method, HeaderMap}};
async fn handler(
// `Method` 和 `HeaderMap` **不会消耗请求主体**,因此它们可以放在参数列表中的任何位置(但在 `body` 之前)
method: Method,
headers: HeaderMap,
// `State`也是一种提取器,因此它需要放在`body`之前。
State(state): State<AppState>,
// `String`消耗请求主体,因此必须是最后一个提取器。
body: String,
) {
// ...
}
注意
如果"String"不是最后一个提取器,则我们会收到编译错误。
use axum::http::Method;
async fn handler(
// this doesn't work since `String` must be the last argument
body: String,
method: Method,
) {
// ...
}
注意
这也意味着您不能两次消耗请求正文。
use axum::Json;
use serde::Deserialize;
#[derive(Deserialize)]
struct Payload {}
async fn handler(
// `String` and `Json` both consume the request body
// so they cannot both be used
string_body: String,
json_body: Json<Payload>,
) {
// ...
}
axum通过要求最后一个提取器实现FromRequest以及其他所有提取器实现FromRequestParts来强制执行此规定。
可选的提取器
在 axum 中定义的所有提取器将在请求不匹配时拒绝。
如果您希望将提取器设置为可选,可以将其包装在 Option 中:
use axum::{
extract::Json,
routing::post,
Router,
};
use serde_json::Value;
async fn create_user(payload: Option<Json<Value>>) {
if let Some(payload) = payload {
// We got a valid JSON payload
} else {
// Payload wasn't valid JSON
}
}
let app = Router::new().route("/users", post(create_user));
将提取器(extractor)包装在 Result 中使其变成可选的,并提供提取失败的原因:
use axum::{
extract::{Json, rejection::JsonRejection},
routing::post,
Router,
};
use serde_json::Value;
async fn create_user(payload: Result<Json<Value>, JsonRejection>) {
match payload {
Ok(payload) => {
// 我们收到了一个有效的 JSON 负载
}
Err(JsonRejection::MissingJsonContentType(_)) => {
// 请求没有 `Content-Type: application/json` 头部
}
Err(JsonRejection::JsonDataError(_)) => {
// 无法将正文反序列化为目标类型
}
Err(JsonRejection::JsonSyntaxError(_)) => {
// 主体体内语法错误
}
Err(JsonRejection::BytesRejection(_)) => {
// 无法提取请求正文
}
Err(_) => {
// `JsonRejection` 标记为 `#[non_exhaustive]`,因此匹配模式必须包括一个捕获所有情况的情况。
}
}
}
let app = Router::new().route("/users", post(create_user));