目录
Axum的response模块
什么是response模块
用于生成响应的类型和特型(Trait)。
构建响应
任何实现 IntoResponse
接口的东西都可以从处理程序中返回。
axum 为常见类型提供了实现:
use axum::{
Json,
response::{Html, IntoResponse},
http::{StatusCode, Uri, header::{self, HeaderMap, HeaderName}},
};
// `()` 提供一个空响应
async fn empty() {}
// String将获得`text/plain; charset=utf-8`内容类型
async fn plain_text(uri: Uri) -> String {
format!("Hi from {}", uri.path())
}
// Bytes 将获得 `application/octet-stream` 内容类型
async fn bytes() -> Vec<u8> {
vec![1, 2, 3, 4]
}
// `Json` 将获得 `application/json` 内容类型,并可以与实现 `serde::Serialize` 的任何东西一起使用
async fn json() -> Json<Vec<String>> {
Json(vec!["foo".to_owned(), "bar".to_owned()])
}
// `Html` 将获得 `text/html` 内容类型
async fn html() -> Html<&'static str> {
Html("<p>Hello, World!</p>")
}
// `StatusCode` 会根据状态码提供一个空的响应。
async fn status() -> StatusCode {
StatusCode::NOT_FOUND
}
// `HeaderMap` 会返回带有一些标头的空响应
async fn headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(header::SERVER, "axum".parse().unwrap());
headers
}
// 一组元组也提供了标头
async fn array_headers() -> [(HeaderName, &'static str); 2] {
[
(header::SERVER, "axum"),
(header::CONTENT_TYPE, "text/plain")
]
}
// 使用 `impl IntoResponse` 可以避免编写整个类型
async fn impl_trait() -> impl IntoResponse {
[
(header::SERVER, "axum"),
(header::CONTENT_TYPE, "text/plain")
]
}
另外,您还可以返回元组,从个别部分构建更复杂的响应。
use axum::{
Json,
response::IntoResponse,
http::{StatusCode, HeaderMap, Uri, header},
extract::Extension,
};
// `(StatusCode, impl IntoResponse)` 将覆盖响应的状态码
async fn with_status(uri: Uri) -> (StatusCode, String) {
(StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path()))
}
// 使用`impl IntoResponse`可以避免输入整个类型
async fn impl_trait(uri: Uri) -> impl IntoResponse {
(StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path()))
}
// `(HeaderMap, impl IntoResponse)` 用于添加额外的标头
async fn with_headers() -> impl IntoResponse {
let mut headers = HeaderMap::new();
headers.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap());
(headers, "foo")
}
// 或者一个元组数组,更容易构建头信息
async fn with_array_headers() -> impl IntoResponse {
([(header::CONTENT_TYPE, "text/plain")], "foo")
}
// 使用字符串键来自定义标头
async fn with_array_headers_custom() -> impl IntoResponse {
([("x-custom", "custom")], "foo")
}
// `(StatusCode, headers, impl IntoResponse)` 用于设置状态码并添加标头
// `headers` 可以是 `HeaderMap` 或元组数组
async fn with_status_and_array_headers() -> impl IntoResponse {
(
StatusCode::NOT_FOUND,
[(header::CONTENT_TYPE, "text/plain")],
"foo",
)
}
// 用 `(Extension<_>, impl IntoResponse)` 设置响应扩展
async fn with_status_extensions() -> impl IntoResponse {
(
Extension(Foo("foo")),
"foo",
)
}
#[derive(Clone)]
struct Foo(&'static str);
// Or mix and match all the things
async fn all_the_things(uri: Uri) -> impl IntoResponse {
let mut header_map = HeaderMap::new();
if uri.path() == "/" {
header_map.insert(header::SERVER, "axum".parse().unwrap());
}
(
// 设置状态码
StatusCode::NOT_FOUND,
// 具有数组的标头
[("x-custom", "custom")],
// 一些扩展
Extension(Foo("foo")),
Extension(Foo("bar")),
// 动态构建更多标头
header_map,
// 最后是正文内容
"foo",
)
}
通常可以像这样返回元组:
(StatusCode, impl IntoResponse)
(Parts, impl IntoResponse)
(Response<()>, impl IntoResponse)
(T1, .., Tn, impl IntoResponse)
: 其中T1
到Tn
均实现了IntoResponseParts
接口。(StatusCode, T1, .., Tn, impl IntoResponse)
: 其中T1
到Tn
都实现了IntoResponseParts
接口。(Parts, T1, .., Tn, impl IntoResponse)
: 其中T1
到Tn
均实现了IntoResponseParts
接口。(Response<()>, T1, .., Tn, impl IntoResponse)
: 其中T1
至Tn
均实现IntoResponseParts
接口。
这意味着您不能意外地覆盖状态或正文,因为 IntoResponseParts
仅允许设置标头和扩展。
使用Response
以获得更低级别的控制:
use axum::{
Json,
response::{IntoResponse, Response},
body::Body,
http::StatusCode,
};
async fn response() -> Response {
Response::builder()
.status(StatusCode::NOT_FOUND)
.header("x-foo", "custom header")
.body(Body::from("not found"))
.unwrap()
}
返回不同的响应类型
如果需要返回多个响应类型,并且 Result<T, E>
不合适,
您可以调用 .into_response()
将事物转换为 axum::response::Response
:
use axum::{
response::{IntoResponse, Redirect, Response},
http::StatusCode,
};
async fn handle() -> Response {
if something() {
"All good!".into_response()
} else if something_else() {
(
StatusCode::INTERNAL_SERVER_ERROR,
"Something went wrong...",
).into_response()
} else {
Redirect::to("/").into_response()
}
}
fn something() -> bool {
// ...
}
fn something_else() -> bool {
// ...
}
关于实现 IntoResponse
的注意事项
您可以使用impl IntoResponse
作为处理程序的返回类 型,以避免输入大量类型。
例如:
use axum::http::StatusCode;
async fn handler() -> (StatusCode, [(&'static str, &'static str); 1], &'static str) {
(StatusCode::OK, [("x-foo", "bar")], "Hello, World!")
}
使用 impl IntoResponse
会变得更容易:
use axum::{http::StatusCode, response::IntoResponse};
async fn impl_into_response() -> impl IntoResponse {
(StatusCode::OK, [("x-foo", "bar")], "Hello, World!")
}
warning
然而,impl IntoResponse
有一些局限性。首先,它只能用于返回单一类型。
use axum::{http::StatusCode, response::IntoResponse};
async fn handler() -> impl IntoResponse {
if check_something() {
StatusCode::NOT_FOUND
} else {
"Hello, World!"
}
}
fn check_something() -> bool {
// ...
}
此函数返回的内容要么是 StatusCode
,要么是 &'static str
,而 impl Trait
不允许这样做。
warning
其次impl IntoResponse
在与Result
和?
一起使用时可能导致类型推断问题:
use axum::{http::StatusCode, response::IntoResponse};
async fn handler() -> impl IntoResponse {
create_thing()?;
Ok(StatusCode::CREATED)
}
fn create_thing() -> Result<(), StatusCode> {
// ...
}
这是因为?
支持使用From
trait 来转换为不同的错误类型,但它不知道要转 换成哪种类型,
因为我们仅指定了impl IntoResponse
作为返回类型。
warning
Result<impl IntoResponse, impl IntoResponse>
也不总是奏效:
use axum::{http::StatusCode, response::IntoResponse};
async fn handler() -> Result<impl IntoResponse, impl IntoResponse> {
create_thing()?;
Ok(StatusCode::CREATED)
}
fn create_thing() -> Result<(), StatusCode> {
// ...
}
解决方法是使用具体的错误类型,例如 Result<impl IntoResponse, StatusCode>
:
use axum::{http::StatusCode, response::IntoResponse};
async fn handler() -> Result<impl IntoResponse, StatusCode> {
create_thing()?;
Ok(StatusCode::CREATED)
}
fn create_thing() -> Result<(), StatusCode> {
// ...
}
因此,通常不建议使用 impl IntoResponse
,除非您熟悉 impl Trait
如何工作的细节。
重新导出
pub use crate::Json; // json
pub use crate::form::Form; // form
pub use crate::Extension;
模块
sse
(tokio): 服务器推送事件(SSE)响应。
结构体
AppendHeaders
: 为响应追加标题。ErrorResponse
: 基于IntoResponse
的错误类型Html
: 一个 HTML 响应。Redirect
: 将请求重定向到另一个位置的响应。ResponseParts
: 响应的部分。Sse
(tokio): 一个 SSE 响应
Traits
IntoResponse
: 生成响应的Trait。IntoResponseParts
: 向响应添加标头和扩展的Trait。
类型别名
Response
:http::Response
的类型别名,默认的正文类型为Body
,这是 axum 中最常用的正文类型。Result
: 基于IntoResponse
的结果类型,将ErrorResponse
作为错误类型