Error Handling
10/30/24About 1 min
Unified Error Handling System (Error and Result<T>)
To build a robust, maintainable, and developer-friendly application, we established a unified error handling system. The core is a custom enum Error and a global type Result<T> = std::result::Result<T, Error>.
Goals:
- Unify Error Types: Convert errors from different libraries into our own
Errortype - Simplify Error Propagation: Use Rust's
?operator for easy error propagation - Automatic API Response Conversion: Seamlessly integrate with
ApiResponse
Core Workflow
- In Service layer, all fallible functions return
Result<T> - Use
?to propagate errors from underlying libraries - In Handler layer, call
ApiResponse::from_result()on the Service's Result - The system handles everything else!
Core Definition: enum Error
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Message(String), // Generic error (defaults to 500)
WithStatus(StatusCode, String), // Business error with specific HTTP status
}Practical Example: Service to Handler Flow
Service Layer
pub async fn edit(arg: SysUserEdit) -> Result<SysUserModel> {
let db = DB().await;
let rmodel = sys_user::Entity::find_by_id(arg.id).one(db).await?;
let model = rmodel.ok_or_else(|| Error::not_found(format!("User {} not found", arg.id)))?;
let mut amodel: sys_user::ActiveModel = model.into();
amodel.user_name = Set(arg.user_name);
// ... more fields ...
Ok(amodel.update(db).await?)
}Handler Layer
pub async fn edit_user(VJson(arg): VJson<SysUserEdit>) -> Response {
ApiResponse::from_result(SysUserService::edit(arg).await)
}Error Conversion Between Libraries
We use a macro to auto-convert errors from different libraries:
macro_rules! impl_error_from {
($($error_type:ty),*) => {
$(
impl From<$error_type> for Error {
fn from(err: $error_type) -> Self {
Error::Message(err.to_string())
}
}
)*
};
}
impl_error_from!(
sea_orm::DbErr,
redis::RedisError,
// ...
);Summary & Best Practices
Important Guidelines
Service Layer:
- Must return
Result<T> - Use
?for unexpected errors - Use helper functions like
Error::not_found(),Error::bad_request()for business errors - Use
.ok_or_else(|| Error::not_found(...)))?to convertOptiontoResult
- Must return
Handler Layer:
- Must return
Response - Should use
ApiResponse::from_result()for Service Results - Handlers should not contain error-handling logic
- Must return