为什么需要全局异常处理

在传统 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 , 和 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)
@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
  • 具体实现代码参考:https://gitee.com/log4j/pig

项目推荐: Spring Cloud 、Spring Security OAuth2的RBAC权限管理系统 欢迎关注

Spring Cloud Gateway 全局通用异常处理的更多相关文章

  1. spring cloud gateway 全局过滤器

    全局过滤器作用于所有的路由,不需要单独配置,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP访问限制等等. 接口定义类:org.springframework.cloud.gateway ...

  2. Spring Cloud Gateway的全局异常处理

    Spring Cloud Gateway中的全局异常处理不能直接用@ControllerAdvice来处理,通过跟踪异常信息的抛出,找到对应的源码,自定义一些处理逻辑来符合业务的需求. 网关都是给接口 ...

  3. Spring Cloud Gateway中异常处理

    最近我们的项目在考虑使用Gateway,考虑使用Spring Cloud Gateway,发现网关的异常处理和spring boot 单体应用异常处理还是有很大区别的.让我们来回顾一下异常. 关于异常 ...

  4. Spring Cloud Gateway(十一):全局过滤器GlobalFilter

    本文基于 spring cloud gateway 2.0.1 1.简介 GlobalGilter 全局过滤器接口与 GatewayFilter 网关过滤器接口具有相同的方法定义.全局过滤器是一系列特 ...

  5. Spring Cloud Alibaba学习笔记(21) - Spring Cloud Gateway 自定义全局过滤器

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的全局过滤器,本文介绍如何自定义全局过滤器. 自定义全局过滤需要实现GlobalFilter 接口,该接口和 GatewayFi ...

  6. Spring Cloud Alibaba学习笔记(20) - Spring Cloud Gateway 内置的全局过滤器

    参考:https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filter ...

  7. Spring Cloud Gateway(十):网关过滤器工厂 GatewayFilterFactory

    本文基于 spring cloud gateway 2.0.1 1.GatewayFilterFactory 简介 路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应. 路径过滤器的范 ...

  8. Spring Cloud实战: 基于Spring Cloud Gateway + vue-element-admin 实现的RBAC权限管理系统,实现网关对RESTful接口方法权限和自定义Vue指令对按钮权限的细粒度控制

    一. 前言 信我的哈,明天过年. 这应该是农历年前的关于开源项目 的最后一篇文章了. 有来商城 是基于 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT ...

  9. Spring Cloud实战 | 第十一篇:Spring Cloud Gateway 网关实现对RESTful接口权限控制和按钮权限控制

    一. 前言 hi,大家好,这应该是农历年前的关于开源项目 的最后一篇文章了. 有来商城 是基于 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT实现的统 ...

随机推荐

  1. 哪些原因会导致JAVA进程退出?

    本文转载自哪些原因会导致JAVA进程退出? 导语 JAVA进程消失可能有哪些原因? 那我们就开一篇文章说一下这个问题,其实很easy的,无外乎三种情况. linux的OOM killer杀死 JVM自 ...

  2. linux系统解压命令总结

    原文链接:https://www.cnblogs.com/lhm166/articles/6604852.html tar -c: 建立压缩档案 -x:解压 -t:查看内容 -r:向压缩归档文件末尾追 ...

  3. Redis 使用入门

    NoSql概述 NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL",它泛指非关系型的数据库, Redis 是一个高性能的开源的.C语言写的Nosql( ...

  4. CentOS 7.7上配置mysql

    转载:https://www.cnblogs.com/VinsonYang/p/12333570.html 首先登陆到阿里云,进行远程连接,在这里我使用的是Xshell 6进行连接的. 参照https ...

  5. const成员函数可以将非const指针作为返回值吗?

    先给出一段代码 class A { int *x; public: int *f() const { return x; } }; 成员函数f返回指向私有成员 x 的非常量指针,我认为这会修改成员x ...

  6. NodeJs 入门到放弃 — 常用模块及网络爬虫(二)

    码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14473101.html 目录 码文不易啊,转载请带上本文链接呀,感谢感谢 https ...

  7. LeetCode-二叉树的深度

    二叉树的深度 二叉树的深度 使用递归求解二叉树的深度. 需要注意使用的临界条件. /** * 任意一个二叉树的最大深度 **/ #include<iostream> #include< ...

  8. 微信小程序 下拉刷新 上拉加载

    1.下拉刷新  小程序页面集成了下拉功能,并提供了接口,我们只需要一些配置就可以拿到事件的回调. 1. 需要在 .json 文件中配置. 如果配置在app.json文件中,那么整个程序都可以下拉刷新. ...

  9. canal mysql select权限粒度

    今天产品问了一个问题,问懵了 产品:canal在开通mysql权限时需要哪些权限 我:SELECT, REPLICATION SLAVE, REPLICATION CLIENT 产品:那SELECT权 ...

  10. vue离开页面销毁滚动事件

    methods:{ handleFun(){    /**销毁处理*/ } }, beforeDestroy(){ window.removeEventListener("scroll&qu ...