发布 axum 0.7.0
2023年11月27日
今天,我们很高兴地宣布 axum
0.7 版本。axum
是一个符合人体工程学且模块化的 Web 框架,它基于 tokio
、tower
和 hyper
构建。
这也包括 axum-core
、axum-extra
和 axum-macros
的新主要版本。
hyper
1.0 支持
axum
0.7 的主要特性是支持 hyper
1.0。hyper
是 Rust 中许多网络生态系统的基础库,最终拥有稳定的 API 是一个重要的里程碑。
hyper
保证在未来三年内不会再进行任何重大更改,这意味着周围的生态系统也将变得更加稳定。
hyper
1.0 带来了 API 的重大调整。之前的低级 API(在 hyper::server::conn
中找到)已稳定,而高级 API(例如 hyper::Server
)已被移除。
计划是将高级 API 添加到一个名为 hyper-util
的新 crate 中。在那里,我们可以构建 API,而无需过多担心稳定性保证和向后兼容性。当某些东西准备好稳定时,它可以被移入 hyper
。
hyper-util
仍处于开发的早期阶段,并且某些东西(例如之前的 Server
类型)仍然缺失。
由于 hyper-util
不稳定,我们不希望它成为 axum
公共 API 的一部分。如果您想使用 hyper-util
中的某些内容,则必须直接依赖它。
如果您将 axum
与 tower-http
一起使用,请注意,由于两者都公开依赖于 http
crate(它也发布了 1.0 版本),因此您需要同时升级 tower-http
(到 v0.5+)。
一个新的 axum::serve
函数
axum
0.6 提供了 axum::Server
,这是一种快速入门的简便方法。然而,axum::Server
只是 hyper::Server
的重新导出,它已从 hyper
1.0 中移除。
hyper-util
中还没有完整的替代品,因此 axum
现在提供了自己的 serve
函数
use axum::{
routing::get,
Router,
};
use tokio::net::TcpListener;
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
axum::serve
的目的是提供一种快速开始使用 axum
的方法,因此它不支持任何配置选项。如果您需要配置,则必须直接使用 hyper
和 hyper-util
。我们提供了一个示例,展示了如何 在此处 进行操作。
我们自己的 Body
类型
http-body
crate 现在也达到了 1.0 版本,并且它也像 hyper
和 hyper-util
一样进行了类似的 API 拆分。http-body
现在只提供核心 API,而高级实用程序已移至 http-body-util
。这包括 Full
、Empty
和 UnsyncBoxBody
等,它们以前由 axum
重新导出。
出于与 hyper-util
不应成为 axum
公共 API 一部分相同的原因,http-body-util
也不应该成为。
作为替代方案,axum
现在提供了自己的 body 类型,位于 axum::body::Body
。
它的 API 类似于 hyper::Body
的 API
use axum::body::Body;
use futures::TryStreamExt;
use http_body_util::BodyExt;
// Create an empty body (Body::default() does the same)
Body::empty();
// Create bodies from strings or buffers
Body::from("foobar");
Body::from(Vec::<u8>::from([1, 3, 3, 7]));
// Wrap another type that implements `http_body::Body`
Body::new(another_http_body);
// Convert a `Body` into a `Stream` of data frames
let mut stream = body.into_data_stream();
while let Some(chunk) = stream.try_next().await? {
// ...
}
// Collect the body into a `Bytes`. Uses `BodyExt::collect`
// This replaces the previous `hyper::body::to_bytes` function
let bytes = body.collect().await?.to_bytes();
更少的泛型
axum::Router
过去是泛型化的请求 body 类型。这意味着应用更改请求 body 类型的中间件会对您的路由产生连锁反应
// This would work just fine
Router::new()
.route(
"/",
get(|body: Request<Body>| async { ... })
);
// But adding `tower_http::limit::RequestBodyLimitLayer` would make
// things no longer compile
Router::new()
.route(
"/",
get(|body: Request<Body>| async { ... })
)
.layer(tower_http::limit::RequestBodyLimitLayer::new(1024));
它不起作用的原因是 RequestBodyLimitLayer
更改了请求 body 类型,因此您必须提取 Request<http_body::Limited<Body>>
而不是 Request<Body>
。这非常微妙,并且是造成一些困惑的根源。
在 axum 0.7 中,一切都像以前一样继续工作,无论您添加哪个中间件
Router::new()
.route(
"/",
// You always extract `Request<Body>` no matter
// which middleware you add
//
// This works because `Router` internally converts
// the body into an `axum::body::Body`, which internally
// holds a trait object
get(|body: Request<Body>| async { ... })
)
.layer(tower_http::limit::RequestBodyLimitLayer::new(1024));
还有一个方便的 http::Request
类型别名,它使用 axum 的 Body
类型
use axum::extract::Request;
Router::new().route("/", get(|body: Request| async { ... }));
请求 body 类型参数已从 axum
的所有类型和 trait 中移除,包括 FromRequest
、MethodRouter
、Next
等。
有关更多信息,请参阅更新日志
我鼓励您阅读更新日志,以查看所有更改以及有关如何从 0.6 升级到 0.7 的提示。
此外,如果您在更新时遇到问题,请发起 GitHub 讨论。也欢迎您在 Discord 中提问。