From 2138489ce55d6d1ec98d3df7c332a753383a5bb4 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 2 Aug 2023 21:17:12 +0200 Subject: [PATCH] Implement `Handler` for `T: IntoResponse` (#2140) --- axum/CHANGELOG.md | 2 ++ axum/src/handler/mod.rs | 42 +++++++++++++++++++++++++++++++++++ axum/src/routing/tests/mod.rs | 11 +++++++++ 3 files changed, 55 insertions(+) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 5e1d181ecf..8cbca27937 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **fixed:** Allow unreachable code in `#[debug_handler]` ([#2014]) - **change:** Update tokio-tungstenite to 0.19 ([#2021]) - **change:** axum's MSRV is now 1.63 ([#2021]) +- **added:** Implement `Handler` for `T: IntoResponse` ([#2140]) [#2021]: https://github.com/tokio-rs/axum/pull/2021 [#2014]: https://github.com/tokio-rs/axum/pull/2014 @@ -78,6 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#2058]: https://github.com/tokio-rs/axum/pull/2058 [#2073]: https://github.com/tokio-rs/axum/pull/2073 [#2096]: https://github.com/tokio-rs/axum/pull/2096 +[#2140]: https://github.com/tokio-rs/axum/pull/2140 # 0.6.17 (25. April, 2023) diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index 69ff8c8025..ea352b9a2a 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -100,6 +100,31 @@ pub use self::service::HandlerService; /// {} /// ``` #[doc = include_str!("../docs/debugging_handler_type_errors.md")] +/// +/// # Handlers that aren't functions +/// +/// The `Handler` trait is also implemented for `T: IntoResponse`. That allows easily returning +/// fixed data for routes: +/// +/// ``` +/// use axum::{ +/// Router, +/// routing::{get, post}, +/// Json, +/// http::StatusCode, +/// }; +/// use serde_json::json; +/// +/// let app = Router::new() +/// // respond with a fixed string +/// .route("/", get("Hello, World!")) +/// // or return some mock data +/// .route("/users", post(( +/// StatusCode::CREATED, +/// Json(json!({ "id": 1, "username": "alice" })), +/// ))); +/// # let _: Router = app; +/// ``` #[cfg_attr( nightly_error_messages, rustc_on_unimplemented( @@ -224,6 +249,23 @@ macro_rules! impl_handler { all_the_tuples!(impl_handler); +mod private { + // Marker type for `impl Handler for T` + #[allow(missing_debug_implementations)] + pub enum IntoResponseHandler {} +} + +impl Handler for T +where + T: IntoResponse + Clone + Send + 'static, +{ + type Future = std::future::Ready; + + fn call(self, _req: Request, _state: S) -> Self::Future { + std::future::ready(self.into_response()) + } +} + /// A [`Service`] created from a [`Handler`] by applying a Tower middleware. /// /// Created with [`Handler::layer`]. See that method for more details. diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 027d735eb0..2bfc84526b 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -1021,3 +1021,14 @@ async fn connect_going_to_default_fallback() { let body = hyper::body::to_bytes(res).await.unwrap(); assert!(body.is_empty()); } + +#[crate::test] +async fn impl_handler_for_into_response() { + let app = Router::new().route("/things", post((StatusCode::CREATED, "thing created"))); + + let client = TestClient::new(app); + + let res = client.post("/things").send().await; + assert_eq!(res.status(), StatusCode::CREATED); + assert_eq!(res.text().await, "thing created"); +}