时间:2021-05-20
为什么需要全局异常处理
在传统 Spring Boot 应用中, 我们 @ControllerAdvice 来处理全局的异常,进行统一包装返回
// 摘至 spring cloud alibaba console 模块处理@ControllerAdvicepublic class ConsoleExceptionHandler { @ExceptionHandler(AccessException.class) private ResponseEntity<String> handleAccessException(AccessException e) { return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg()); }}例如: ③ 处应用调用数据库异常,通过 @ControllerAdvice 包装异常请求响应给客户端
但在微服务架构下, 例如 ② 处 网关调用业务微服务失败(转发失败、调用异常、转发失败),在应用设置的 @ControllerAdvice 将失效,因为流量根本没有转发到应用上处理。
如上图: 模拟所有路由断言都不匹配 404 , 和 spring boot 默认保持一致的错误输出页面。 显然我们在网关同样配置 @ControllerAdvice 是不能解决问题,因为 spring cloud gateway 是基于 webflux 反应式编程。
解决方法
默认处理流程
ExceptionHandlingWebHandler 作为 spring cloud gateway 最核心 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)@RequiredArgsConstructorpublic 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]); } })); }}总结
到此这篇关于Spring Cloud Gateway全局通用异常处理的实现的文章就介绍到这了,更多相关Spring Cloud Gateway全局异常内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
在Spring-Cloud-Gateway之请求处理流程文中我们了解最终网关是将请求交给过滤器链表进行处理,接下来我们阅读Spring-Cloud-Gatewa
springcloudgateway的包结构(在Idea2019.3中展示)这个包是spring-cloud-gateway-core.这里是真正的spring
本文首先将会回顾Spring5之前的SpringMVC异常处理机制,然后主要讲解SpringBoot2Webflux的全局异常处理机制。SpringMVC的异常
1.官方文档https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.2.R
文档地址https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-ali