Эта статья является оригинальной статьей. Любая форма перепечатки приветствуется, но обязательно с указанием источникаЛенг Ленг https://lltx.github.io.
Зачем нужна глобальная обработка исключений
В традиционных приложениях Spring Boot мы используем @ControllerAdvice для обработки глобальных исключений, упаковывая и возвращая их единообразно.
// 摘至 spring cloud alibaba console 模块处理
@ControllerAdvice
public class ConsoleExceptionHandler {
@ExceptionHandler(AccessException.class)
private ResponseEntity<String> handleAccessException(AccessException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
}
}
Например: ③ Приложение вызывает исключение базы данных и передает клиенту ответ на запрос исключения через @ControllerAdvice.
Однако в архитектуре микросервиса, например, в ②, где шлюзу не удается вызвать бизнес-микросервис (сбой переадресации, исключение вызова, сбой переадресации), @ControllerAdvice, установленный в приложении, будет недействительным, поскольку трафик не перенаправляется на заявка на обработку вообще.
Как показано выше: смоделируйте, что все утверждения маршрутизации не соответствуют 404, а страница вывода ошибок соответствует значениям по умолчанию для весенней загрузки. Очевидно, мы можем настроить @ControllerAdvice и в шлюзе, потому что Spring Cloud Gateway основан на реактивном программировании webflux.
Решение
Поток обработки по умолчанию
- ExceptionHandlingWebHandler как часть основного WebHandler весеннего облачного шлюза будет фильтровать обработку исключений.
public class ExceptionHandlingWebHandler extends WebHandlerDecorator {
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
Mono<Void> completion;
try {
completion = super.handle(exchange);
}
catch (Throwable ex) {
completion = Mono.error(ex);
}
// 获取全局的 WebExceptionHandler 执行
for (WebExceptionHandler handler : this.exceptionHandlers) {
completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
}
return completion;
}
}
- Реализация по умолчанию DefaultErrorWebExceptionHandler
public class DefaultErrorWebExceptionHandler {
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
// 根据客户端 `accpet` 请求头决定返回什么资源,如上浏览器返回的是 页面
return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
}
}
// 模拟指定 `accpet` 情况
curl --location --request GET 'http://localhost:9999/adminx/xx' \ 18:09:23
--header 'Accept: application/json'
{"timestamp":"2020-05-24 18:09:24","path":"/adminx/xx","status":404,"error":"Not Found","message":null,"requestId":"083c48e3-2"}⏎
Переопределить ErrorWebExceptionHandler
/**
* @author lengleng
* @date 2020/5/23
* <p>
* 网关异常通用处理器,只作用在webflux 环境下 , 优先级低于 {@link ResponseStatusExceptionHandler} 执行
*/
@Slf4j
@Order(-1)
@RequiredArgsConstructor
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
// header set
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response
.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
return bufferFactory.wrap(objectMapper.writeValueAsBytes(R.failed(ex.getMessage())));
} catch (JsonProcessingException e) {
log.warn("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
Суммировать
- Приоритет переопределенного DefaultErrorWebExceptionHandler должен быть ниже, чем у встроенного ResponseStatusExceptionHandler для получения кода ответа соответствующего класса ошибки.
- Другие расширения могут ссылаться на интегрированную обработку шлюза SentinelBlockExceptionHandler Sentinel, но общая обработка исключений и обработка исключений по умолчанию ничем не отличаются.
- Описание базовой среды: Spring Cloud Hoxton.SR4 и Spring Boot 2.3.0
- Ссылка на конкретный код реализации:gitee.com/log4j/pig