全局异常处理器 - oljc/arco-serve GitHub Wiki

概述

GlobalExceptionHandler 是一个全局异常处理器,使用 Spring Boot 的 @RestControllerAdvice 注解,能够统一处理整个应用中抛出的各种异常,并返回标准化的 JSON 响应格式。目前就封装了 6 种场景的下的异常自动处理。

流程

graph TD
    A[请求进入Controller] --> B{是否抛出异常?}
    B -->|否| C[正常返回ApiResponse]
    B -->|是| D[Spring AOP捕获异常]
    
    D --> E{异常类型匹配}
    
    E -->|BusinessException| F[handleBusinessException]
    E -->|MethodArgumentNotValidException| G[handleMethodArgumentNotValid]
    E -->|BindException| H[handleBindException]
    E -->|MissingServletRequestParameterException| I[handleMissingParam]
    E -->|HttpMessageNotReadableException| J[handleNotReadable]
    E -->|其他Exception| K[handleOther]
    
    F --> F1[记录WARN日志<br/>返回自定义错误码和消息]
    G --> G1[收集字段错误信息<br/>返回详细验证错误]
    H --> H1[收集绑定错误信息<br/>返回绑定错误]
    I --> I1[返回缺少的参数名称]
    J --> J1[记录WARN日志<br/>返回格式错误提示]
    K --> K1[记录ERROR日志<br/>返回通用系统错误]
    
    F1 --> L[统一返回ApiResponse格式]
    G1 --> L
    H1 --> L
    I1 --> L
    J1 --> L
    K1 --> L
    
    C --> M[客户端接收响应]
    L --> M
Loading

功能

  1. 业务异常处理:业务逻辑异常,如数据不存在、权限不足等
  2. 参数校验异常处理:使用 @Valid 注解进行参数校验时的异常
  3. 参数绑定异常处理:使用 @ModelAttribute 绑定对象时的异常
  4. 缺少参数异常处理:必需的请求参数未提供时
  5. 请求体解析异常处理:JSON 格式错误或请求体解析失败
  6. 兜底异常处理:未被其他处理器捕获的所有异常

使用方法

1. 抛出业务异常

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    public ApiResponse<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user == null) {
            // 抛出业务异常,会被GlobalExceptionHandler捕获
            throw new BusinessException(Code.NOT_FOUND, "用户不存在");
        }
        return ApiResponse.success(user);
    }
}

2. 使用参数校验

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @PostMapping
    public ApiResponse<User> createUser(@Valid @RequestBody CreateUserRequest request) {
        // 如果request中的字段不满足校验条件,会抛出MethodArgumentNotValidException
        // 被GlobalExceptionHandler捕获并返回详细的字段错误信息
        User user = userService.create(request);
        return ApiResponse.success(user);
    }
}

// 请求对象
public class CreateUserRequest {
    @NotBlank(message = "用户名不能为空")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Min(value = 18, message = "年龄不能小于18岁")
    private Integer age;
    
    // getters and setters...
}

3. 使用参数绑定

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping
    public ApiResponse<List<User>> getUsers(@ModelAttribute UserQueryRequest request) {
        // 如果URL参数绑定到对象时出错,会抛出BindException
        // 被GlobalExceptionHandler捕获并返回绑定错误信息
        List<User> users = userService.findByQuery(request);
        return ApiResponse.success(users);
    }
}

响应格式

所有异常都会返回标准的 ApiResponse 格式:

成功响应

{
  "code": 200,
  "message": "成功",
  "data": { ... },
  "timestamp": "2024-01-01T10:00:00Z"
}

业务异常响应

{
  "code": 404,
  "message": "用户不存在",
  "data": null,
  "timestamp": "2024-01-01T10:00:00Z"
}

参数校验失败响应

{
  "code": 400,
  "message": "参数校验失败",
  "data": {
    "username": "用户名不能为空",
    "email": "邮箱格式不正确",
    "age": "年龄不能小于18岁"
  },
  "timestamp": "2024-01-01T10:00:00Z"
}

缺少参数响应

{
  "code": 400,
  "message": "缺少必需参数",
  "data": {
    "param": "id"
  },
  "timestamp": "2024-01-01T10:00:00Z"
}

请求体格式错误响应

{
  "code": 400,
  "message": "请求体格式错误",
  "data": null,
  "timestamp": "2024-01-01T10:00:00Z"
}

系统错误响应

{
  "code": 500,
  "message": "系统内部错误",
  "data": null,
  "timestamp": "2024-01-01T10:00:00Z"
}

注意事项

  1. 异常处理顺序: Spring 会按照异常类型的继承关系选择最匹配的处理器,具体的异常类型会优先于通用的异常类型。

  2. 自定义业务异常: 如需添加新的业务异常,请继承 BusinessException 类或创建新的异常处理方法。

  3. 扩展性: 如需处理其他类型的异常,可以在 GlobalExceptionHandler 中添加新的 @ExceptionHandler 方法。

相关类

  • BusinessException: 自定义业务异常类
  • Code: 错误码枚举类
  • ApiResponse: 统一响应格式类
⚠️ **GitHub.com Fallback** ⚠️