mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-01 16:55:08 +02:00
refined error model (#2253)
This commit is contained in:
76
src/error/error.rs
Normal file
76
src/error/error.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use std::{error::Error as StdError, fmt};
|
||||
|
||||
use actix_http::{body::AnyBody, Response};
|
||||
|
||||
use crate::{HttpResponse, ResponseError};
|
||||
|
||||
/// General purpose actix web error.
|
||||
///
|
||||
/// An actix web error is used to carry errors from `std::error`
|
||||
/// through actix in a convenient way. It can be created through
|
||||
/// converting errors with `into()`.
|
||||
///
|
||||
/// Whenever it is created from an external object a response error is created
|
||||
/// for it that can be used to create an HTTP response from it this means that
|
||||
/// if you have access to an actix `Error` you can always get a
|
||||
/// `ResponseError` reference from it.
|
||||
pub struct Error {
|
||||
cause: Box<dyn ResponseError>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Returns the reference to the underlying `ResponseError`.
|
||||
pub fn as_response_error(&self) -> &dyn ResponseError {
|
||||
self.cause.as_ref()
|
||||
}
|
||||
|
||||
/// Similar to `as_response_error` but downcasts.
|
||||
pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> {
|
||||
<dyn ResponseError>::downcast_ref(self.cause.as_ref())
|
||||
}
|
||||
|
||||
/// Shortcut for creating an `HttpResponse`.
|
||||
pub fn error_response(&self) -> HttpResponse {
|
||||
self.cause.error_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.cause, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", &self.cause)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
// TODO: populate if replacement for Box<dyn Error> is found
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for Error {
|
||||
fn from(val: std::convert::Infallible) -> Self {
|
||||
match val {}
|
||||
}
|
||||
}
|
||||
|
||||
/// `Error` for any error that implements `ResponseError`
|
||||
impl<T: ResponseError + 'static> From<T> for Error {
|
||||
fn from(err: T) -> Error {
|
||||
Error {
|
||||
cause: Box::new(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for Response<AnyBody> {
|
||||
fn from(err: Error) -> Response<AnyBody> {
|
||||
err.error_response().into()
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use std::{cell::RefCell, fmt, io::Write as _};
|
||||
|
||||
use actix_http::{body::Body, header, Response, StatusCode};
|
||||
use actix_http::{body::Body, header, StatusCode};
|
||||
use bytes::{BufMut as _, BytesMut};
|
||||
|
||||
use crate::{Error, HttpResponse, ResponseError};
|
||||
use crate::{Error, HttpRequest, HttpResponse, Responder, ResponseError};
|
||||
|
||||
/// Wraps errors to alter the generated response status code.
|
||||
///
|
||||
@ -77,10 +77,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> Response<Body> {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
match self.status {
|
||||
InternalErrorType::Status(status) => {
|
||||
let mut res = Response::new(status);
|
||||
let mut res = HttpResponse::new(status);
|
||||
let mut buf = BytesMut::new().writer();
|
||||
let _ = write!(buf, "{}", self);
|
||||
|
||||
@ -88,20 +88,29 @@ where
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("text/plain; charset=utf-8"),
|
||||
);
|
||||
res.set_body(Body::from(buf.into_inner())).into()
|
||||
res.set_body(Body::from(buf.into_inner()))
|
||||
}
|
||||
|
||||
InternalErrorType::Response(ref resp) => {
|
||||
if let Some(resp) = resp.borrow_mut().take() {
|
||||
resp.into()
|
||||
resp
|
||||
} else {
|
||||
Response::new(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Responder for InternalError<T>
|
||||
where
|
||||
T: fmt::Debug + fmt::Display + 'static,
|
||||
{
|
||||
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
||||
HttpResponse::from_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! error_helper {
|
||||
($name:ident, $status:ident) => {
|
||||
paste::paste! {
|
||||
@ -171,134 +180,134 @@ error_helper!(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_http::{error::ParseError, Response};
|
||||
use actix_http::error::ParseError;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_internal_error() {
|
||||
let err = InternalError::from_response(ParseError::Method, HttpResponse::Ok().finish());
|
||||
let resp: Response<Body> = err.error_response();
|
||||
let resp: HttpResponse = err.error_response();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_helpers() {
|
||||
let res: Response<Body> = ErrorBadRequest("err").into();
|
||||
let res: HttpResponse = ErrorBadRequest("err").into();
|
||||
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let res: Response<Body> = ErrorUnauthorized("err").into();
|
||||
let res: HttpResponse = ErrorUnauthorized("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
let res: Response<Body> = ErrorPaymentRequired("err").into();
|
||||
let res: HttpResponse = ErrorPaymentRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PAYMENT_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorForbidden("err").into();
|
||||
let res: HttpResponse = ErrorForbidden("err").into();
|
||||
assert_eq!(res.status(), StatusCode::FORBIDDEN);
|
||||
|
||||
let res: Response<Body> = ErrorNotFound("err").into();
|
||||
let res: HttpResponse = ErrorNotFound("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let res: Response<Body> = ErrorMethodNotAllowed("err").into();
|
||||
let res: HttpResponse = ErrorMethodNotAllowed("err").into();
|
||||
assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
|
||||
let res: Response<Body> = ErrorNotAcceptable("err").into();
|
||||
let res: HttpResponse = ErrorNotAcceptable("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
|
||||
|
||||
let res: Response<Body> = ErrorProxyAuthenticationRequired("err").into();
|
||||
let res: HttpResponse = ErrorProxyAuthenticationRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PROXY_AUTHENTICATION_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorRequestTimeout("err").into();
|
||||
let res: HttpResponse = ErrorRequestTimeout("err").into();
|
||||
assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
|
||||
|
||||
let res: Response<Body> = ErrorConflict("err").into();
|
||||
let res: HttpResponse = ErrorConflict("err").into();
|
||||
assert_eq!(res.status(), StatusCode::CONFLICT);
|
||||
|
||||
let res: Response<Body> = ErrorGone("err").into();
|
||||
let res: HttpResponse = ErrorGone("err").into();
|
||||
assert_eq!(res.status(), StatusCode::GONE);
|
||||
|
||||
let res: Response<Body> = ErrorLengthRequired("err").into();
|
||||
let res: HttpResponse = ErrorLengthRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorPreconditionFailed("err").into();
|
||||
let res: HttpResponse = ErrorPreconditionFailed("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PRECONDITION_FAILED);
|
||||
|
||||
let res: Response<Body> = ErrorPayloadTooLarge("err").into();
|
||||
let res: HttpResponse = ErrorPayloadTooLarge("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
||||
|
||||
let res: Response<Body> = ErrorUriTooLong("err").into();
|
||||
let res: HttpResponse = ErrorUriTooLong("err").into();
|
||||
assert_eq!(res.status(), StatusCode::URI_TOO_LONG);
|
||||
|
||||
let res: Response<Body> = ErrorUnsupportedMediaType("err").into();
|
||||
let res: HttpResponse = ErrorUnsupportedMediaType("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
|
||||
|
||||
let res: Response<Body> = ErrorRangeNotSatisfiable("err").into();
|
||||
let res: HttpResponse = ErrorRangeNotSatisfiable("err").into();
|
||||
assert_eq!(res.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
||||
|
||||
let res: Response<Body> = ErrorExpectationFailed("err").into();
|
||||
let res: HttpResponse = ErrorExpectationFailed("err").into();
|
||||
assert_eq!(res.status(), StatusCode::EXPECTATION_FAILED);
|
||||
|
||||
let res: Response<Body> = ErrorImATeapot("err").into();
|
||||
let res: HttpResponse = ErrorImATeapot("err").into();
|
||||
assert_eq!(res.status(), StatusCode::IM_A_TEAPOT);
|
||||
|
||||
let res: Response<Body> = ErrorMisdirectedRequest("err").into();
|
||||
let res: HttpResponse = ErrorMisdirectedRequest("err").into();
|
||||
assert_eq!(res.status(), StatusCode::MISDIRECTED_REQUEST);
|
||||
|
||||
let res: Response<Body> = ErrorUnprocessableEntity("err").into();
|
||||
let res: HttpResponse = ErrorUnprocessableEntity("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
||||
|
||||
let res: Response<Body> = ErrorLocked("err").into();
|
||||
let res: HttpResponse = ErrorLocked("err").into();
|
||||
assert_eq!(res.status(), StatusCode::LOCKED);
|
||||
|
||||
let res: Response<Body> = ErrorFailedDependency("err").into();
|
||||
let res: HttpResponse = ErrorFailedDependency("err").into();
|
||||
assert_eq!(res.status(), StatusCode::FAILED_DEPENDENCY);
|
||||
|
||||
let res: Response<Body> = ErrorUpgradeRequired("err").into();
|
||||
let res: HttpResponse = ErrorUpgradeRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UPGRADE_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorPreconditionRequired("err").into();
|
||||
let res: HttpResponse = ErrorPreconditionRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PRECONDITION_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorTooManyRequests("err").into();
|
||||
let res: HttpResponse = ErrorTooManyRequests("err").into();
|
||||
assert_eq!(res.status(), StatusCode::TOO_MANY_REQUESTS);
|
||||
|
||||
let res: Response<Body> = ErrorRequestHeaderFieldsTooLarge("err").into();
|
||||
let res: HttpResponse = ErrorRequestHeaderFieldsTooLarge("err").into();
|
||||
assert_eq!(res.status(), StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
|
||||
let res: Response<Body> = ErrorUnavailableForLegalReasons("err").into();
|
||||
let res: HttpResponse = ErrorUnavailableForLegalReasons("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
|
||||
|
||||
let res: Response<Body> = ErrorInternalServerError("err").into();
|
||||
let res: HttpResponse = ErrorInternalServerError("err").into();
|
||||
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
let res: Response<Body> = ErrorNotImplemented("err").into();
|
||||
let res: HttpResponse = ErrorNotImplemented("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_IMPLEMENTED);
|
||||
|
||||
let res: Response<Body> = ErrorBadGateway("err").into();
|
||||
let res: HttpResponse = ErrorBadGateway("err").into();
|
||||
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
|
||||
|
||||
let res: Response<Body> = ErrorServiceUnavailable("err").into();
|
||||
let res: HttpResponse = ErrorServiceUnavailable("err").into();
|
||||
assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||
|
||||
let res: Response<Body> = ErrorGatewayTimeout("err").into();
|
||||
let res: HttpResponse = ErrorGatewayTimeout("err").into();
|
||||
assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);
|
||||
|
||||
let res: Response<Body> = ErrorHttpVersionNotSupported("err").into();
|
||||
let res: HttpResponse = ErrorHttpVersionNotSupported("err").into();
|
||||
assert_eq!(res.status(), StatusCode::HTTP_VERSION_NOT_SUPPORTED);
|
||||
|
||||
let res: Response<Body> = ErrorVariantAlsoNegotiates("err").into();
|
||||
let res: HttpResponse = ErrorVariantAlsoNegotiates("err").into();
|
||||
assert_eq!(res.status(), StatusCode::VARIANT_ALSO_NEGOTIATES);
|
||||
|
||||
let res: Response<Body> = ErrorInsufficientStorage("err").into();
|
||||
let res: HttpResponse = ErrorInsufficientStorage("err").into();
|
||||
assert_eq!(res.status(), StatusCode::INSUFFICIENT_STORAGE);
|
||||
|
||||
let res: Response<Body> = ErrorLoopDetected("err").into();
|
||||
let res: HttpResponse = ErrorLoopDetected("err").into();
|
||||
assert_eq!(res.status(), StatusCode::LOOP_DETECTED);
|
||||
|
||||
let res: Response<Body> = ErrorNotExtended("err").into();
|
||||
let res: HttpResponse = ErrorNotExtended("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_EXTENDED);
|
||||
|
||||
let res: Response<Body> = ErrorNetworkAuthenticationRequired("err").into();
|
||||
let res: HttpResponse = ErrorNetworkAuthenticationRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NETWORK_AUTHENTICATION_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
109
src/error/macros.rs
Normal file
109
src/error/macros.rs
Normal file
@ -0,0 +1,109 @@
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __downcast_get_type_id {
|
||||
() => {
|
||||
/// A helper method to get the type ID of the type
|
||||
/// this trait is implemented on.
|
||||
/// This method is unsafe to *implement*, since `downcast_ref` relies
|
||||
/// on the returned `TypeId` to perform a cast.
|
||||
///
|
||||
/// Unfortunately, Rust has no notion of a trait method that is
|
||||
/// unsafe to implement (marking it as `unsafe` makes it unsafe
|
||||
/// to *call*). As a workaround, we require this method
|
||||
/// to return a private type along with the `TypeId`. This
|
||||
/// private type (`PrivateHelper`) has a private constructor,
|
||||
/// making it impossible for safe code to construct outside of
|
||||
/// this module. This ensures that safe code cannot violate
|
||||
/// type-safety by implementing this method.
|
||||
///
|
||||
/// We also take `PrivateHelper` as a parameter, to ensure that
|
||||
/// safe code cannot obtain a `PrivateHelper` instance by
|
||||
/// delegating to an existing implementation of `__private_get_type_id__`
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
fn __private_get_type_id__(&self, _: PrivateHelper) -> (std::any::TypeId, PrivateHelper)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
(std::any::TypeId::of::<Self>(), PrivateHelper(()))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//Generate implementation for dyn $name
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __downcast_dyn {
|
||||
($name:ident) => {
|
||||
/// A struct with a private constructor, for use with
|
||||
/// `__private_get_type_id__`. Its single field is private,
|
||||
/// ensuring that it can only be constructed from this module
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
pub struct PrivateHelper(());
|
||||
|
||||
impl dyn $name + 'static {
|
||||
/// Downcasts generic body to a specific type.
|
||||
#[allow(dead_code)]
|
||||
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
||||
if self.__private_get_type_id__(PrivateHelper(())).0
|
||||
== std::any::TypeId::of::<T>()
|
||||
{
|
||||
// SAFETY: external crates cannot override the default
|
||||
// implementation of `__private_get_type_id__`, since
|
||||
// it requires returning a private type. We can therefore
|
||||
// rely on the returned `TypeId`, which ensures that this
|
||||
// case is correct.
|
||||
unsafe { Some(&*(self as *const dyn $name as *const T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Downcasts a generic body to a mutable specific type.
|
||||
#[allow(dead_code)]
|
||||
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
||||
if self.__private_get_type_id__(PrivateHelper(())).0
|
||||
== std::any::TypeId::of::<T>()
|
||||
{
|
||||
// SAFETY: external crates cannot override the default
|
||||
// implementation of `__private_get_type_id__`, since
|
||||
// it requires returning a private type. We can therefore
|
||||
// rely on the returned `TypeId`, which ensures that this
|
||||
// case is correct.
|
||||
unsafe { Some(&mut *(self as *const dyn $name as *const T as *mut T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
trait MB {
|
||||
__downcast_get_type_id!();
|
||||
}
|
||||
|
||||
__downcast_dyn!(MB);
|
||||
|
||||
impl MB for String {}
|
||||
impl MB for () {}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_any_casting() {
|
||||
let mut body = String::from("hello cast");
|
||||
let resp_body: &mut dyn MB = &mut body;
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast");
|
||||
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||
body.push('!');
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast!");
|
||||
let not_body = resp_body.downcast_ref::<()>();
|
||||
assert!(not_body.is_none());
|
||||
}
|
||||
}
|
@ -9,14 +9,20 @@ use url::ParseError as UrlParseError;
|
||||
|
||||
use crate::http::StatusCode;
|
||||
|
||||
#[allow(clippy::module_inception)]
|
||||
mod error;
|
||||
mod internal;
|
||||
mod macros;
|
||||
mod response_error;
|
||||
|
||||
pub use self::error::Error;
|
||||
pub use self::internal::*;
|
||||
pub use self::response_error::ResponseError;
|
||||
|
||||
/// A convenience [`Result`](std::result::Result) for Actix Web operations.
|
||||
///
|
||||
/// This type alias is generally used to avoid writing out `actix_http::Error` directly.
|
||||
pub type Result<T, E = actix_http::Error> = std::result::Result<T, E>;
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// Errors which can occur when attempting to generate resource uri.
|
||||
#[derive(Debug, PartialEq, Display, Error, From)]
|
||||
|
144
src/error/response_error.rs
Normal file
144
src/error/response_error.rs
Normal file
@ -0,0 +1,144 @@
|
||||
//! `ResponseError` trait and foreign impls.
|
||||
|
||||
use std::{
|
||||
error::Error as StdError,
|
||||
fmt,
|
||||
io::{self, Write as _},
|
||||
};
|
||||
|
||||
use actix_http::{body::AnyBody, header, Response, StatusCode};
|
||||
use bytes::BytesMut;
|
||||
|
||||
use crate::{__downcast_dyn, __downcast_get_type_id};
|
||||
use crate::{helpers, HttpResponse};
|
||||
|
||||
/// Errors that can generate responses.
|
||||
// TODO: add std::error::Error bound when replacement for Box<dyn Error> is found
|
||||
pub trait ResponseError: fmt::Debug + fmt::Display {
|
||||
/// Returns appropriate status code for error.
|
||||
///
|
||||
/// A 500 Internal Server Error is used by default. If [error_response](Self::error_response) is
|
||||
/// also implemented and does not call `self.status_code()`, then this will not be used.
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
/// Creates full response for error.
|
||||
///
|
||||
/// By default, the generated response uses a 500 Internal Server Error status code, a
|
||||
/// `Content-Type` of `text/plain`, and the body is set to `Self`'s `Display` impl.
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let mut res = HttpResponse::new(self.status_code());
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
let _ = write!(helpers::MutWriter(&mut buf), "{}", self);
|
||||
|
||||
res.headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("text/plain; charset=utf-8"),
|
||||
);
|
||||
|
||||
res.set_body(AnyBody::from(buf))
|
||||
}
|
||||
|
||||
__downcast_get_type_id!();
|
||||
}
|
||||
|
||||
__downcast_dyn!(ResponseError);
|
||||
|
||||
impl ResponseError for Box<dyn StdError + 'static> {}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl ResponseError for actix_tls::accept::openssl::SslError {}
|
||||
|
||||
impl ResponseError for serde::de::value::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for std::str::Utf8Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for std::io::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
// TODO: decide if these errors should consider not found or permission errors
|
||||
match self.kind() {
|
||||
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
|
||||
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::error::HttpError {}
|
||||
|
||||
impl ResponseError for actix_http::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
// TODO: map error kinds to status code better
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::new(self.status_code()).set_body(self.to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::header::InvalidHeaderValue {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::error::ParseError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::error::BlockingError {}
|
||||
|
||||
impl ResponseError for actix_http::error::PayloadError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
actix_http::error::PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::ws::ProtocolError {}
|
||||
|
||||
impl ResponseError for actix_http::error::ContentTypeError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::ws::HandshakeError {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
Response::from(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_error_casting() {
|
||||
use actix_http::error::{ContentTypeError, PayloadError};
|
||||
|
||||
let err = PayloadError::Overflow;
|
||||
let resp_err: &dyn ResponseError = &err;
|
||||
|
||||
let err = resp_err.downcast_ref::<PayloadError>().unwrap();
|
||||
assert_eq!(err.to_string(), "Payload reached size limit.");
|
||||
|
||||
let not_err = resp_err.downcast_ref::<ContentTypeError>();
|
||||
assert!(not_err.is_none());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user