anycms-core 0.5.4

A unified API response library supporting multiple Rust web frameworks
Documentation
# 错误处理集成指南

本文档介绍 how to 使用 `anycms-core` 的 thiserror 错误处理功能。

## 概述

`anycms-core` 现在提供了完整的错误处理集成,支持:

- **ErrorCode**: 标准化的错误码枚举,自动映射到 HTTP 状态码
- **ApiError**: 简单的 API 错误类型
- **AppError**: 通用的应用错误类型(使用 thiserror)
- **IntoApiResult trait**: 便捷的 Result 转换

## 核心类型

### ErrorCode

```rust
pub enum ErrorCode {
    Success = 0,
    BadRequest = 400,
    Unauthorized = 401,
    Forbidden = 403,
    NotFound = 404,
    Conflict = 409,
    ValidationError = 422,
    InternalError = 500,
    // ...
}
```

### ApiError

简单直接的错误类型:

```rust
use anycms_core::{ApiError, ErrorCode};

// 使用错误码
let err = ApiError::new(ErrorCode::NotFound, "User not found");

// 使用便捷方法
let err = ApiError::not_found("User not found");
let err = ApiError::bad_request("Invalid email");
let err = ApiError::unauthorized("Please login");
```

### AppError

使用 thiserror 的通用错误类型:

```rust
use anycms_core::AppError;
use thiserror::Error;

#[derive(Error, Debug)]
enum MyError {
    #[error("Database error: {0}")]
    Database(String),
}

// 自动转换
let result: Result<User, MyError> = ...;
let api_result: ApiResult<User> = result.into(); // 自动转换为 ApiResult
```

## 使用模式

### 模式 1: 使用 ApiError

```rust
use anycms_core::{ApiResult, ApiError};

async fn get_user(id: u32) -> Result<User, UserServiceError> {
    // ...
}

#[get("/users/{id}")]
async fn handler(path: web::Path<u32>) -> ApiResult<User> {
    let user_id = path.into_inner();

    get_user(user_id)
        .await
        .map(|user| ApiResult::value(user))
        .unwrap_or_else(|e| ApiError::from(e).into())
}
```

### 模式 2: 使用 IntoApiResult trait

```rust
use anycms_core::{ApiResult, ApiError, IntoApiResult};

async fn find_user(id: u32) -> Result<User, UserServiceError> {
    // ...
}

#[get("/users/{id}")]
async fn handler(path: web::Path<u32>) -> ApiResult<User> {
    let user_id = path.into_inner();

    find_user(user_id)
        .await
        .into_api_result_with(|| {
            ApiError::not_found(format!("User {} not found", user_id))
        })
}
```

### 模式 3: 自定义错误类型

```rust
use thiserror::Error;
use anycms_core::{ApiError, ApiResult};

#[derive(Error, Debug)]
enum UserServiceError {
    #[error("User {0} not found")]
    UserNotFound(u32),

    #[error("Email {0} already exists")]
    EmailAlreadyExists(String),
}

impl From<UserServiceError> for ApiError {
    fn from(err: UserServiceError) -> Self {
        match err {
            UserServiceError::UserNotFound(id) => {
                ApiError::not_found(format!("User {} not found", id))
            }
            UserServiceError::EmailAlreadyExists(email) => {
                ApiError::conflict(format!("Email {} already registered", email))
            }
        }
    }
}

// 使用
async fn handler() -> ApiResult<User> {
    find_user(1)
        .await
        .map(|user| ApiResult::value(user))
        .unwrap_or_else(|e| ApiError::from(e).into())
}
```

### 模式 4: Early Return

```rust
#[post("/users")]
async fn create_user(data: web::Json<CreateUserRequest>) -> ApiResult<User> {
    // 验证
    let validation = validate_email(&data.email).await;
    if let Err(e) = validation {
        return ApiError::from(e).into();
    }

    // 检查冲突
    if email_exists(&data.email).await {
        return ApiError::conflict("Email already registered").into();
    }

    // 创建用户
    let user = create_user(data).await;
    ApiResult::value(user)
}
```

## HTTP 状态码自动映射

当使用 ApiError 或 AppError 时,HTTP 状态码会自动根据错误码设置:

```rust
ApiError::not_found(...)      // HTTP 404
ApiError::bad_request(...)    // HTTP 400
ApiError::unauthorized(...)   // HTTP 401
ApiError::forbidden(...)      // HTTP 403
ApiError::validation(...)     // HTTP 422
ApiError::conflict(...)       // HTTP 409
ApiError::internal(...)       // HTTP 500
```

## 完整示例

查看 `examples/error_handling_demo.rs` 了解所有错误处理模式。

运行示例:

```bash
cargo run --example error_handling_demo --features actix
```

测试不同错误类型:

```bash
# 成功
curl http://localhost:8080/users/1

# 404 Not Found
curl http://localhost:8080/users/999

# HTTP 状态码演示
curl http://localhost:8080/error-demo/not_found    # 404
curl http://localhost:8080/error-demo/bad_request  # 400
curl http://localhost:8080/error-demo/conflict     # 409
```

## 最佳实践

1. **定义领域特定的错误类型**:使用 thiserror 定义业务错误
2. **实现 From<YourError> for ApiError**:统一转换路径
3. **使用语义化的错误码**:选择正确的 ErrorCode
4. **提供清晰的错误消息**:帮助客户端理解问题
5. **记录内部错误**:使用日志记录详细信息,但只向客户端返回友好消息

## 迁移指南

### 从旧的 ApiResult::failure 迁移

**之前:**
```rust
ApiResult::<User>::failure("User not found")
```

**现在:**
```rust
ApiError::not_found("User not found").into()
```

### 从 match 语句迁移

**之前:**
```rust
match get_user(id).await {
    Some(user) => ApiResult::value(user),
    None => ApiResult::<User>::failure("Not found"),
}
```

**现在:**
```rust
get_user(id)
    .await
    .ok_or_else(|| ApiError::not_found("User not found"))
    .into_api_result()
```