actix_web/response/
responder.rs

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
13/// Trait implemented by types that can be converted to an HTTP response.
14///
15/// Any types that implement this trait can be used in the return type of a handler. Since handlers
16/// will only have one return type, it is idiomatic to use opaque return types `-> impl Responder`.
17///
18/// # Implementations
19/// It is often not required to implement `Responder` for your own types due to a broad base of
20/// built-in implementations:
21/// - `HttpResponse` and `HttpResponseBuilder`
22/// - `Option<R>` where `R: Responder`
23/// - `Result<R, E>` where `R: Responder` and [`E: ResponseError`](crate::ResponseError)
24/// - `(R, StatusCode)` where `R: Responder`
25/// - `&'static str`, `String`, `&'_ String`, `Cow<'_, str>`, [`ByteString`](bytestring::ByteString)
26/// - `&'static [u8]`, `Vec<u8>`, `Bytes`, `BytesMut`
27/// - [`Json<T>`](crate::web::Json) and [`Form<T>`](crate::web::Form) where `T: Serialize`
28/// - [`Either<L, R>`](crate::web::Either) where `L: Serialize` and `R: Serialize`
29/// - [`CustomizeResponder<R>`]
30/// - [`actix_files::NamedFile`](https://docs.rs/actix-files/latest/actix_files/struct.NamedFile.html)
31/// - [Experimental responders from `actix-web-lab`](https://docs.rs/actix-web-lab/latest/actix_web_lab/respond/index.html)
32/// - Third party integrations may also have implemented `Responder` where appropriate. For example,
33///   HTML templating engines.
34///
35/// # Customizing Responder Output
36/// Calling [`.customize()`](Responder::customize) on any responder type will wrap it in a
37/// [`CustomizeResponder`] capable of overriding various parts of the response such as the status
38/// code and header map.
39pub trait Responder {
40    type Body: MessageBody + 'static;
41
42    /// Convert self to `HttpResponse`.
43    fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body>;
44
45    /// Wraps responder to allow alteration of its response.
46    ///
47    /// See [`CustomizeResponder`] docs for more details on its capabilities.
48    ///
49    /// # Examples
50    /// ```
51    /// use actix_web::{Responder, http::StatusCode, test::TestRequest};
52    ///
53    /// let responder = "Hello world!"
54    ///     .customize()
55    ///     .with_status(StatusCode::BAD_REQUEST)
56    ///     .insert_header(("x-hello", "world"));
57    ///
58    /// let request = TestRequest::default().to_http_request();
59    /// let response = responder.respond_to(&request);
60    /// assert_eq!(response.status(), StatusCode::BAD_REQUEST);
61    /// assert_eq!(response.headers().get("x-hello").unwrap(), "world");
62    /// ```
63    #[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
134// Note: see https://github.com/actix/actix-web/issues/1108 for reasoning why Responder is not
135// implemented for `()`, and https://github.com/actix/actix-web/pull/3560 for discussion about this
136// impl and the decision not to include a similar one for `Option<()>`.
137impl<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        // InternalError
341        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        // Result<I, E>
350        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}