发布 Axum
2021 年 7 月 30 日
今天我们很高兴宣布 axum
:一个易于使用但功能强大的 Web 框架,旨在充分利用 Tokio 生态系统。
高级特性
- 使用无宏 API 将请求路由到处理程序。
- 使用提取器声明式地解析请求。
- 简单且可预测的错误处理模型。
- 以最少的样板代码生成响应。
- 充分利用
tower
和tower-http
中间件、服务和实用程序生态系统。
特别是最后一点使 axum
从现有框架中脱颖而出。axum
没有自己的中间件系统,而是使用 tower::Service
。这意味着 axum
可以免费获得超时、跟踪、压缩、授权等功能。它还使您能够与使用 hyper
或 tonic
编写的应用程序共享中间件。
使用示例
axum
的 “hello world” 示例如下
use axum::prelude::*;
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
let app = route("/", get(root));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
hyper::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn root() -> &'static str {
"Hello, World!"
}
这将使用 200 OK
响应来响应 GET /
请求,其中响应正文为 Hello, World!
。任何其他请求都将导致 404 Not Found
响应。
提取器
可以使用“提取器”声明式地解析请求。提取器是一种实现 FromRequest
的类型。提取器可以用作处理程序的参数,并且将在请求 URI 匹配时运行。
例如,Json
是一个提取器,它消耗请求正文并将其解析为 JSON
use axum::{prelude::*, extract::Json};
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
username: String,
}
async fn create_user(Json(payload): Json<CreateUser>) {
// `payload` is a `CreateUser`
}
let app = route("/users", post(create_user));
axum
附带了许多有用的提取器,例如
Bytes
、String
、Body
和BodyStream
,用于消耗请求正文。Method
、HeaderMap
和Uri
,用于获取请求的特定部分。Form
、Query
、UrlParams
和UrlParamsMap
,用于更高级别的请求解析。- [
Extension
],用于在处理程序之间共享状态。 - 如果您想要完全控制,可以使用
Request<hyper::Body>
。 - 使用
Result<T, E>
和Option<T>
使提取器成为可选的。
您还可以通过实现 FromRequest
来定义自己的提取器。
构建响应
处理程序可以返回任何实现了 IntoResponse
的内容,它将自动转换为响应
use http::StatusCode;
use axum::response::{Html, Json};
use serde_json::{json, Value};
// We've already seen returning &'static str
async fn text() -> &'static str {
"Hello, World!"
}
// String works too
async fn string() -> String {
"Hello, World!".to_string()
}
// Returning a tuple of `StatusCode` and another `IntoResponse` will
// change the status code
async fn not_found() -> (StatusCode, &'static str) {
(StatusCode::NOT_FOUND, "not found")
}
// `Html` gives a content-type of `text/html`
async fn html() -> Html<&'static str> {
Html("<h1>Hello, World!</h1>")
}
// `Json` gives a content-type of `application/json` and works with any type
// that implements `serde::Serialize`
async fn json() -> Json<Value> {
Json(json!({ "data": 42 }))
}
这意味着在实践中,您很少需要构建自己的 Response
。您还可以实现 IntoResponse
以创建您自己的特定于域的响应。
路由
可以使用简单的 DSL 组合多个路由
use axum::prelude::*;
let app = route("/", get(root))
.route("/users", get(list_users).post(create_user))
.route("/users/:id", get(show_user).delete(delete_user));
中间件
axum
支持来自 tower
和 tower-http
的中间件
use axum::prelude::*;
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
use tower::ServiceBuilder;
use std::time::Duration;
let middleware_stack = ServiceBuilder::new()
// timeout all requests after 10 seconds
.timeout(Duration::from_secs(10))
// add high level tracing of requests and responses
.layer(TraceLayer::new_for_http())
// compression responses
.layer(CompressionLayer::new())
// convert the `ServiceBuilder` into a `tower::Layer`
.into_inner();
let app = route("/", get(|| async { "Hello, World!" }))
// wrap our application in the middleware stack
.layer(middleware_stack);
此功能至关重要,因为它允许我们编写一次中间件并在应用程序之间共享它们。例如,axum
不必提供自己的跟踪/日志记录中间件,可以直接使用来自 tower-http
的 TraceLayer
。相同的中间件也可以用于使用 tonic
构建的客户端或服务器。
路由到任何 tower::Service
axum
也可以将请求路由到任何 tower
叶子服务。可以是您使用 service_fn
编写的服务,也可以是来自另一个 crate 的服务,例如来自 tower-http
的 ServeFile
use axum::{service, prelude::*};
use http::Response;
use std::convert::Infallible;
use tower::{service_fn, BoxError};
use tower_http::services::ServeFile;
let app = route(
// Any request to `/` goes to a some `Service`
"/",
service::any(service_fn(|_: Request<Body>| async {
let res = Response::new(Body::from("Hi from `GET /`"));
Ok::<_, Infallible>(res)
}))
).route(
// GET `/static/Cargo.toml` goes to a service from tower-http
"/static/Cargo.toml",
service::get(ServeFile::new("Cargo.toml"))
);
了解更多
这只是 axum
提供功能的一小部分示例。错误处理、Web 套接字和解析 multipart/form-data
请求是一些此处未展示的功能。请参阅 文档 了解更多详情。
我们还鼓励您查看 repo 中的示例,以了解一些使用 axum
编写的稍大型的应用程序。
与往常一样,如果您有任何问题,可以在 Tokio Discord 服务器中找到我们。