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