actix-error
Introduction
The actix-error crate, along with actix-error-derive, provides a powerful derive macro for Rust developers working with Actix-Web to easily convert enum variants into structured API errors. This library simplifies error handling in web applications by enabling automatic mapping of custom error types to HTTP responses, now with enhanced support for detailed, structured error information using the #[api_error(...)] attribute.
Features
- Automatic Conversion: Automatically convert enum variants to
ApiErrorinstances, including status codes, error kinds, and messages, using the#[api_error(...)]attribute. - Customizable Error Responses: Customize error messages, HTTP status codes (via
codeorstatus), and errorkinddirectly in enum definitions. - Support for Structured Errors: Handle errors with additional context. The
ApiErrorstruct includes adetailsfield (Option<serde_json::Value>). The derive macro automatically populates this field if an enum variant (not marked asgroup) has a single field of typeserde_json::ValueorOption<serde_json::Value>. - Message Formatting: Use the
msgattribute to define custom messages, with support for interpolating values from variant fields (e.g.,msg = "Error with {field_name}"). Useignoreon a field if it should not be part of the default message generation whenmsgis not provided, or if you handle its display manually. - Conditional
DisplayImplementation: Astd::fmt::Displayimplementation is automatically generated for your enum if any variant uses themsgattribute. If nomsgattribute is used, you should provide your ownDisplayimplementation (e.g., usingthiserror). - Group Error Handling: Aggregate related errors into groups for streamlined error management using the
groupattribute. - Integration with Actix-Web: Seamlessly integrates with Actix-Web's error handling mechanisms.
Installation
To use actix-error in your project, add the following to your Cargo.toml:
[]
= "0.x.y" # Replace with the desired version
# actix-error-derive is re-exported by actix-error, so you usually don't need to add it separately.
It's recommended to use the same version for both crates if you are using the derive macro.
Usage
Defining Errors
Use the #[derive(AsApiError)] macro on enums to define your error types. Customize each variant with #[api_error(...)] attributes to specify HTTP status codes, error messages, and more.
code = <u16>: Directly sets the HTTP status code (e.g.,code = 404).status = "<StatusCodeString>": Sets the HTTP status code based on a predefined string (e.g.,status = "NotFound"which maps to 404). If bothcodeandstatusare provided,codetakes precedence. If neither is provided, it defaults to 500.kind = "<string>": Sets a machine-readable error type. Defaults to the snake_case version of the variant name.msg = "<string>": Sets a human-readable message. Can interpolate variant fields using{field_name}for named fields or{index}for unnamed fields (e.g.,{0}). If a variant has fields andmsgis not provided, the macro attempts to generate a message from the fields unlessignoreis used.ignore: If a variant has fields and nomsgis specified, addingignoreto a field (or to the variant if it's a unit-like variant whose message should be suppressed from auto-generation) prevents it from being automatically included in the message. For fields, this is useful if they are only meant for thedetailsfield.group: Used on a variant that wraps another error type that itself implementsAsApiErrorTrait. Theas_api_error()method will be called on the wrapped error.
use AsApiError;
use ;
// Example of creating an error with details
Handling Errors in Actix-Web
Implement your Actix-Web handlers to return your custom errors. The AsApiErrorTrait (which is automatically implemented by the derive macro) ensures they are converted into appropriate HTTP responses.
use ;
# use AsApiError;
# use ;
#
#
async
async
// main function for example purposes
#
# async
Advanced Error Handling & details Field
The derive macro automatically populates the ApiError.details field if an enum variant meets these conditions:
- It is not marked with
#[api_error(group)]. - It has exactly one field.
- The type of that single field is
serde_json::ValueorOption<serde_json::Value>.
If these conditions are met, the value of this field is moved into the details field of the generated ApiError.
# use AsApiError;
# use Value; // Required for serde_json::Value
// Assuming PostgresError is some error type, and DataField is some struct/enum
// For simplicity, let's define them minimally for the example to compile
;
;
// Example of how the derive macro populates details:
// If ErrorGroupExample::RawJsonError(json!({"complex": "data"})) is returned,
// the ApiError generated will have `message: "Raw JSON error"` and
// `details: Some(json!({"complex": "data"}))`.
//
// If DetailedError::ComplexIssueWithDetails(json!({"issue_code": 123})) is returned,
// the ApiError generated will have `message: "Complex issue encountered."` and
// `details: Some(json!({"issue_code": 123}))`.
Conditional Display Trait Implementation
The actix-error-derive macro will generate a std::fmt::Display implementation for your enum if any of its variants use the #[api_error(msg = "...")] attribute.
- If
msgis used, theDisplayimplementation will use the provided message string, performing field interpolation if specified (e.g.,msg = "Error: {field}"). - If a variant does not have a
msgattribute:- And it has fields that are not
ignored, theDisplayimplementation will attempt to create a string from these fields. - And it's a unit variant (no fields), its
Displayoutput will be the variant name.
- And it has fields that are not
- If no variants in the enum use the
msgattribute, the derive macro will not generate aDisplayimplementation. In this scenario, you are responsible for providing one, for example, by also derivingthiserror::Errorwhich provides aDisplayimpl based on its own attributes. This is to avoid conflicts and give you more control whenmsgis not the primary way you define error messages.
Response Format
The ApiError struct serializes to JSON. The code field (HTTP status code) is used by Actix-Web to set the response status and is not part of the JSON body by default (due to #[serde(skip_serializing)] on ApiError.code).
Basic Error:
Error with Details:
If the details field in ApiError is Some(value), it will be included in the JSON response (due to #[serde(skip_serializing_if = "Option::is_none")] on ApiError.details).