From dc5c202c5f6ad8be0466cc2172d656e883fce648 Mon Sep 17 00:00:00 2001 From: Leon Lux Date: Sun, 17 Nov 2024 22:12:45 +0100 Subject: [PATCH] Add shorthand way to return non-IntoResponse errors (#3010) Co-authored-by: Jonas Platte --- axum-extra/Cargo.toml | 1 + axum-extra/src/response/error_response.rs | 51 +++++++++++++++++++++++ axum-extra/src/response/mod.rs | 6 +++ 3 files changed, 58 insertions(+) create mode 100644 axum-extra/src/response/error_response.rs diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 10459f70..c357975a 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -16,6 +16,7 @@ default = ["tracing", "multipart"] async-read-body = ["dep:tokio-util", "tokio-util?/io", "dep:tokio"] attachment = ["dep:tracing"] +error_response = ["dep:tracing", "tracing/std"] cookie = ["dep:cookie"] cookie-private = ["cookie", "cookie?/private"] cookie-signed = ["cookie", "cookie?/signed"] diff --git a/axum-extra/src/response/error_response.rs b/axum-extra/src/response/error_response.rs new file mode 100644 index 00000000..07069505 --- /dev/null +++ b/axum-extra/src/response/error_response.rs @@ -0,0 +1,51 @@ +use axum_core::response::{IntoResponse, Response}; +use http::StatusCode; +use std::error::Error; +use tracing::error; + +/// Convenience response to create an error response from a non-[`IntoResponse`] error +/// +/// This provides a method to quickly respond with an error that does not implement +/// the `IntoResponse` trait itself. This type should only be used for debugging purposes or internal +/// facing applications, as it includes the full error chain with descriptions, +/// thus leaking information that could possibly be sensitive. +/// +/// ```rust +/// use axum_extra::response::InternalServerError; +/// use axum_core::response::IntoResponse; +/// # use std::io::{Error, ErrorKind}; +/// # fn try_thing() -> Result<(), Error> { +/// # Err(Error::new(ErrorKind::Other, "error")) +/// # } +/// +/// async fn maybe_error() -> Result> { +/// try_thing().map_err(InternalServerError)?; +/// // do something on success +/// # Ok(String::from("ok")) +/// } +/// ``` +#[derive(Debug)] +pub struct InternalServerError(pub T); + +impl IntoResponse for InternalServerError { + fn into_response(self) -> Response { + error!(error = &self.0 as &dyn Error); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "An error occurred while processing your request.", + ) + .into_response() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::{Error, ErrorKind}; + + #[test] + fn internal_server_error() { + let response = InternalServerError(Error::new(ErrorKind::Other, "Test")).into_response(); + assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); + } +} diff --git a/axum-extra/src/response/mod.rs b/axum-extra/src/response/mod.rs index 12e104d8..bac7d040 100644 --- a/axum-extra/src/response/mod.rs +++ b/axum-extra/src/response/mod.rs @@ -9,6 +9,12 @@ mod attachment; #[cfg(feature = "multipart")] pub mod multiple; +#[cfg(feature = "error_response")] +mod error_response; + +#[cfg(feature = "error_response")] +pub use error_response::InternalServerError; + #[cfg(feature = "erased-json")] pub use erased_json::ErasedJson;