From f07c78a5ca5490e9cb9aa969776ae01ecd208424 Mon Sep 17 00:00:00 2001 From: Cameron Dershem Date: Mon, 17 Jun 2019 13:46:21 -0400 Subject: [PATCH] First pass at errors section. --- content/docs/errors.md | 112 ++------------------------ examples/Cargo.toml | 1 + examples/errors/Cargo.toml | 8 ++ examples/errors/src/helpers.rs | 14 ++++ examples/errors/src/main.rs | 21 +++++ examples/errors/src/override_error.rs | 30 +++++++ examples/errors/src/recommend_one.rs | 20 +++++ examples/errors/src/recommend_two.rs | 25 ++++++ 8 files changed, 124 insertions(+), 107 deletions(-) create mode 100644 examples/errors/Cargo.toml create mode 100644 examples/errors/src/helpers.rs create mode 100644 examples/errors/src/main.rs create mode 100644 examples/errors/src/override_error.rs create mode 100644 examples/errors/src/recommend_one.rs create mode 100644 examples/errors/src/recommend_two.rs diff --git a/content/docs/errors.md b/content/docs/errors.md index f8683c4..ace3a4c 100644 --- a/content/docs/errors.md +++ b/content/docs/errors.md @@ -52,22 +52,7 @@ foreign implementations for `ResponseError`. Here's an example implementation for `ResponseError`: -```rust -use actix_web::*; - -#[derive(Fail, Debug)] -#[fail(display="my error")] -struct MyError { - name: &'static str -} - -// Use default implementation for `error_response()` method -impl error::ResponseError for MyError {} - -fn index(req: &HttpRequest) -> Result<&'static str, MyError> { - Err(MyError{name: "test"}) -} -``` +{{< include-example example="errors" file="main.rs" section="response-error" >}} `ResponseError` has a default implementation for `error_response()` that will render a *500* (internal server error), and that's what will happen when the @@ -75,37 +60,7 @@ render a *500* (internal server error), and that's what will happen when the Override `error_response()` to produce more useful results: -```rust -#[macro_use] extern crate failure; -use actix_web::{App, HttpRequest, HttpResponse, http, error}; - -#[derive(Fail, Debug)] -enum MyError { - #[fail(display="internal error")] - InternalError, - #[fail(display="bad request")] - BadClientData, - #[fail(display="timeout")] - Timeout, -} - -impl error::ResponseError for MyError { - fn error_response(&self) -> HttpResponse { - match *self { - MyError::InternalError => HttpResponse::new( - http::StatusCode::INTERNAL_SERVER_ERROR), - MyError::BadClientData => HttpResponse::new( - http::StatusCode::BAD_REQUEST), - MyError::Timeout => HttpResponse::new( - http::StatusCode::GATEWAY_TIMEOUT), - } - } -} - -fn index(req: &HttpRequest) -> Result<&'static str, MyError> { - Err(MyError::BadClientData) -} -``` +{{< include-example example="errors" file="override_error.rs" section="override" >}} # Error helpers @@ -114,21 +69,7 @@ specific HTTP error codes from other errors. Here we convert `MyError`, which doesn't implement the `ResponseError` trait, to a *400* (bad request) using `map_err`: -```rust -# extern crate actix_web; -use actix_web::*; - -#[derive(Debug)] -struct MyError { - name: &'static str -} - -fn index(req: &HttpRequest) -> Result<&'static str> { - let result: Result<&'static str, MyError> = Err(MyError{name: "test"}); - - Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?) -} -``` +{{< include-example example="errors" file="helpers.rs" section="helpers" >}} See the [API documentation for actix-web's `error` module][errorhelpers] for a full list of available error helpers. @@ -165,27 +106,7 @@ An example of the former is that I might use failure to specify a `UserError` enum which encapsulates a `ValidationError` to return whenever a user sends bad input: -```rust -#[macro_use] extern crate failure; -use actix_web::{HttpResponse, http, error}; - -#[derive(Fail, Debug)] -enum UserError { - #[fail(display="Validation error on field: {}", field)] - ValidationError { - field: String, - } -} - -impl error::ResponseError for UserError { - fn error_response(&self) -> HttpResponse { - match *self { - UserError::ValidationError { .. } => HttpResponse::new( - http::StatusCode::BAD_REQUEST), - } - } -} -``` +{{< include-example example="errors" file="recommend_one.rs" section="recommend-one" >}} This will behave exactly as intended because the error message defined with `display` is written with the explicit intent to be read by a user. @@ -201,30 +122,7 @@ consumption. Here's an example that maps an internal error to a user-facing `InternalError` with a custom message: -```rust -#[macro_use] extern crate failure; -use actix_web::{App, HttpRequest, HttpResponse, http, error, fs}; - -#[derive(Fail, Debug)] -enum UserError { - #[fail(display="An internal error occurred. Please try again later.")] - InternalError, -} - -impl error::ResponseError for UserError { - fn error_response(&self) -> HttpResponse { - match *self { - UserError::InternalError => HttpResponse::new( - http::StatusCode::INTERNAL_SERVER_ERROR), - } - } -} - -fn index(_: &HttpRequest) -> Result<&'static str, UserError> { - fs::NamedFile::open("static/index.html").map_err(|_e| UserError::InternalError)?; - Ok("success!") -} -``` +{{< include-example example="errors" file="recommend_two.rs" section="recommend-two" >}} By dividing errors into those which are user facing and those which are not, we can ensure that we don't accidentally expose users to errors thrown by diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 9ee6417..4a6c8ae 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -17,4 +17,5 @@ exclude = [ "async-handlers", "extractors", "autoreload", + "errors" ] diff --git a/examples/errors/Cargo.toml b/examples/errors/Cargo.toml new file mode 100644 index 0000000..1314e8f --- /dev/null +++ b/examples/errors/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "errors" +version = "0.1.0" +edition = "2018" + +[dependencies] +actix-web = "1.0" +failure = "0.1" diff --git a/examples/errors/src/helpers.rs b/examples/errors/src/helpers.rs new file mode 100644 index 0000000..9fcd86a --- /dev/null +++ b/examples/errors/src/helpers.rs @@ -0,0 +1,14 @@ +// +use actix_web::{error, HttpRequest, Result}; + +#[derive(Debug)] +struct MyError { + name: &'static str, +} + +fn index(req: &HttpRequest) -> Result<&'static str> { + let result: Result<&'static str, MyError> = Err(MyError { name: "test" }); + + Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?) +} +// diff --git a/examples/errors/src/main.rs b/examples/errors/src/main.rs new file mode 100644 index 0000000..63e09b9 --- /dev/null +++ b/examples/errors/src/main.rs @@ -0,0 +1,21 @@ +mod helpers; +mod override_error; +mod recommend_one; +// +use actix_web::{error, HttpRequest}; +use failure::Fail; + +#[derive(Fail, Debug)] +#[fail(display = "my error")] +struct MyError { + name: &'static str, +} + +// Use default implementation for `error_response()` method +impl error::ResponseError for MyError {} + +fn index(req: HttpRequest) -> Result<&'static str, MyError> { + Err(MyError { name: "test" }) +} +// +fn main() {} diff --git a/examples/errors/src/override_error.rs b/examples/errors/src/override_error.rs new file mode 100644 index 0000000..a5eaea3 --- /dev/null +++ b/examples/errors/src/override_error.rs @@ -0,0 +1,30 @@ +// +use actix_web::{error, http, HttpRequest, HttpResponse}; +use failure::Fail; + +#[derive(Fail, Debug)] +enum MyError { + #[fail(display = "internal error")] + InternalError, + #[fail(display = "bad request")] + BadClientData, + #[fail(display = "timeout")] + Timeout, +} + +impl error::ResponseError for MyError { + fn error_response(&self) -> HttpResponse { + match *self { + MyError::InternalError => { + HttpResponse::new(http::StatusCode::INTERNAL_SERVER_ERROR) + } + MyError::BadClientData => HttpResponse::new(http::StatusCode::BAD_REQUEST), + MyError::Timeout => HttpResponse::new(http::StatusCode::GATEWAY_TIMEOUT), + } + } +} + +fn index(req: &HttpRequest) -> Result<&'static str, MyError> { + Err(MyError::BadClientData) +} +// diff --git a/examples/errors/src/recommend_one.rs b/examples/errors/src/recommend_one.rs new file mode 100644 index 0000000..51b6131 --- /dev/null +++ b/examples/errors/src/recommend_one.rs @@ -0,0 +1,20 @@ +// +use actix_web::{error, http, HttpResponse}; +use failure::Fail; + +#[derive(Fail, Debug)] +enum UserError { + #[fail(display = "Validation error on field: {}", field)] + ValidationError { field: String }, +} + +impl error::ResponseError for UserError { + fn error_response(&self) -> HttpResponse { + match *self { + UserError::ValidationError { .. } => { + HttpResponse::new(http::StatusCode::BAD_REQUEST) + } + } + } +} +// diff --git a/examples/errors/src/recommend_two.rs b/examples/errors/src/recommend_two.rs new file mode 100644 index 0000000..80e5f1f --- /dev/null +++ b/examples/errors/src/recommend_two.rs @@ -0,0 +1,25 @@ +// +use actix_web::{error, fs, http, App, HttpRequest, HttpResponse}; +use failure::Fail; + +#[derive(Fail, Debug)] +enum UserError { + #[fail(display = "An internal error occurred. Please try again later.")] + InternalError, +} + +impl error::ResponseError for UserError { + fn error_response(&self) -> HttpResponse { + match *self { + UserError::InternalError => { + HttpResponse::new(http::StatusCode::INTERNAL_SERVER_ERROR) + } + } + } +} + +fn index(_req: HttpRequest) -> Result<&'static str, UserError> { + fs::NamedFile::open("static/index.html").map_err(|_e| UserError::InternalError)?; + Ok("success!") +} +//