Rust's error handling differs from languages like Java—it doesn't have constructs like try...catch. The conventional approach is to define a global error handling type at the application level:
Here, the thiserror crate is used, which conveniently helps define custom error types and simplifies code. For brevity, an AppResult type is also defined.
In Rust's error handling ecosystem, two commonly used crates are thiserror and anyhow:
thiserror: Suitable for library developers to define clear error types. It uses derive macros to help implement the std::error::Error trait for custom error types while allowing customization of error display. When building a library or needing to provide users with well-defined error types, thiserror is the better choice.
anyhow: Designed for application developers, providing a generic error type anyhow::Error that can encapsulate any error implementing the std::error::Error trait. It focuses more on error propagation rather than definition, making it ideal for application-layer code. It allows quick conversion of various errors into anyhow::Error, reducing boilerplate.
In some scenarios, you might use both crates: thiserror for defining error types in libraries and anyhow for handling and propagating those errors in applications.
In Salvo, Handlers often encounter various errors, such as database connection failures, file access issues, or network errors. For these cases, the aforementioned error handling approach can be applied:
Here, home directly returns an AppResult<()>. But how should this error be displayed? We need to implement the Writer trait for the custom AppResult error type, where we can define error presentation:
In Salvo, a Handler can return a Result as long as both the Ok and Err variants implement the Writer trait.
Given the widespread use of anyhow, Salvo provides built-in support for anyhow::Error. When the anyhow feature is enabled, anyhow::Error implements the Writer trait and is mapped to InternalServerError:
To use the anyhow feature, enable it in Cargo.toml:
This allows your handler functions to directly return anyhow::Result<T>:
Errors often contain sensitive information that shouldn't be exposed to regular users—doing so would be insecure and a privacy breach. However, developers or administrators might prefer seeing raw error details for debugging.
Since the write method provides access to Request and Depot references, implementing conditional error display becomes straightforward:
Salvo's built-in error pages suffice for most cases, rendering Html, Json, or Xml based on the request's data type. However, there may be scenarios where custom error page displays are desired.
This can be achieved by implementing a custom Catcher. For detailed instructions, refer to the Catcher section.