axum简介
目录
- 高级功能
- 兼容性
- Hello World
- 路由
- 处理程序
- 提取器
- 响应
- 错误处理
- 中间件
- 与处理程序共享状态
- Axum集成
- 必需依赖
- 示例
- 功能标志
高级功能
- 使用无宏(
macro-free
)的API路由(router
)请求(request
)到处理程序(handler
) - 使用提取器(
extractor
)声明式解析请求 - 简单且可预测的错误处理模型
- 生成局域最少样板的响应
- 充分利用
tower
和tower-http
生态系统的中间件、服务和使用程序
特别是,最后一点是区分axum与其它框架的地方。 axum没有自己的中间件系统,而是使用tower::Service
。
这意味着axum获得超时、追踪、压缩、授权等功能,而且是免费的。
它还能让您与使用hyper或tonic编写的应用程序共享中间件。
兼容性
axum旨在与tokio和hyper协同工作。至少目前来看,并不追求运行时和传输层独立性。
Hello World
use axum::{
routing::get,
Router,
};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello World!" }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
:::note使用#[tokio::main]
需要您启用tokio的宏和rt-multi-thread
功能, 或者只需启用所有功能(cargo add tokio --features macro, rt-multi-thread
) :::
路由
路由器用于设置哪些路径指向那些服务。
use axum::{Router, routing::get};
let app = Router::new
.route("/", get(root))
.route("/foo", get(get_foo).post(post_foo))
.route("/foo/bar", get(foo_bar));
async fn root() {}
async fn get_foo() {}
async fn post_foo() {}
async fn foo_bar() {}
处理程序
在axum中,处理程序是一个异步函数,它接受零个或多个提取器作为参数,并返回可以转换为响应的东西。处理程序是您的应用程序逻辑所在的地方,并且axum应用程序 是通过在处理程序之间进行路由而构建的。
提取器
提取器是实现了FromRequest
和FromRequestsParts
接口的类型。提取器是您拆解传入请求以获取处理程序所需部分的方式。
use axum::extract::{Path, Query, Json};
use std::collections::HashMap;
// `Path`会提供路径参数并对其进行反序列化
async fn path(Path(user_id): Path<u32>) {}
// `Query`为您提供查询参数并将其反序列化
async fn query(Query(params): Query<HashMap<String, String>>) {}
// 将请求正文缓冲并将其反序列化为JSON到`serde_json::Value`
// `Json`支持任何实现`serde::Deserialize`的类型
async fn json(Json(payload): Json<serde_json:Value>) {}
响应
从处理程序返回的可以实现IntoResponse
接口的任何内容
use axum::{
body::Body,
routing::get,
response::Json,
Router,
};
use serde_json::{Value, json};
async fn plain_text() -> &'static str {
"foo"
}
async fn json() -> Json<Value> {
Json(json!({ "data": 42 }))
}
let app = Router::new()
.route("/plain_text", get(plain_text))
.route("/json", get(json));
错误处理
axum旨在拥有一个简单且可预测的错误处理模型。这意味着将错误转换为响应变得简单,并且可以保证所有错误都得到处理。
参见error_handling
处理程序之间共享状态
处理程序之间共享状态是很常见的。例如,可能需要共享数据库连接池或其他服务的客户端。实现这一点的三种常见方式是:
- 使用
State
提取器 - 使用请求扩展
- 使用闭包捕获
- 使用
State
提取器
use axum::{
extract::State,
routing::get,
Router,
};
use std::sync::Arc;
struct AppState {
//...
}
let shared_state = Arc::new(AppState { /* ... */ });
let app = Router.new()
.route("/", get(handler))
.with_state(shared_state);
async fn hander(
State(state): State<Arc<AppState>>,
) {
// ...
}
如果可能的话,您应该更倾向于使用State
,因为它更具有类型安全性。不足之处是,它比请求扩展更少的动态性。
- 使用请求扩展
在处理程序中提取状态的另一种方式是使用Extension
作为中间层和提取器。
use axum::{
extract::Extension,
routing::get,
Router,
};
use std::sync::Arc;
struct AppState {
// ...
}
let shared_state = Arc::new(AppState { /* ... */ });
let app = Router::new()
.route("/", get(handler))
.layer(Extension(shared_state));
async fn hander(
Extension(state): Extension<Arc<AppState>>
) {
// ...
}
这种方法的缺点是,如果你尝试提取一个不存在的扩展,你将会得到运行时错误(500内部服务器错误响应)
- 使用闭包捕获
状态也可以通过闭包捕获直接传递给处理程序。
use axum::{
Json,
extract::{Extension, Path},
routing::{get, post},
Router,
};
use std::sync::Arc;
use serde::Deserialize;
struct AppState {
// ...
}
let shared_state = Arc::new(AppState { /* ... */ });
let app = Router::new()
.route(
"/users",
post({
let shared_state = Arc::clone(&shared_state);
move |body| create_user(body, shared_state)
}),
)
.route(
"/users/:id",
get({
let shared_state = Arc::clone(&shared_state);
move |path| get_user(path, shared_state)
}),
);
async fn get_user(Path(user_id): Path<String>, state: Arc<AppState>) {
// ...
}
async fn create_user(Json(payload): Json<CreateUserPayload>, state: Arc<AppState>) {
// ...
}
#[derive(Deserialize)]
struct CreateUserPayload {
// ...
}
这种方法的缺点在于,它比使用State或Extensions要冗长一些。
为axum构建集成
系统提供FromRequest
, FromRequestParts
和IntoResponse
实现的库作者应该依赖于axum-core
包,而不是axum。 axum-core
包含核心类型和特性,并且不太可能出现破坏性变化。
必需依赖
要使用axum,你还需要引入一些依赖:
[dependencies]
axum = "<latest-version>"
tokio = { version = "<latest-version>", features = ["full"] }
tower = "<latest-version>"
为了开始使用,完整功能对于 tokio
并不是必需的,但却是最简单的方法。 Tower 也不是绝对必要的,但在测试时很有帮助。请参考存储库中的测试示例,了解有关测试 axum 应用程序的更多信息。
特性标志
axum使用一组功能标志来减少编译和可选依赖项的数量
以下可选项特性可用:
名称 | 描述 | 是否默认 |
---|---|---|
http1 | 启用hyper的http1特性 | 是 |
http2 | 启用hyper的http2特性 | 否 |
json | 启用Json类型和一些类似的便利功能 | 是 |
macro | 启动可选工具宏 | 否 |
matched-path | 启用了对每个请求的路由路径进行捕获,并使用MatchedPath 提取器 | 是 |
multipart | 启用Multipart解析multipart/form-data 请求 | 否 |
original-uri | 启用捕获每个请求的原始URI和OriginalUri提取器 | 是 |
tokio | 启用tokio作为依赖和axum::serve ,SSE和extract::connect_info 类型 | 是 |
tower-log | 启用tower的日志特性 | 是 |
tracing | 从内置提取器记录日志 | 是 |
ws | 通过extract::ws 启用Websocket支持 | 否 |
form | 启用表单提取器 | 是 |
query | 启用查询提取器 | 是 |
模块
body
: HTTP请求体工具error_handling
: 错误处理模型和工具extract
: 从请求为类型和特型提取数据handler
: 可以用来处理请求的异步函数middleware
: 写中间件的工具response
: 生成响应的类型和特型routing
: 在Service
和处理之间的路由serve
: 提供服务
结构体
Error
: 在使用axum时可能发生的错误Extension
: 提取器和扩展响应Form
: URL编码的提取器和响应。Json
: JSON提取器 / 响应。Router
: 用于组合处理程序和服务的路由器类型。
Traits(特型)
RequestExt
: 扩展特性,为Request
添加额外方法RequestPartExt
: 扩展特性,为Parts
添加额外的方法ServiceExt
: 想任何服务添加附加方法的扩展特性
函数
serve
: tokio和(http1或http2),使用提供的监听器提供服务
属性宏
debug_handler
: 宏在应用处理函数时生成更好的错误消息。