Spring Cloud Gateway全局通用异常处理的实现

网友投稿 622 2022-12-06


Spring Cloud Gateway全局通用异常处理的实现

为什么需要全局异常处理

在传统 Spring Boot 应用中, 我们 @ControllerAdvice 来处理全局的异常,进行统一包装返回

// 摘至 spring cloud alibaba console 模块处理

@ControllerAdvice

public class ConsoleExceptionHandler {

@ExceptionHandler(AccessException.class)

private ResponseEntity handleAccessException(AccessException e) {

return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());

}

}

例如: ③ 处应用调用数据库异常,通过 @ControllerAdvice 包装异常请求响应给客户端

但在微服务架构下, 例如 ② 处 网关调用http://业务微服务失败(转发失败、调用异常、转发失败),在应用设置的 @ControllerAdvice 将失效,因为流量根本没有转发到应用上处理。

如上图: 模拟所有路由断言都不匹配 404 , 和 spring boot 默认保持一致的错误输出页面。 显然我们在网关同样配置 @ControllerAdvice 是不能解决问题,因为 spring cloud gateway 是基于 webflux 反应式编程。

解决方法

默认处理流程

ExceptionHandlingWebHandler 作为 spring cloud gateway 最核心 WebHandler 的一部分会进行异常处理的过滤

public class ExceptionHandlingWebHandler extends WebHandlerDecorator {

@Override

public Mono handle(ServerWebExchange exchange) {

Mono completion;

try http://{

completion = super.handle(exchange);

}

catch (Throwable ex) {

completion = Mono.error(ex);

}

// 获取全局的 WebExceptionHandler 执行

for (WebExceptionHandler handler : this.exceptionHandlers) {

completion = completion.onErrorResume(ex -> handler.handle(exchahttp://nge, ex));

}

return completion;

}

}

默认实现 DefaultErrorWebExceptionHandler

public class DefaultErrorWebExceptionHandler {

@Override

proGobFFwOYwtected RouterFunction 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"GobFFwOYw,"path":"/adminx/xx","status":404,"error":"Not Found","message":null,"requestId":"083c48e3-2"}⏎

重写 ErrorWebExceptionHandler

/**

* @author lengleng

* @date 2020/5/23

*

* 网关异常通用处理器,只作用在webflux 环境下 , 优先级低于 {@link ResponseStatusExceptionHandler} 执行

*/

@Slf4j

@Order(-1)

@RequiredArgsConstructor

public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {

private final ObjectMapper objectMapper;

@Override

public Mono 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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:浅谈Spring中@Import注解的作用和使用
下一篇:Springboot热部署实现原理及实例详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~