diff --git a/guide/src/qs_10.md b/guide/src/qs_10.md index c69d26f68..9a4addba1 100644 --- a/guide/src/qs_10.md +++ b/guide/src/qs_10.md @@ -210,3 +210,37 @@ fn main() { # let _ = sys.run(); } ``` + +## Error handlers + +`ErrorHandlers` middleware allows to provide custom handlers for responses. + +You can use `ErrorHandlers::handler()` method to register a custom error handler +for specific status code. You can modify existing response or create completly new +one. Error handler can return response immediately or return future that resolves +to a response. + +```rust +# extern crate actix_web; +use actix_web::{ + App, HttpRequest, HttpResponse, Result, + http, middleware::Response, middleware::ErrorHandlers}; + +fn render_500(_: &mut HttpRequest, resp: HttpResponse) -> Result { + let mut builder = resp.into_builder(); + builder.header(http::header::CONTENT_TYPE, "application/json"); + Ok(Response::Done(builder.into())) +} + +fn main() { + let app = App::new() + .middleware( + ErrorHandlers::new() + .handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500)) + .resource("/test", |r| { + r.method(http::Method::GET).f(|_| HttpResponse::Ok()); + r.method(http::Method::HEAD).f(|_| HttpResponse::MethodNotAllowed()); + }) + .finish(); +} +``` diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs new file mode 100644 index 000000000..2906e39ed --- /dev/null +++ b/src/middleware/errhandlers.rs @@ -0,0 +1,115 @@ +use std::collections::HashMap; + +use error::Result; +use http::StatusCode; +use httprequest::HttpRequest; +use httpresponse::HttpResponse; +use middleware::{Middleware, Response}; + + +type ErrorHandler = Fn(&mut HttpRequest, HttpResponse) -> Result; + +/// `Middleware` for allowing custom handlers for responses. +/// +/// You can use `ErrorHandlers::handler()` method to register a custom error handler +/// for specific status code. You can modify existing response or create completly new +/// one. +/// +/// ## Example +/// +/// ```rust +/// # extern crate actix_web; +/// use actix_web::{ +/// App, HttpRequest, HttpResponse, Result, +/// http, middleware::Response, middleware::ErrorHandlers}; +/// +/// fn render_500(_: &mut HttpRequest, resp: HttpResponse) -> Result { +/// let mut builder = resp.into_builder(); +/// builder.header(http::header::CONTENT_TYPE, "application/json"); +/// Ok(Response::Done(builder.into())) +/// } +/// +/// fn main() { +/// let app = App::new() +/// .middleware( +/// ErrorHandlers::new() +/// .handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500)) +/// .resource("/test", |r| { +/// r.method(http::Method::GET).f(|_| HttpResponse::Ok()); +/// r.method(http::Method::HEAD).f(|_| HttpResponse::MethodNotAllowed()); +/// }) +/// .finish(); +/// } +/// ``` +pub struct ErrorHandlers { + handlers: HashMap>>, +} + +impl Default for ErrorHandlers { + fn default() -> Self { + ErrorHandlers { + handlers: HashMap::new(), + } + } +} + +impl ErrorHandlers { + + /// Construct new `ErrorHandlers` instance + pub fn new() -> Self { + ErrorHandlers::default() + } + + /// Register error handler for specified status code + pub fn handler(mut self, status: StatusCode, handler: F) -> Self + where F: Fn(&mut HttpRequest, HttpResponse) -> Result + 'static + { + self.handlers.insert(status, Box::new(handler)); + self + } +} + +impl Middleware for ErrorHandlers { + + fn response(&self, req: &mut HttpRequest, resp: HttpResponse) -> Result { + if let Some(handler) = self.handlers.get(&resp.status()) { + handler(req, resp) + } else { + Ok(Response::Done(resp)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use http::StatusCode; + use http::header::CONTENT_TYPE; + + fn render_500(_: &mut HttpRequest, resp: HttpResponse) -> Result { + let mut builder = resp.into_builder(); + builder.header(CONTENT_TYPE, "0001"); + Ok(Response::Done(builder.into())) + } + + #[test] + fn test_handler() { + let mw = ErrorHandlers::new() + .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); + + let mut req = HttpRequest::default(); + let resp = HttpResponse::InternalServerError().finish(); + let resp = match mw.response(&mut req, resp) { + Ok(Response::Done(resp)) => resp, + _ => panic!(), + }; + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + + let resp = HttpResponse::Ok().finish(); + let resp = match mw.response(&mut req, resp) { + Ok(Response::Done(resp)) => resp, + _ => panic!(), + }; + assert!(!resp.headers().contains_key(CONTENT_TYPE)); + } +} diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 6f85dfeb5..97f3fa2b0 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -10,9 +10,11 @@ mod logger; #[cfg(feature = "session")] mod session; mod defaultheaders; +mod errhandlers; pub mod cors; pub mod csrf; pub use self::logger::Logger; +pub use self::errhandlers::ErrorHandlers; pub use self::defaultheaders::{DefaultHeaders, DefaultHeadersBuilder}; #[cfg(feature = "session")]