diff --git a/axum-core/src/response/into_response_parts.rs b/axum-core/src/response/into_response_parts.rs index b6e838a854..00c2763de6 100644 --- a/axum-core/src/response/into_response_parts.rs +++ b/axum-core/src/response/into_response_parts.rs @@ -57,7 +57,10 @@ use std::{ /// } /// /// // We can now return `SetHeader` in responses -/// async fn handler() -> impl IntoResponse { +/// // +/// // Note that returning `impl IntoResponse` might be easier if the response has many parts to +/// // it. The return type is written out here for clarity. +/// async fn handler() -> (SetHeader<'static>, SetHeader<'static>, &'static str) { /// ( /// SetHeader("server", "axum"), /// SetHeader("x-foo", "custom"), @@ -66,7 +69,7 @@ use std::{ /// } /// /// // Or on its own as the whole response -/// async fn other_handler() -> impl IntoResponse { +/// async fn other_handler() -> SetHeader<'static> { /// SetHeader("x-foo", "custom") /// } /// ``` diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 85cd421e10..7bda1ea34e 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -50,7 +50,7 @@ pub use cookie_lib::Key; /// async fn create_session( /// TypedHeader(auth): TypedHeader>, /// jar: CookieJar, -/// ) -> impl IntoResponse { +/// ) -> Result<(CookieJar, Redirect), StatusCode> { /// if let Some(session_id) = authorize_and_create_session(auth.token()).await { /// Ok(( /// // the updated jar must be returned for the changes @@ -63,7 +63,7 @@ pub use cookie_lib::Key; /// } /// } /// -/// async fn me(jar: CookieJar) -> impl IntoResponse { +/// async fn me(jar: CookieJar) -> Result<(), StatusCode> { /// if let Some(session_id) = jar.get("session_id") { /// // fetch and render user... /// # Ok(()) @@ -141,7 +141,7 @@ impl CookieJar { /// use axum_extra::extract::cookie::{CookieJar, Cookie}; /// use axum::response::IntoResponse; /// - /// async fn handle(jar: CookieJar) -> impl IntoResponse { + /// async fn handle(jar: CookieJar) -> CookieJar { /// jar.remove(Cookie::named("foo")) /// } /// ``` @@ -161,7 +161,7 @@ impl CookieJar { /// use axum_extra::extract::cookie::{CookieJar, Cookie}; /// use axum::response::IntoResponse; /// - /// async fn handle(jar: CookieJar) -> impl IntoResponse { + /// async fn handle(jar: CookieJar) -> CookieJar { /// jar.add(Cookie::new("foo", "bar")) /// } /// ``` diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index a896d110e7..a583a3c9a9 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -33,12 +33,12 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// /// async fn set_secret( /// jar: PrivateCookieJar, -/// ) -> impl IntoResponse { +/// ) -> (PrivateCookieJar, Redirect) { /// let updated_jar = jar.add(Cookie::new("secret", "secret-data")); /// (updated_jar, Redirect::to("/get")) /// } /// -/// async fn get_secret(jar: PrivateCookieJar) -> impl IntoResponse { +/// async fn get_secret(jar: PrivateCookieJar) { /// if let Some(data) = jar.get("secret") { /// // ... /// } @@ -129,7 +129,7 @@ impl PrivateCookieJar { /// use axum_extra::extract::cookie::{PrivateCookieJar, Cookie}; /// use axum::response::IntoResponse; /// - /// async fn handle(jar: PrivateCookieJar) -> impl IntoResponse { + /// async fn handle(jar: PrivateCookieJar) -> PrivateCookieJar { /// jar.remove(Cookie::named("foo")) /// } /// ``` @@ -149,7 +149,7 @@ impl PrivateCookieJar { /// use axum_extra::extract::cookie::{PrivateCookieJar, Cookie}; /// use axum::response::IntoResponse; /// - /// async fn handle(jar: PrivateCookieJar) -> impl IntoResponse { + /// async fn handle(jar: PrivateCookieJar) -> PrivateCookieJar { /// jar.add(Cookie::new("foo", "bar")) /// } /// ``` diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index 000c51e561..51fb865c6e 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -35,7 +35,7 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// async fn create_session( /// TypedHeader(auth): TypedHeader>, /// jar: SignedCookieJar, -/// ) -> impl IntoResponse { +/// ) -> Result<(SignedCookieJar, Redirect), StatusCode> { /// if let Some(session_id) = authorize_and_create_session(auth.token()).await { /// Ok(( /// // the updated jar must be returned for the changes @@ -48,7 +48,7 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// } /// } /// -/// async fn me(jar: SignedCookieJar) -> impl IntoResponse { +/// async fn me(jar: SignedCookieJar) -> Result<(), StatusCode> { /// if let Some(session_id) = jar.get("session_id") { /// // fetch and render user... /// # Ok(()) @@ -148,7 +148,7 @@ impl SignedCookieJar { /// use axum_extra::extract::cookie::{SignedCookieJar, Cookie}; /// use axum::response::IntoResponse; /// - /// async fn handle(jar: SignedCookieJar) -> impl IntoResponse { + /// async fn handle(jar: SignedCookieJar) -> SignedCookieJar { /// jar.remove(Cookie::named("foo")) /// } /// ``` @@ -168,7 +168,7 @@ impl SignedCookieJar { /// use axum_extra::extract::cookie::{SignedCookieJar, Cookie}; /// use axum::response::IntoResponse; /// - /// async fn handle(jar: SignedCookieJar) -> impl IntoResponse { + /// async fn handle(jar: SignedCookieJar) -> SignedCookieJar { /// jar.add(Cookie::new("foo", "bar")) /// } /// ``` diff --git a/axum-extra/src/response/erased_json.rs b/axum-extra/src/response/erased_json.rs index 98c791a7fc..c61d31456b 100644 --- a/axum-extra/src/response/erased_json.rs +++ b/axum-extra/src/response/erased_json.rs @@ -15,7 +15,7 @@ use serde::Serialize; /// ```rust /// # use axum::{response::IntoResponse}; /// # use axum_extra::response::ErasedJson; -/// async fn handler() -> impl IntoResponse { +/// async fn handler() -> ErasedJson { /// # let condition = true; /// # let foo = (); /// # let bar = vec![()]; diff --git a/axum/src/body/stream_body.rs b/axum/src/body/stream_body.rs index 89758ed33c..3d83ef56f1 100644 --- a/axum/src/body/stream_body.rs +++ b/axum/src/body/stream_body.rs @@ -32,10 +32,11 @@ pin_project! { /// body::StreamBody, /// response::IntoResponse, /// }; - /// use futures::stream; + /// use futures::stream::{self, Stream}; + /// use std::io; /// - /// async fn handler() -> impl IntoResponse { - /// let chunks: Vec> = vec![ + /// async fn handler() -> StreamBody>> { + /// let chunks: Vec> = vec![ /// Ok("Hello,"), /// Ok(" "), /// Ok("world!"), diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index ba5d27842b..3792092322 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -316,7 +316,9 @@ use axum::{ }; use serde_json::{json, Value}; -async fn handler(result: Result, JsonRejection>) -> impl IntoResponse { +async fn handler( + result: Result, JsonRejection>, +) -> Result, (StatusCode, String)> { match result { // if the client sent valid JSON then we're good Ok(Json(payload)) => Ok(Json(json!({ "payload": payload }))), diff --git a/axum/src/docs/method_routing/fallback.md b/axum/src/docs/method_routing/fallback.md index 0d2ee30045..c6a0d09024 100644 --- a/axum/src/docs/method_routing/fallback.md +++ b/axum/src/docs/method_routing/fallback.md @@ -15,7 +15,7 @@ let handler = get(|| async {}).fallback(fallback.into_service()); let app = Router::new().route("/", handler); -async fn fallback(method: Method, uri: Uri) -> impl IntoResponse { +async fn fallback(method: Method, uri: Uri) -> (StatusCode, String) { (StatusCode::NOT_FOUND, format!("`{}` not allowed for {}", method, uri)) } # async { @@ -44,8 +44,8 @@ let two = post(|| async {}) let method_route = one.merge(two); -async fn fallback_one() -> impl IntoResponse {} -async fn fallback_two() -> impl IntoResponse {} +async fn fallback_one() -> impl IntoResponse { /* ... */ } +async fn fallback_two() -> impl IntoResponse { /* ... */ } # let app = axum::Router::new().route("/", method_route); # async { # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md index a65ca6d568..17a3d72fb4 100644 --- a/axum/src/docs/middleware.md +++ b/axum/src/docs/middleware.md @@ -383,7 +383,7 @@ use axum::{ Router, http::{Request, StatusCode}, routing::get, - response::IntoResponse, + response::{IntoResponse, Response}, middleware::{self, Next}, extract::Extension, }; @@ -391,7 +391,7 @@ use axum::{ #[derive(Clone)] struct CurrentUser { /* ... */ } -async fn auth(mut req: Request, next: Next) -> impl IntoResponse { +async fn auth(mut req: Request, next: Next) -> Result { let auth_header = req.headers() .get(http::header::AUTHORIZATION) .and_then(|header| header.to_str().ok()); diff --git a/axum/src/docs/response.md b/axum/src/docs/response.md index 270ba7bda0..09839de5c3 100644 --- a/axum/src/docs/response.md +++ b/axum/src/docs/response.md @@ -133,7 +133,7 @@ async fn all_the_things(uri: Uri) -> impl IntoResponse { ( // set status code StatusCode::NOT_FOUND, - // headers ith an array + // headers with an array [("x-custom", "custom")], // some extensions Extension(Foo("foo")), @@ -165,11 +165,11 @@ Use [`Response`](crate::response::Response) for more low level control: use axum::{ Json, response::{IntoResponse, Response}, - body::Full, + body::{Full, Bytes}, http::StatusCode, }; -async fn response() -> impl IntoResponse { +async fn response() -> Response> { Response::builder() .status(StatusCode::NOT_FOUND) .header("x-foo", "custom header") @@ -178,6 +178,41 @@ async fn response() -> impl IntoResponse { } ``` +# Returning different response types + +If you need to return multiple response types, and `Result` isn't appropriate, you can call +`.into_response()` to turn things into `axum::response::Response`: + +```rust +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 { + // ... + # true +} + +fn something_else() -> bool { + // ... + # true +} +``` + [`IntoResponse`]: crate::response::IntoResponse [`IntoResponseParts`]: crate::response::IntoResponseParts [`StatusCode`]: http::StatusCode diff --git a/axum/src/docs/routing/fallback.md b/axum/src/docs/routing/fallback.md index 78cac4e122..a5c7467ed3 100644 --- a/axum/src/docs/routing/fallback.md +++ b/axum/src/docs/routing/fallback.md @@ -15,7 +15,7 @@ let app = Router::new() .route("/foo", get(|| async { /* ... */ })) .fallback(fallback.into_service()); -async fn fallback(uri: Uri) -> impl IntoResponse { +async fn fallback(uri: Uri) -> (StatusCode, String) { (StatusCode::NOT_FOUND, format!("No route for {}", uri)) } # async { diff --git a/axum/src/extension.rs b/axum/src/extension.rs index 55efa1b5ed..390f29e031 100644 --- a/axum/src/extension.rs +++ b/axum/src/extension.rs @@ -59,7 +59,7 @@ use tower_service::Service; /// response::IntoResponse, /// }; /// -/// async fn handler() -> impl IntoResponse { +/// async fn handler() -> (Extension, &'static str) { /// ( /// Extension(Foo("foo")), /// "Hello, World!" diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 008115a52b..90a8c9b542 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -6,13 +6,13 @@ //! use axum::{ //! extract::ws::{WebSocketUpgrade, WebSocket}, //! routing::get, -//! response::IntoResponse, +//! response::{IntoResponse, Response}, //! Router, //! }; //! //! let app = Router::new().route("/ws", get(handler)); //! -//! async fn handler(ws: WebSocketUpgrade) -> impl IntoResponse { +//! async fn handler(ws: WebSocketUpgrade) -> Response { //! ws.on_upgrade(handle_socket) //! } //! @@ -148,13 +148,13 @@ impl WebSocketUpgrade { /// use axum::{ /// extract::ws::{WebSocketUpgrade, WebSocket}, /// routing::get, - /// response::IntoResponse, + /// response::{IntoResponse, Response}, /// Router, /// }; /// /// let app = Router::new().route("/ws", get(handler)); /// - /// async fn handler(ws: WebSocketUpgrade) -> impl IntoResponse { + /// async fn handler(ws: WebSocketUpgrade) -> Response { /// ws.protocols(["graphql-ws", "graphql-transport-ws"]) /// .on_upgrade(|socket| async { /// // ... diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index 56348eb3cc..69482fecef 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -126,7 +126,7 @@ pub trait Handler: Clone + Send + Sized + 'static { /// use tower::make::Shared; /// use std::net::SocketAddr; /// - /// async fn handler(method: Method, uri: Uri) -> impl IntoResponse { + /// async fn handler(method: Method, uri: Uri) -> (StatusCode, String) { /// (StatusCode::NOT_FOUND, format!("Nothing to see at {} {}", method, uri)) /// } /// @@ -157,7 +157,7 @@ pub trait Handler: Clone + Send + Sized + 'static { /// }; /// use std::net::SocketAddr; /// - /// async fn handler(method: Method, uri: Uri, body: String) -> impl IntoResponse { + /// async fn handler(method: Method, uri: Uri, body: String) -> String { /// format!("received `{} {}` with body `{:?}`", method, uri, body) /// } /// @@ -188,7 +188,7 @@ pub trait Handler: Clone + Send + Sized + 'static { /// }; /// use std::net::SocketAddr; /// - /// async fn handler(ConnectInfo(addr): ConnectInfo) -> impl IntoResponse { + /// async fn handler(ConnectInfo(addr): ConnectInfo) -> String { /// format!("Hello {}", addr) /// } /// diff --git a/axum/src/middleware/from_fn.rs b/axum/src/middleware/from_fn.rs index df8d31ea7d..9f9563b590 100644 --- a/axum/src/middleware/from_fn.rs +++ b/axum/src/middleware/from_fn.rs @@ -34,11 +34,11 @@ use tower_service::Service; /// Router, /// http::{Request, StatusCode}, /// routing::get, -/// response::IntoResponse, +/// response::{IntoResponse, Response}, /// middleware::{self, Next}, /// }; /// -/// async fn auth(req: Request, next: Next) -> impl IntoResponse { +/// async fn auth(req: Request, next: Next) -> Result { /// let auth_header = req.headers() /// .get(http::header::AUTHORIZATION) /// .and_then(|header| header.to_str().ok()); @@ -71,7 +71,7 @@ use tower_service::Service; /// Router, /// http::{Request, StatusCode}, /// routing::get, -/// response::IntoResponse, +/// response::{IntoResponse, Response}, /// middleware::{self, Next} /// }; /// @@ -82,9 +82,9 @@ use tower_service::Service; /// req: Request, /// next: Next, /// state: State, -/// ) -> impl IntoResponse { +/// ) -> Response { /// // ... -/// # () +/// # ().into_response() /// } /// /// let state = State { /* ... */ }; @@ -105,7 +105,7 @@ use tower_service::Service; /// extract::Extension, /// http::{Request, StatusCode}, /// routing::get, -/// response::IntoResponse, +/// response::{IntoResponse, Response}, /// middleware::{self, Next}, /// }; /// use tower::ServiceBuilder; @@ -116,11 +116,11 @@ use tower_service::Service; /// async fn my_middleware( /// req: Request, /// next: Next, -/// ) -> impl IntoResponse { +/// ) -> Response { /// let state: &State = req.extensions().get().unwrap(); /// /// // ... -/// # () +/// # ().into_response() /// } /// /// let state = State { /* ... */ }; diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index edd3ac3a15..264dedaacf 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -603,7 +603,7 @@ where /// }; /// use std::net::SocketAddr; /// - /// async fn handler(method: Method, uri: Uri, body: String) -> impl IntoResponse { + /// async fn handler(method: Method, uri: Uri, body: String) -> String { /// format!("received `{} {}` with body `{:?}`", method, uri, body) /// } /// @@ -637,7 +637,7 @@ where /// }; /// use std::net::SocketAddr; /// - /// async fn handler(ConnectInfo(addr): ConnectInfo) -> impl IntoResponse { + /// async fn handler(ConnectInfo(addr): ConnectInfo) -> String { /// format!("Hello {}", addr) /// } /// diff --git a/axum/src/typed_header.rs b/axum/src/typed_header.rs index bb5bc721ed..1ce72d04f8 100644 --- a/axum/src/typed_header.rs +++ b/axum/src/typed_header.rs @@ -40,7 +40,7 @@ use std::{convert::Infallible, ops::Deref}; /// headers::ContentType, /// }; /// -/// async fn handler() -> impl IntoResponse { +/// async fn handler() -> (TypedHeader, &'static str) { /// ( /// TypedHeader(ContentType::text_utf8()), /// "Hello, World!",