Expand description
Types and traits for generating responses.
§Building responses
Anything that implements IntoResponse can be returned from a handler. axum
provides implementations for common types:
use axum::{
Json,
response::{Html, IntoResponse},
http::{StatusCode, Uri, header::{self, HeaderMap, HeaderName}},
};
// `()` gives an empty response
async fn empty() {}
// String will get a `text/plain; charset=utf-8` content-type
async fn plain_text(uri: Uri) -> String {
format!("Hi from {}", uri.path())
}
// Bytes will get a `application/octet-stream` content-type
async fn bytes() -> Vec<u8> {
vec![1, 2, 3, 4]
}
// `Json` will get a `application/json` content-type and work with anything that
// implements `serde::Serialize`
async fn json() -> Json<Vec<String>> {
Json(vec!["foo".to_owned(), "bar".to_owned()])
}
// `Html` will get a `text/html` content-type
async fn html() -> Html<&'static str> {
Html("<p>Hello, World!</p>")
}
// `StatusCode` gives an empty response with that status code
async fn status() -> StatusCode {
StatusCode::NOT_FOUND
}
// `HeaderMap` gives an empty response with some headers
async fn headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(header::SERVER, "axum".parse().unwrap());
headers
}
// An array of tuples also gives headers
async fn array_headers() -> [(HeaderName, &'static str); 2] {
[
(header::SERVER, "axum"),
(header::CONTENT_TYPE, "text/plain")
]
}
// Use `impl IntoResponse` to avoid writing the whole type
async fn impl_trait() -> impl IntoResponse {
[
(header::SERVER, "axum"),
(header::CONTENT_TYPE, "text/plain")
]
}Additionally you can return tuples to build more complex responses from individual parts.
use axum::{
Json,
response::IntoResponse,
http::{StatusCode, HeaderMap, Uri, header},
extract::Extension,
};
// `(StatusCode, impl IntoResponse)` will override the status code of the response
async fn with_status(uri: Uri) -> (StatusCode, String) {
(StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path()))
}
// Use `impl IntoResponse` to avoid having to type the whole type
async fn impl_trait(uri: Uri) -> impl IntoResponse {
(StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path()))
}
// `(HeaderMap, impl IntoResponse)` to add additional headers
async fn with_headers() -> impl IntoResponse {
let mut headers = HeaderMap::new();
headers.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap());
(headers, "foo")
}
// Or an array of tuples to more easily build the headers
async fn with_array_headers() -> impl IntoResponse {
([(header::CONTENT_TYPE, "text/plain")], "foo")
}
// Use string keys for custom headers
async fn with_array_headers_custom() -> impl IntoResponse {
([("x-custom", "custom")], "foo")
}
// `(StatusCode, headers, impl IntoResponse)` to set status and add headers
// `headers` can be either a `HeaderMap` or an array of tuples
async fn with_status_and_array_headers() -> impl IntoResponse {
(
StatusCode::NOT_FOUND,
[(header::CONTENT_TYPE, "text/plain")],
"foo",
)
}
// `(Extension<_>, impl IntoResponse)` to set response extensions
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());
}
(
// set status code
StatusCode::NOT_FOUND,
// headers with an array
[("x-custom", "custom")],
// some extensions
Extension(Foo("foo")),
Extension(Foo("bar")),
// more headers, built dynamically
header_map,
// and finally the body
"foo",
)
}In general you can return tuples like:
(StatusCode, impl IntoResponse)(Parts, impl IntoResponse)(Response<()>, impl IntoResponse)(T1, .., Tn, impl IntoResponse)whereT1toTnall implementIntoResponseParts.(StatusCode, T1, .., Tn, impl IntoResponse)whereT1toTnall implementIntoResponseParts.(Parts, T1, .., Tn, impl IntoResponse)whereT1toTnall implementIntoResponseParts.(Response<()>, T1, .., Tn, impl IntoResponse)whereT1toTnall implementIntoResponseParts.
This means you cannot accidentally override the status or body as IntoResponseParts only allows
setting headers and extensions.
Use Response for more low level control:
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()
}§Returning different response types
If you need to return multiple response types, and Result<T, E> isn’t appropriate, you can call
.into_response() to turn things into 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 {
// ...
}§Regarding impl IntoResponse
You can use impl IntoResponse as the return type from handlers to avoid
typing large types. For example
use axum::http::StatusCode;
async fn handler() -> (StatusCode, [(&'static str, &'static str); 1], &'static str) {
(StatusCode::OK, [("x-foo", "bar")], "Hello, World!")
}Becomes easier using impl IntoResponse:
use axum::{http::StatusCode, response::IntoResponse};
async fn impl_into_response() -> impl IntoResponse {
(StatusCode::OK, [("x-foo", "bar")], "Hello, World!")
}However impl IntoResponse has a few limitations. Firstly it can only be used
to return a single type:
use axum::{http::StatusCode, response::IntoResponse};
async fn handler() -> impl IntoResponse {
if check_something() {
StatusCode::NOT_FOUND
} else {
"Hello, World!"
}
}
fn check_something() -> bool {
// ...
}This function returns either a StatusCode or a &'static str which impl Trait doesn’t allow.
Secondly impl IntoResponse can lead to type inference issues when used with
Result and ?:
use axum::{http::StatusCode, response::IntoResponse};
async fn handler() -> impl IntoResponse {
create_thing()?;
Ok(StatusCode::CREATED)
}
fn create_thing() -> Result<(), StatusCode> {
// ...
}This is because ? supports using the From trait to convert to a different
error type but it doesn’t know which type to convert to, because we only
specified impl IntoResponse as the return type.
Result<impl IntoResponse, impl IntoResponse> doesn’t always work either:
use axum::{http::StatusCode, response::IntoResponse};
async fn handler() -> Result<impl IntoResponse, impl IntoResponse> {
create_thing()?;
Ok(StatusCode::CREATED)
}
fn create_thing() -> Result<(), StatusCode> {
// ...
}The solution is to use a concrete error type, such as 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> {
// ...
}Because of this it is generally not recommended to use impl IntoResponse
unless you’re familiar with the details of how impl Trait works.
Re-exports§
Modules§
- sse
- Server-Sent Events (SSE) responses.
Structs§
- Append
Headers - Append headers to a response.
- Error
Response - An
IntoResponse-based error type - Html
- An HTML response.
- NoContent
- An empty response with 204 No Content status.
- Redirect
- Response that redirects the request to another location.
- Response
Parts - Parts of a response.
- Sse
- An SSE response
Traits§
- Into
Response - Trait for generating responses.
- Into
Response Parts - Trait for adding headers and extensions to a response.
Type Aliases§
- Response
- Type alias for
http::Responsewhose body type defaults toBody, the most common body type used with axum. - Result
- An
IntoResponse-based result type that usesErrorResponseas the error type.