2 releases (1 stable)

1.0.0 Apr 5, 2023
0.1.0 Apr 5, 2023

#1112 in HTTP server

Apache-2.0

17KB
310 lines

Error handling helpers for axum while using anyhow, focusing on how to safely unwrap Result<T, E> and Option<T> in an ergonomic way.

Usage

There are two recommended approaches to implementing recoil for error handling in your axum server application.

Both approaches take advantage of the fact that, because recoil::Failure (and anything that implements recoil::ErrorResponder) generates a type that implements axum::response::IntoResponse, you can call .into_response and return it as a http::response::Response to axum.

If-Let-Err Pattern

For each fallible handler, return Response.

When encountering a Result or an Option, use if let to handle the error branch, and then Failure (or anything that implements ErrorResponder), to manually generate a response for the error.

use std::fs::write;
use recoil::{ErrorResponder, Failure};
use axum::{
    response::{IntoResponse, Response},
    http::StatusCode
};

fn your_handler() -> Response {
    if let Err(error) = write("/root/warning.txt", "Big brother is watching you.") {
        return Failure::fail_because("Failed to write to path /root/warning.txt on file system.", error.into(), None).into_response();
    }

    // the ok branch continues until the end of the handler.
    (StatusCode::CREATED, "Handled successfully.").into_response()
}

// ...rest of your server application code that integrates `your_handler()`.

This pattern however, is basic, and creates a lot of boilerplate code for catching error branches. Although a good starting point, you should use the more efficient "catch-method pattern," when handling errors in bulk.

Catch-Method Pattern

For each fallible handler, return Result<impl IntoResponse, Response>.

When encountering a Result or an Option, use .recoil() to handle the error branch, with the trait recoil::Recoil in scope, followed by the ? operator, to automatically generate a response for the error.

use std::fs::write;
use recoil::{Failure, Recoil};
use axum::{
    response::{IntoResponse, Response},
    http::StatusCode
};

fn your_handler() -> Result<impl IntoResponse, Response> {
    write("/root/warning.txt", "Big brother is watching you.")
        .recoil::<Failure>(Some("Failed to write to path /root/warning.txt on file system."), None)?;

    // the ok branch continues until the end of the handler.
    Ok((StatusCode::CREATED, "Handled successfully."))
}

// ...rest of your server application code that integrates `your_handler()`.

Customization

If the included error response structure does not fit your needs, you can write your own, as long as it implements the required traits and methods for ErrorResponder.

Feel free to make use of recoil::trace_error() to produce error message sequences for your structure.

Custom responders can be used with Recoil::recoil by specifying the responder as the generic, in place of the included Failure structure.

Headers

recoil deliberately excludes the means to customize headers of error responses, for simplicity's sake.

Consider the following instead, if you want custom headers:

  • Add middleware in your application to inject headers.
  • Don't use this library and write your own implementation.

http::response::Response (which is the structure produced by IntoResponse) has the method .headers_mut(), which returns a mutable reference to the response's internal HeaderMap, and is where you can inject your headers.

Dependencies

~6–8MB
~146K SLoC