1use std::borrow::Cow;
2
3use actix_http::{
4 body::{BoxBody, EitherBody, MessageBody},
5 header::TryIntoHeaderPair,
6 StatusCode,
7};
8use bytes::{Bytes, BytesMut};
9
10use super::CustomizeResponder;
11use crate::{Error, HttpRequest, HttpResponse};
12
13pub trait Responder {
40 type Body: MessageBody + 'static;
41
42 fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body>;
44
45 #[inline]
64 fn customize(self) -> CustomizeResponder<Self>
65 where
66 Self: Sized,
67 {
68 CustomizeResponder::new(self)
69 }
70
71 #[doc(hidden)]
72 #[deprecated(since = "4.0.0", note = "Prefer `.customize().with_status(header)`.")]
73 fn with_status(self, status: StatusCode) -> CustomizeResponder<Self>
74 where
75 Self: Sized,
76 {
77 self.customize().with_status(status)
78 }
79
80 #[doc(hidden)]
81 #[deprecated(since = "4.0.0", note = "Prefer `.customize().insert_header(header)`.")]
82 fn with_header(self, header: impl TryIntoHeaderPair) -> CustomizeResponder<Self>
83 where
84 Self: Sized,
85 {
86 self.customize().insert_header(header)
87 }
88}
89
90impl Responder for actix_http::Response<BoxBody> {
91 type Body = BoxBody;
92
93 #[inline]
94 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
95 HttpResponse::from(self)
96 }
97}
98
99impl Responder for actix_http::ResponseBuilder {
100 type Body = BoxBody;
101
102 #[inline]
103 fn respond_to(mut self, req: &HttpRequest) -> HttpResponse<Self::Body> {
104 self.finish().map_into_boxed_body().respond_to(req)
105 }
106}
107
108impl<R: Responder> Responder for Option<R> {
109 type Body = EitherBody<R::Body>;
110
111 fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
112 match self {
113 Some(val) => val.respond_to(req).map_into_left_body(),
114 None => HttpResponse::new(StatusCode::NOT_FOUND).map_into_right_body(),
115 }
116 }
117}
118
119impl<R, E> Responder for Result<R, E>
120where
121 R: Responder,
122 E: Into<Error>,
123{
124 type Body = EitherBody<R::Body>;
125
126 fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
127 match self {
128 Ok(val) => val.respond_to(req).map_into_left_body(),
129 Err(err) => HttpResponse::from_error(err.into()).map_into_right_body(),
130 }
131 }
132}
133
134impl<E> Responder for Result<(), E>
138where
139 E: Into<Error>,
140{
141 type Body = BoxBody;
142
143 fn respond_to(self, _req: &HttpRequest) -> HttpResponse {
144 match self {
145 Ok(()) => HttpResponse::new(StatusCode::NO_CONTENT),
146 Err(err) => HttpResponse::from_error(err.into()),
147 }
148 }
149}
150
151impl<R: Responder> Responder for (R, StatusCode) {
152 type Body = R::Body;
153
154 fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
155 let mut res = self.0.respond_to(req);
156 *res.status_mut() = self.1;
157 res
158 }
159}
160
161macro_rules! impl_responder_by_forward_into_base_response {
162 ($res:ty, $body:ty) => {
163 impl Responder for $res {
164 type Body = $body;
165
166 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
167 let res: actix_http::Response<_> = self.into();
168 res.into()
169 }
170 }
171 };
172
173 ($res:ty) => {
174 impl_responder_by_forward_into_base_response!($res, $res);
175 };
176}
177
178impl_responder_by_forward_into_base_response!(&'static [u8]);
179impl_responder_by_forward_into_base_response!(Vec<u8>);
180impl_responder_by_forward_into_base_response!(Bytes);
181impl_responder_by_forward_into_base_response!(BytesMut);
182
183impl_responder_by_forward_into_base_response!(&'static str);
184impl_responder_by_forward_into_base_response!(String);
185impl_responder_by_forward_into_base_response!(bytestring::ByteString);
186
187macro_rules! impl_into_string_responder {
188 ($res:ty) => {
189 impl Responder for $res {
190 type Body = String;
191
192 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
193 let string: String = self.into();
194 let res: actix_http::Response<_> = string.into();
195 res.into()
196 }
197 }
198 };
199}
200
201impl_into_string_responder!(&'_ String);
202impl_into_string_responder!(Cow<'_, str>);
203
204#[cfg(test)]
205pub(crate) mod tests {
206 use actix_http::body::to_bytes;
207 use actix_service::Service;
208
209 use super::*;
210 use crate::{
211 error,
212 http::header::{HeaderValue, CONTENT_TYPE},
213 test::{assert_body_eq, init_service, TestRequest},
214 web, App,
215 };
216
217 #[actix_rt::test]
218 async fn test_option_responder() {
219 let srv = init_service(
220 App::new()
221 .service(web::resource("/none").to(|| async { Option::<&'static str>::None }))
222 .service(web::resource("/some").to(|| async { Some("some") })),
223 )
224 .await;
225
226 let req = TestRequest::with_uri("/none").to_request();
227 let resp = srv.call(req).await.unwrap();
228 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
229
230 let req = TestRequest::with_uri("/some").to_request();
231 let resp = srv.call(req).await.unwrap();
232 assert_eq!(resp.status(), StatusCode::OK);
233 assert_body_eq!(resp, b"some");
234 }
235
236 #[actix_rt::test]
237 async fn test_responder() {
238 let req = TestRequest::default().to_http_request();
239
240 let res = "test".respond_to(&req);
241 assert_eq!(res.status(), StatusCode::OK);
242 assert_eq!(
243 res.headers().get(CONTENT_TYPE).unwrap(),
244 HeaderValue::from_static("text/plain; charset=utf-8")
245 );
246 assert_eq!(
247 to_bytes(res.into_body()).await.unwrap(),
248 Bytes::from_static(b"test"),
249 );
250
251 let res = b"test".respond_to(&req);
252 assert_eq!(res.status(), StatusCode::OK);
253 assert_eq!(
254 res.headers().get(CONTENT_TYPE).unwrap(),
255 HeaderValue::from_static("application/octet-stream")
256 );
257 assert_eq!(
258 to_bytes(res.into_body()).await.unwrap(),
259 Bytes::from_static(b"test"),
260 );
261
262 let res = "test".to_string().respond_to(&req);
263 assert_eq!(res.status(), StatusCode::OK);
264 assert_eq!(
265 res.headers().get(CONTENT_TYPE).unwrap(),
266 HeaderValue::from_static("text/plain; charset=utf-8")
267 );
268 assert_eq!(
269 to_bytes(res.into_body()).await.unwrap(),
270 Bytes::from_static(b"test"),
271 );
272
273 let res = (&"test".to_string()).respond_to(&req);
274 assert_eq!(res.status(), StatusCode::OK);
275 assert_eq!(
276 res.headers().get(CONTENT_TYPE).unwrap(),
277 HeaderValue::from_static("text/plain; charset=utf-8")
278 );
279 assert_eq!(
280 to_bytes(res.into_body()).await.unwrap(),
281 Bytes::from_static(b"test"),
282 );
283
284 let s = String::from("test");
285 let res = Cow::Borrowed(s.as_str()).respond_to(&req);
286 assert_eq!(res.status(), StatusCode::OK);
287 assert_eq!(
288 res.headers().get(CONTENT_TYPE).unwrap(),
289 HeaderValue::from_static("text/plain; charset=utf-8")
290 );
291 assert_eq!(
292 to_bytes(res.into_body()).await.unwrap(),
293 Bytes::from_static(b"test"),
294 );
295
296 let res = Cow::<'_, str>::Owned(s).respond_to(&req);
297 assert_eq!(res.status(), StatusCode::OK);
298 assert_eq!(
299 res.headers().get(CONTENT_TYPE).unwrap(),
300 HeaderValue::from_static("text/plain; charset=utf-8")
301 );
302 assert_eq!(
303 to_bytes(res.into_body()).await.unwrap(),
304 Bytes::from_static(b"test"),
305 );
306
307 let res = Cow::Borrowed("test").respond_to(&req);
308 assert_eq!(res.status(), StatusCode::OK);
309 assert_eq!(
310 res.headers().get(CONTENT_TYPE).unwrap(),
311 HeaderValue::from_static("text/plain; charset=utf-8")
312 );
313 assert_eq!(
314 to_bytes(res.into_body()).await.unwrap(),
315 Bytes::from_static(b"test"),
316 );
317
318 let res = Bytes::from_static(b"test").respond_to(&req);
319 assert_eq!(res.status(), StatusCode::OK);
320 assert_eq!(
321 res.headers().get(CONTENT_TYPE).unwrap(),
322 HeaderValue::from_static("application/octet-stream")
323 );
324 assert_eq!(
325 to_bytes(res.into_body()).await.unwrap(),
326 Bytes::from_static(b"test"),
327 );
328
329 let res = BytesMut::from(b"test".as_ref()).respond_to(&req);
330 assert_eq!(res.status(), StatusCode::OK);
331 assert_eq!(
332 res.headers().get(CONTENT_TYPE).unwrap(),
333 HeaderValue::from_static("application/octet-stream")
334 );
335 assert_eq!(
336 to_bytes(res.into_body()).await.unwrap(),
337 Bytes::from_static(b"test"),
338 );
339
340 let res = error::InternalError::new("err", StatusCode::BAD_REQUEST).respond_to(&req);
342 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
343 }
344
345 #[actix_rt::test]
346 async fn test_result_responder() {
347 let req = TestRequest::default().to_http_request();
348
349 let resp = Ok::<_, Error>("test".to_string()).respond_to(&req);
351 assert_eq!(resp.status(), StatusCode::OK);
352 assert_eq!(
353 resp.headers().get(CONTENT_TYPE).unwrap(),
354 HeaderValue::from_static("text/plain; charset=utf-8")
355 );
356 assert_eq!(
357 to_bytes(resp.into_body()).await.unwrap(),
358 Bytes::from_static(b"test"),
359 );
360
361 let res = Err::<String, _>(error::InternalError::new("err", StatusCode::BAD_REQUEST))
362 .respond_to(&req);
363
364 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
365 }
366}