Skip to main content

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): 其中 T1Tn 均实现了 IntoResponseParts 接口。
  • (StatusCode, T1, .., Tn, impl IntoResponse): 其中T1Tn都实现了IntoResponseParts接口。
  • (Parts, T1, .., Tn, impl IntoResponse): 其中 T1Tn 均实现了 IntoResponseParts 接口。
  • (Response<()>, T1, .., Tn, impl IntoResponse): 其中T1Tn均实现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 作为错误类型