1Types and traits for generating responses. 2 3# Table of contents 4 5- [Building responses](#building-responses) 6- [Returning different response types](#returning-different-response-types) 7- [Regarding `impl IntoResponse`](#regarding-impl-intoresponse) 8 9# Building responses 10 11Anything that implements [`IntoResponse`] can be returned from a handler. axum 12provides implementations for common types: 13 14```rust,no_run 15use axum::{ 16 Json, 17 response::{Html, IntoResponse}, 18 http::{StatusCode, Uri, header::{self, HeaderMap, HeaderName}}, 19}; 20 21// `()` gives an empty response 22async fn empty() {} 23 24// String will get a `text/plain; charset=utf-8` content-type 25async fn plain_text(uri: Uri) -> String { 26 format!("Hi from {}", uri.path()) 27} 28 29// Bytes will get a `application/octet-stream` content-type 30async fn bytes() -> Vec<u8> { 31 vec![1, 2, 3, 4] 32} 33 34// `Json` will get a `application/json` content-type and work with anything that 35// implements `serde::Serialize` 36async fn json() -> Json<Vec<String>> { 37 Json(vec!["foo".to_owned(), "bar".to_owned()]) 38} 39 40// `Html` will get a `text/html` content-type 41async fn html() -> Html<&'static str> { 42 Html("<p>Hello, World!</p>") 43} 44 45// `StatusCode` gives an empty response with that status code 46async fn status() -> StatusCode { 47 StatusCode::NOT_FOUND 48} 49 50// `HeaderMap` gives an empty response with some headers 51async fn headers() -> HeaderMap { 52 let mut headers = HeaderMap::new(); 53 headers.insert(header::SERVER, "axum".parse().unwrap()); 54 headers 55} 56 57// An array of tuples also gives headers 58async fn array_headers() -> [(HeaderName, &'static str); 2] { 59 [ 60 (header::SERVER, "axum"), 61 (header::CONTENT_TYPE, "text/plain") 62 ] 63} 64 65// Use `impl IntoResponse` to avoid writing the whole type 66async fn impl_trait() -> impl IntoResponse { 67 [ 68 (header::SERVER, "axum"), 69 (header::CONTENT_TYPE, "text/plain") 70 ] 71} 72``` 73 74Additionally you can return tuples to build more complex responses from 75individual parts. 76 77```rust,no_run 78use axum::{ 79 Json, 80 response::IntoResponse, 81 http::{StatusCode, HeaderMap, Uri, header}, 82 extract::Extension, 83}; 84 85// `(StatusCode, impl IntoResponse)` will override the status code of the response 86async fn with_status(uri: Uri) -> (StatusCode, String) { 87 (StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path())) 88} 89 90// Use `impl IntoResponse` to avoid having to type the whole type 91async fn impl_trait(uri: Uri) -> impl IntoResponse { 92 (StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path())) 93} 94 95// `(HeaderMap, impl IntoResponse)` to add additional headers 96async fn with_headers() -> impl IntoResponse { 97 let mut headers = HeaderMap::new(); 98 headers.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap()); 99 (headers, "foo") 100} 101 102// Or an array of tuples to more easily build the headers 103async fn with_array_headers() -> impl IntoResponse { 104 ([(header::CONTENT_TYPE, "text/plain")], "foo") 105} 106 107// Use string keys for custom headers 108async fn with_array_headers_custom() -> impl IntoResponse { 109 ([("x-custom", "custom")], "foo") 110} 111 112// `(StatusCode, headers, impl IntoResponse)` to set status and add headers 113// `headers` can be either a `HeaderMap` or an array of tuples 114async fn with_status_and_array_headers() -> impl IntoResponse { 115 ( 116 StatusCode::NOT_FOUND, 117 [(header::CONTENT_TYPE, "text/plain")], 118 "foo", 119 ) 120} 121 122// `(Extension<_>, impl IntoResponse)` to set response extensions 123async fn with_status_extensions() -> impl IntoResponse { 124 ( 125 Extension(Foo("foo")), 126 "foo", 127 ) 128} 129 130struct Foo(&'static str); 131 132// Or mix and match all the things 133async fn all_the_things(uri: Uri) -> impl IntoResponse { 134 let mut header_map = HeaderMap::new(); 135 if uri.path() == "/" { 136 header_map.insert(header::SERVER, "axum".parse().unwrap()); 137 } 138 139 ( 140 // set status code 141 StatusCode::NOT_FOUND, 142 // headers with an array 143 [("x-custom", "custom")], 144 // some extensions 145 Extension(Foo("foo")), 146 Extension(Foo("bar")), 147 // more headers, built dynamically 148 header_map, 149 // and finally the body 150 "foo", 151 ) 152} 153``` 154 155In general you can return tuples like: 156 157- `(StatusCode, impl IntoResponse)` 158- `(Parts, impl IntoResponse)` 159- `(Response<()>, impl IntoResponse)` 160- `(T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. 161- `(StatusCode, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. 162- `(Parts, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. 163- `(Response<()>, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. 164 165This means you cannot accidentally override the status or body as [`IntoResponseParts`] only allows 166setting headers and extensions. 167 168Use [`Response`](crate::response::Response) for more low level control: 169 170```rust,no_run 171use axum::{ 172 Json, 173 response::{IntoResponse, Response}, 174 body::{Full, Bytes}, 175 http::StatusCode, 176}; 177 178async fn response() -> Response<Full<Bytes>> { 179 Response::builder() 180 .status(StatusCode::NOT_FOUND) 181 .header("x-foo", "custom header") 182 .body(Full::from("not found")) 183 .unwrap() 184} 185``` 186 187# Returning different response types 188 189If you need to return multiple response types, and `Result<T, E>` isn't appropriate, you can call 190`.into_response()` to turn things into `axum::response::Response`: 191 192```rust 193use axum::{ 194 response::{IntoResponse, Redirect, Response}, 195 http::StatusCode, 196}; 197 198async fn handle() -> Response { 199 if something() { 200 "All good!".into_response() 201 } else if something_else() { 202 ( 203 StatusCode::INTERNAL_SERVER_ERROR, 204 "Something went wrong...", 205 ).into_response() 206 } else { 207 Redirect::to("/").into_response() 208 } 209} 210 211fn something() -> bool { 212 // ... 213 # true 214} 215 216fn something_else() -> bool { 217 // ... 218 # true 219} 220``` 221 222# Regarding `impl IntoResponse` 223 224You can use `impl IntoResponse` as the return type from handlers to avoid 225typing large types. For example 226 227```rust 228use axum::http::StatusCode; 229 230async fn handler() -> (StatusCode, [(&'static str, &'static str); 1], &'static str) { 231 (StatusCode::OK, [("x-foo", "bar")], "Hello, World!") 232} 233``` 234 235Becomes easier using `impl IntoResponse`: 236 237```rust 238use axum::{http::StatusCode, response::IntoResponse}; 239 240async fn impl_into_response() -> impl IntoResponse { 241 (StatusCode::OK, [("x-foo", "bar")], "Hello, World!") 242} 243``` 244 245However `impl IntoResponse` has a few limitations. Firstly it can only be used 246to return a single type: 247 248```rust,compile_fail 249use axum::{http::StatusCode, response::IntoResponse}; 250 251async fn handler() -> impl IntoResponse { 252 if check_something() { 253 StatusCode::NOT_FOUND 254 } else { 255 "Hello, World!" 256 } 257} 258 259fn check_something() -> bool { 260 # false 261 // ... 262} 263``` 264 265This function returns either a `StatusCode` or a `&'static str` which `impl 266Trait` doesn't allow. 267 268Secondly `impl IntoResponse` can lead to type inference issues when used with 269`Result` and `?`: 270 271```rust,compile_fail 272use axum::{http::StatusCode, response::IntoResponse}; 273 274async fn handler() -> impl IntoResponse { 275 create_thing()?; 276 Ok(StatusCode::CREATED) 277} 278 279fn create_thing() -> Result<(), StatusCode> { 280 # Ok(()) 281 // ... 282} 283``` 284 285This is because `?` supports using the [`From`] trait to convert to a different 286error type but it doesn't know which type to convert to, because we only 287specified `impl IntoResponse` as the return type. 288 289`Result<impl IntoResponse, impl IntoResponse>` doesn't always work either: 290 291```rust,compile_fail 292use axum::{http::StatusCode, response::IntoResponse}; 293 294async fn handler() -> Result<impl IntoResponse, impl IntoResponse> { 295 create_thing()?; 296 Ok(StatusCode::CREATED) 297} 298 299fn create_thing() -> Result<(), StatusCode> { 300 # Ok(()) 301 // ... 302} 303``` 304 305The solution is to use a concrete error type, such as `Result<impl IntoResponse, StatusCode>`: 306 307```rust 308use axum::{http::StatusCode, response::IntoResponse}; 309 310async fn handler() -> Result<impl IntoResponse, StatusCode> { 311 create_thing()?; 312 Ok(StatusCode::CREATED) 313} 314 315fn create_thing() -> Result<(), StatusCode> { 316 # Ok(()) 317 // ... 318} 319``` 320 321Because of this it is generally not recommended to use `impl IntoResponse` 322unless you're familiar with the details of how `impl Trait` works. 323 324[`IntoResponse`]: crate::response::IntoResponse 325[`IntoResponseParts`]: crate::response::IntoResponseParts 326[`StatusCode`]: http::StatusCode 327