axum/response/
mod.rs

1#![doc = include_str!("../docs/response.md")]
2
3use axum_core::body::Body;
4use http::{header, HeaderValue};
5
6mod redirect;
7
8#[cfg(feature = "tokio")]
9pub mod sse;
10
11#[doc(no_inline)]
12#[cfg(feature = "json")]
13pub use crate::Json;
14
15#[cfg(feature = "form")]
16#[doc(no_inline)]
17pub use crate::form::Form;
18
19#[doc(no_inline)]
20pub use crate::Extension;
21
22#[doc(inline)]
23pub use axum_core::response::{
24    AppendHeaders, ErrorResponse, IntoResponse, IntoResponseParts, Response, ResponseParts, Result,
25};
26
27#[doc(inline)]
28pub use self::redirect::Redirect;
29
30#[doc(inline)]
31#[cfg(feature = "tokio")]
32pub use sse::Sse;
33
34/// An HTML response.
35///
36/// Will automatically get `Content-Type: text/html`.
37#[derive(Clone, Copy, Debug)]
38#[must_use]
39pub struct Html<T>(pub T);
40
41impl<T> IntoResponse for Html<T>
42where
43    T: Into<Body>,
44{
45    fn into_response(self) -> Response {
46        (
47            [(
48                header::CONTENT_TYPE,
49                HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
50            )],
51            self.0.into(),
52        )
53            .into_response()
54    }
55}
56
57impl<T> From<T> for Html<T> {
58    fn from(inner: T) -> Self {
59        Self(inner)
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use crate::extract::Extension;
66    use crate::{routing::get, Router};
67    use axum_core::response::IntoResponse;
68    use http::HeaderMap;
69    use http::{StatusCode, Uri};
70
71    // just needs to compile
72    #[allow(dead_code)]
73    fn impl_trait_result_works() {
74        async fn impl_trait_ok() -> Result<impl IntoResponse, ()> {
75            Ok(())
76        }
77
78        async fn impl_trait_err() -> Result<(), impl IntoResponse> {
79            Err(())
80        }
81
82        async fn impl_trait_both(uri: Uri) -> Result<impl IntoResponse, impl IntoResponse> {
83            if uri.path() == "/" {
84                Ok(())
85            } else {
86                Err(())
87            }
88        }
89
90        async fn impl_trait(uri: Uri) -> impl IntoResponse {
91            if uri.path() == "/" {
92                Ok(())
93            } else {
94                Err(())
95            }
96        }
97
98        _ = Router::<()>::new()
99            .route("/", get(impl_trait_ok))
100            .route("/", get(impl_trait_err))
101            .route("/", get(impl_trait_both))
102            .route("/", get(impl_trait));
103    }
104
105    // just needs to compile
106    #[allow(dead_code)]
107    fn tuple_responses() {
108        async fn status() -> impl IntoResponse {
109            StatusCode::OK
110        }
111
112        async fn status_headermap() -> impl IntoResponse {
113            (StatusCode::OK, HeaderMap::new())
114        }
115
116        async fn status_header_array() -> impl IntoResponse {
117            (StatusCode::OK, [("content-type", "text/plain")])
118        }
119
120        async fn status_headermap_body() -> impl IntoResponse {
121            (StatusCode::OK, HeaderMap::new(), String::new())
122        }
123
124        async fn status_header_array_body() -> impl IntoResponse {
125            (
126                StatusCode::OK,
127                [("content-type", "text/plain")],
128                String::new(),
129            )
130        }
131
132        async fn status_headermap_impl_into_response() -> impl IntoResponse {
133            (StatusCode::OK, HeaderMap::new(), impl_into_response())
134        }
135
136        async fn status_header_array_impl_into_response() -> impl IntoResponse {
137            (
138                StatusCode::OK,
139                [("content-type", "text/plain")],
140                impl_into_response(),
141            )
142        }
143
144        fn impl_into_response() -> impl IntoResponse {}
145
146        async fn status_header_array_extension_body() -> impl IntoResponse {
147            (
148                StatusCode::OK,
149                [("content-type", "text/plain")],
150                Extension(1),
151                String::new(),
152            )
153        }
154
155        async fn status_header_array_extension_mixed_body() -> impl IntoResponse {
156            (
157                StatusCode::OK,
158                [("content-type", "text/plain")],
159                Extension(1),
160                HeaderMap::new(),
161                String::new(),
162            )
163        }
164
165        //
166
167        async fn headermap() -> impl IntoResponse {
168            HeaderMap::new()
169        }
170
171        async fn header_array() -> impl IntoResponse {
172            [("content-type", "text/plain")]
173        }
174
175        async fn headermap_body() -> impl IntoResponse {
176            (HeaderMap::new(), String::new())
177        }
178
179        async fn header_array_body() -> impl IntoResponse {
180            ([("content-type", "text/plain")], String::new())
181        }
182
183        async fn headermap_impl_into_response() -> impl IntoResponse {
184            (HeaderMap::new(), impl_into_response())
185        }
186
187        async fn header_array_impl_into_response() -> impl IntoResponse {
188            ([("content-type", "text/plain")], impl_into_response())
189        }
190
191        async fn header_array_extension_body() -> impl IntoResponse {
192            (
193                [("content-type", "text/plain")],
194                Extension(1),
195                String::new(),
196            )
197        }
198
199        async fn header_array_extension_mixed_body() -> impl IntoResponse {
200            (
201                [("content-type", "text/plain")],
202                Extension(1),
203                HeaderMap::new(),
204                String::new(),
205            )
206        }
207
208        _ = Router::<()>::new()
209            .route("/", get(status))
210            .route("/", get(status_headermap))
211            .route("/", get(status_header_array))
212            .route("/", get(status_headermap_body))
213            .route("/", get(status_header_array_body))
214            .route("/", get(status_headermap_impl_into_response))
215            .route("/", get(status_header_array_impl_into_response))
216            .route("/", get(status_header_array_extension_body))
217            .route("/", get(status_header_array_extension_mixed_body))
218            .route("/", get(headermap))
219            .route("/", get(header_array))
220            .route("/", get(headermap_body))
221            .route("/", get(header_array_body))
222            .route("/", get(headermap_impl_into_response))
223            .route("/", get(header_array_impl_into_response))
224            .route("/", get(header_array_extension_body))
225            .route("/", get(header_array_extension_mixed_body));
226    }
227}