GlobalErrorController
全局错误控制器,统一处理所有未捕获的异常和 HTTP 错误状态
GlobalErrorController
GlobalErrorController 用于统一处理所有未被 @ExceptionHandler 捕获的异常和 HTTP 错误状态(如 404、500 等)。根据使用的框架不同,实现方式有所差异。
框架实现对比
| 对比项 | MVC 版本 | WebFlux 版本 |
|---|---|---|
| 实现接口 | ErrorController | ErrorWebExceptionHandler |
| 处理方法 | 返回 ResponseEntity | 返回 Mono<Void> |
| 请求对象 | HttpServletRequest | ServerWebExchange |
| 响应写入 | 框架自动处理 | 手动写入 DataBuffer |
| 自动配置类 | BaseSdkAutoConfiguration | WebFluxSdkAutoConfiguration |
工作原理
MVC 版本
HTTP 请求
│
▼
Spring DispatcherServlet
│
├── 正常处理 → Controller → 返回响应
│
└── 异常/错误(未匹配任何 @ExceptionHandler)
│
▼
转发至 /error
│
▼
GlobalErrorController
│
▼
构造 BaseResponse 响应WebFlux 版本
HTTP 请求
│
▼
WebFilter Chain
│
├── 正常处理 → Handler → 返回 Mono<Response>
│
└── 异常/错误(未匹配任何 @ExceptionHandler)
│
▼
GlobalErrorController.handle()
│
▼
构造 BaseResponse 并写入响应
│
▼
Mono<Void> 完成错误码映射
GlobalErrorController 将 HTTP 状态码自动映射到对应的 ErrorCode:
| HTTP 状态码 | ErrorCode | 说明 |
|---|---|---|
| 400 | BAD_REQUEST | 错误请求 |
| 401 | UNAUTHORIZED | 未授权 |
| 403 | FORBIDDEN | 禁止访问 |
| 404 | PAGE_NOT_FOUND | 页面未找到 |
| 405 | METHOD_NOT_ALLOWED | 方法不允许 |
| 406 | NOT_ACCEPTABLE | 不可接受 |
| 408 | TIMEOUT | 请求超时 |
| 429 | TOO_MANY_REQUESTS | 请求过多 |
| 500 | SERVER_INTERNAL_ERROR | 服务器内部错误(仅 WebFlux) |
| 502 | GATEWAY_ERROR | 网关错误 |
| 503 | SERVICE_UNAVAILABLE | 服务不可用 |
| 其他 | SERVER_INTERNAL_ERROR | 服务器内部错误(仅 MVC) |
响应格式
当发生错误时,GlobalErrorController 返回如下格式的响应:
{
"context": "550e8400-e29b-41d4-a716-446655440000",
"output": "PageNotFound",
"code": 40401,
"message": "页面未找到",
"errorMessage": "No static resource found for /api/v1/unknown",
"duration": 5,
"data": {
"path": "/api/v1/unknown",
"status": 404
}
}字段
类型
常见错误场景
404 Not Found
当访问不存在的路径时:
{
"context": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"output": "PageNotFound",
"code": 40401,
"message": "页面未找到",
"errorMessage": null,
"duration": 2,
"data": {
"path": "/api/v1/users/999",
"status": 404
}
}405 Method Not Allowed
当使用错误的 HTTP 方法时:
{
"context": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"output": "MethodNotAllowed",
"code": 40501,
"message": "方法不允许",
"errorMessage": "Request method 'DELETE' not supported",
"duration": 1,
"data": {
"path": "/api/v1/user/profile",
"status": 405
}
}500 Internal Server Error
当发生未捕获的异常时:
{
"context": "123e4567-e89b-12d3-a456-426614174000",
"output": "ServerInternalError",
"code": 50001,
"message": "服务器内部错误",
"errorMessage": "NullPointerException: ...",
"duration": 150,
"data": {
"path": "/api/v1/data/process",
"status": 500
}
}WebFlux 特有功能
处理器结构(仅 WebFlux)
public class GlobalErrorController implements ErrorWebExceptionHandler {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public @NotNull Mono<Void> handle(ServerWebExchange exchange, @NotNull Throwable ex) {
// 确定状态码
HttpStatus status = this.determineHttpStatus(ex);
String requestPath = exchange.getRequest().getPath().value();
// 映射状态码到 ErrorCode
ErrorCode errorCode = this.mapStatusCodeToErrorCode(status.value());
// 构建错误详情
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("path", requestPath);
errorDetails.put("status", status.value());
// 使用 ResultUtil 构造响应并写入
return ResultUtil.error(errorCode, errorMessage, errorDetails)
.flatMap(response -> this.writeResponse(exchange, response));
}
}特殊异常处理(仅 WebFlux)
ResponseStatusException
WebFlux 中常见的 ResponseStatusException 会被正确处理:
// 在 Handler 中抛出
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
// GlobalErrorController 会提取状态码 404,映射到 PAGE_NOT_FOUNDIllegalArgumentException
对于 IllegalArgumentException,返回 400 状态码:
private HttpStatus determineHttpStatus(Throwable ex) {
if (ex instanceof ResponseStatusException rse) {
return HttpStatus.valueOf(rse.getStatusCode().value());
}
if (ex instanceof IllegalArgumentException) {
return HttpStatus.BAD_REQUEST;
}
return HttpStatus.INTERNAL_SERVER_ERROR;
}响应写入机制(仅 WebFlux)
WebFlux 版本需要手动将响应写入 ServerHttpResponse:
private Mono<Void> writeResponse(
ServerWebExchange exchange,
ResponseEntity<BaseResponse<Map<String, Object>>> response
) {
ServerHttpResponse serverResponse = exchange.getResponse();
// 设置状态码
serverResponse.setStatusCode(response.getStatusCode());
// 设置内容类型
serverResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON);
// 序列化响应体
try {
String jsonBody = OBJECT_MAPPER.writeValueAsString(response.getBody());
DataBuffer buffer = serverResponse.bufferFactory()
.wrap(jsonBody.getBytes(StandardCharsets.UTF_8));
return serverResponse.writeWith(Mono.just(buffer));
} catch (JsonProcessingException e) {
// 序列化失败时的降级处理
String fallbackBody = "{\"output\":\"UnknownError\",\"code\":50999}";
DataBuffer buffer = serverResponse.bufferFactory()
.wrap(fallbackBody.getBytes(StandardCharsets.UTF_8));
return serverResponse.writeWith(Mono.just(buffer));
}
}MVC 特有功能
自定义错误路径(仅 MVC)
默认错误路径为 /error,可通过配置修改:
server:
error:
path: /custom-error生产环境配置(仅 MVC)
生产环境建议关闭详细的错误信息输出:
server:
error:
include-message: never与异常处理器的关系
GlobalErrorController 是异常处理的最后一道防线:
异常抛出
│
▼
@ExceptionHandler 匹配?
│
├── 是 → 对应的 ExceptionHandler 处理
│
└── 否 → Spring 转发至 /error(MVC)或传播至 ErrorWebExceptionHandler(WebFlux)
│
▼
GlobalErrorController 处理注意事项
GlobalErrorController由对应的自动配置类自动注册,无需手动声明- 该控制器仅处理未被
@ExceptionHandler捕获的错误 - 响应格式与
ResultUtil.error()保持一致,确保前端处理逻辑统一 - WebFlux 版本在序列化失败时会返回降级响应,避免级联错误