mirror of
https://github.com/actix/actix-website
synced 2024-12-01 11:30:06 +01:00
134 lines
5.2 KiB
Markdown
134 lines
5.2 KiB
Markdown
---
|
|
title: Errors
|
|
menu: docs_advanced
|
|
weight: 180
|
|
---
|
|
|
|
# Errors
|
|
|
|
Actix-web uses its own [`actix_web::error::Error`][actixerror] type and
|
|
[`actix_web::error::ResponseError`][responseerror] trait for error handling from web handlers.
|
|
|
|
If a handler returns an `Error` (referring to the [general Rust trait
|
|
`std::error::Error`][stderror]) in a `Result` that also implements the `ResponseError` trait,
|
|
actix-web will render that error as an HTTP response with it's corresponding
|
|
[`actix_web::http::StatusCode`][status_code]. Internal server error is generated by default:
|
|
|
|
```rust
|
|
pub trait ResponseError {
|
|
fn error_response(&self) -> Response<Body>;
|
|
fn status_code(&self) -> StatusCode;
|
|
}
|
|
```
|
|
|
|
A `Responder` coerces compatible `Result`s into HTTP responses:
|
|
|
|
```rust
|
|
impl<T: Responder, E: Into<Error>> Responder for Result<T, E>
|
|
```
|
|
|
|
`Error` in the code above is actix-web's error definition, and any errors that implement
|
|
`ResponseError` can be converted to one automatically.
|
|
|
|
Actix-web provides `ResponseError` implementations for some common non-actix errors. For example, if
|
|
a handler responds with an `io::Error`, that error is converted into an `HttpInternalServerError`:
|
|
|
|
```rust
|
|
use std::io;
|
|
use actix_files::NamedFile;
|
|
|
|
fn index(_req: HttpRequest) -> io::Result<NamedFile> {
|
|
Ok(NamedFile::open("static/index.html")?)
|
|
}
|
|
```
|
|
|
|
See [the actix-web API documentation][responseerrorimpls] for a full list of foreign implementations
|
|
for `ResponseError`.
|
|
|
|
## An example of a custom error response
|
|
|
|
Here's an example implementation for `ResponseError`, using the [derive_more] crate
|
|
for declarative error enums.
|
|
|
|
{{< 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 `index` handler executes above.
|
|
|
|
Override `error_response()` to produce more useful results:
|
|
|
|
{{< include-example example="errors" file="override_error.rs" section="override" >}}
|
|
|
|
# Error helpers
|
|
|
|
Actix-web provides a set of error helper functions that are useful for generating 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`:
|
|
|
|
{{< include-example example="errors" file="helpers.rs" section="helpers" >}}
|
|
|
|
See the [API documentation for actix-web's `error` module][actixerror] for a full list of available
|
|
error helpers.
|
|
|
|
# Error logging
|
|
|
|
Actix logs all errors at the `WARN` log level. If an application's log level is set to `DEBUG` and
|
|
`RUST_BACKTRACE` is enabled, the backtrace is also logged. These are configurable with environmental
|
|
variables:
|
|
|
|
```
|
|
>> RUST_BACKTRACE=1 RUST_LOG=actix_web=debug cargo run
|
|
```
|
|
|
|
The `Error` type uses the cause's error backtrace if available. If the underlying failure does not
|
|
provide a backtrace, a new backtrace is constructed pointing to the point where the conversion
|
|
occurred (rather than the origin of the error).
|
|
|
|
# Recommended practices in error handling
|
|
|
|
It might be useful to think about dividing the errors an application produces into two broad groups:
|
|
those which are intended to be user-facing, and those which are not.
|
|
|
|
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:
|
|
|
|
{{< 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.
|
|
|
|
However, sending back an error's message isn't desirable for all errors -- there are many failures
|
|
that occur in a server environment where we'd probably want the specifics to be hidden from the
|
|
user. For example, if a database goes down and client libraries start producing connect timeout
|
|
errors, or if an HTML template was improperly formatted and errors when rendered. In these cases, it
|
|
might be preferable to map the errors to a generic error suitable for user consumption.
|
|
|
|
Here's an example that maps an internal error to a user-facing `InternalError` with a custom
|
|
message:
|
|
|
|
{{< 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 application internals which they weren't meant
|
|
to see.
|
|
|
|
# Error Logging
|
|
|
|
This is a basic example using `middleware::Logger` which depends on `env_logger` and `log`:
|
|
```toml
|
|
[dependencies]
|
|
env_logger = "0.8"
|
|
log = "0.4"
|
|
```
|
|
|
|
{{< include-example example="errors" file="logging.rs" section="logging" >}}
|
|
|
|
[actixerror]: https://docs.rs/actix-web/3/actix_web/error/struct.Error.html
|
|
[errorhelpers]: https://docs.rs/actix-web/3/actix_web/trait.ResponseError.html
|
|
[derive_more]: https://crates.io/crates/derive_more
|
|
[responseerror]: https://docs.rs/actix-web/3/actix_web/error/trait.ResponseError.html
|
|
[responseerrorimpls]:
|
|
https://docs.rs/actix-web/3/actix_web/error/trait.ResponseError.html#foreign-impls
|
|
[stderror]: https://doc.rust-lang.org/std/error/trait.Error.html
|
|
[status_code]: https://docs.rs/actix-web/3.0.0/actix_web/http/struct.StatusCode.html
|