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