服务网关Gateway全局通用异常处理
Spring Cloud Gateway 全局通用异常处理在传统 Spring Boot 应用中, 我们 @ControllerAdvice 来处理全局的异常,进行统一包装返回// 摘至 spring cloud alibaba console 模块处理@ControllerAdvicepublic class ConsoleExceptionHandler {@ExceptionHandler(
·
Spring Cloud Gateway 全局通用异常处理
在传统 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 {
private final List<WebExceptionHandler> exceptionHandlers;
public ExceptionHandlingWebHandler(WebHandler delegate, List<WebExceptionHandler> handlers) {
super(delegate);
this.exceptionHandlers = Collections.unmodifiableList(new ArrayList(handlers));
}
public List<WebExceptionHandler> getExceptionHandlers() {
return this.exceptionHandlers;
}
public Mono<Void> handle(ServerWebExchange exchange) {
Mono completion;
try {
completion = super.handle(exchange);
} catch (Throwable var5) {
completion = Mono.error(var5);
}
WebExceptionHandler handler;
// 获取全局的 WebExceptionHandler 执行
for(Iterator var3 = this.exceptionHandlers.iterator(); var3.hasNext(); completion = completion.onErrorResume((ex) -> {
return handler.handle(exchange, ex);
})) {
handler = (WebExceptionHandler)var3.next();
}
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_json响应
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 {
//返回json异常原因给前端
return bufferFactory.wrap(objectMapper.writeValueAsBytes(R.failed(ex.getMessage())));
} catch (JsonProcessingException e) {
log.warn("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
链接:https://juejin.im/post/5ecf06bbf265da76bd1ac76a
链接:https://juejin.im/post/5bbad1405188255c4a7137e1
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1
更多推荐
已为社区贡献2条内容
所有评论(0)