zoukankan      html  css  js  c++  java
  • 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 {
    	@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权限管理系统 欢迎关注

  • 相关阅读:
    [HAOI2015]树上染色 [树形dp]
    【luogu1052】 过河 [动态规划]
    【luogu4819】 [中山市选]杀人游戏 [tarjan 缩点]
    【luogu4185】 [USACO18JAN]MooTube [并查集]
    [国家集训队]墨墨的等式 [差分约束]
    【RMQ】
    [USACO05DEC] 布局 [差分约束]
    [SCOI2011]糖果 [差分约束]
    【POJ 1201】Intervals
    【luogu1993】 小K的农场 [差分约束]
  • 原文地址:https://www.cnblogs.com/leng-leng/p/13064060.html
Copyright © 2011-2022 走看看