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