Skip to main content

actix_web/error/
error.rs

1use std::{error::Error as StdError, fmt};
2
3use actix_http::{body::BoxBody, Response};
4
5use crate::{HttpResponse, ResponseError};
6
7/// General purpose Actix Web error.
8///
9/// An Actix Web error is used to carry errors from `std::error` through Actix in a convenient way.
10/// It can be created through converting errors with `into()`.
11///
12/// Whenever it is created from an external object a response error is created for it that can be
13/// used to create an HTTP response from it this means that if you have access to an actix `Error`
14/// you can always get a `ResponseError` reference from it.
15pub struct Error {
16    cause: Box<dyn ResponseError>,
17    response_mappers: Vec<Box<dyn Fn(HttpResponse) -> HttpResponse>>,
18}
19
20impl Error {
21    /// Returns the reference to the underlying `ResponseError`.
22    pub fn as_response_error(&self) -> &dyn ResponseError {
23        self.cause.as_ref()
24    }
25
26    /// Similar to `as_response_error` but downcasts.
27    pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> {
28        <dyn ResponseError>::downcast_ref(self.cause.as_ref())
29    }
30
31    /// Shortcut for creating an `HttpResponse`.
32    pub fn error_response(&self) -> HttpResponse {
33        let mut res = self.cause.error_response();
34
35        for mapper in &self.response_mappers {
36            res = (mapper)(res);
37        }
38
39        res
40    }
41
42    /// Adds a function that maps the HTTP response generated for this error.
43    ///
44    /// Mappers are called in the order they are added each time
45    /// [`error_response`](Self::error_response) is called. A mapper may receive a response already
46    /// modified by other mappers, so it should avoid relying on a particular position in the chain.
47    ///
48    /// Prefer narrowly mutating the provided response. Preserve fields the mapper does not own,
49    /// insert default headers only when absent, and merge list-valued headers without introducing
50    /// duplicates. Mappers should also be deterministic and safe to call more than once.
51    ///
52    /// # Good
53    ///
54    /// This mapper preserves the error response and adds a default only when it is absent:
55    ///
56    /// ```
57    /// use actix_web::{
58    ///     error,
59    ///     http::header::{self, HeaderValue},
60    /// };
61    ///
62    /// let mut err = error::ErrorBadRequest("bad request");
63    /// err.add_response_mapper(|mut res| {
64    ///     if !res.headers().contains_key(header::CACHE_CONTROL) {
65    ///         res.headers_mut()
66    ///             .insert(header::CACHE_CONTROL, HeaderValue::from_static("no-store"));
67    ///     }
68    ///
69    ///     res
70    /// });
71    ///
72    /// assert_eq!(
73    ///     err.error_response().headers().get(header::CACHE_CONTROL),
74    ///     Some(&HeaderValue::from_static("no-store")),
75    /// );
76    /// ```
77    ///
78    /// # Bad
79    ///
80    /// Replacing the response discards the original status, body, headers, extensions, and any
81    /// changes made by earlier mappers:
82    ///
83    /// ```
84    /// use actix_web::{error, HttpResponse};
85    ///
86    /// let mut err = error::ErrorBadRequest("bad request");
87    /// err.add_response_mapper(|_| HttpResponse::InternalServerError().finish());
88    /// ```
89    pub fn add_response_mapper<F>(&mut self, mapper: F)
90    where
91        F: Fn(HttpResponse) -> HttpResponse + 'static,
92    {
93        self.response_mappers.push(Box::new(mapper))
94    }
95}
96
97impl fmt::Display for Error {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        fmt::Display::fmt(&self.cause, f)
100    }
101}
102
103impl fmt::Debug for Error {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(f, "{:?}", &self.cause)
106    }
107}
108
109impl StdError for Error {
110    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
111        None
112    }
113}
114
115/// `Error` for any error that implements `ResponseError`
116impl<T: ResponseError + 'static> From<T> for Error {
117    fn from(err: T) -> Error {
118        Error {
119            cause: Box::new(err),
120            response_mappers: Vec::new(),
121        }
122    }
123}
124
125impl From<Box<dyn ResponseError>> for Error {
126    fn from(value: Box<dyn ResponseError>) -> Self {
127        Error {
128            cause: value,
129            response_mappers: Vec::new(),
130        }
131    }
132}
133
134impl From<Error> for Response<BoxBody> {
135    fn from(err: Error) -> Response<BoxBody> {
136        err.error_response().into()
137    }
138}