Spring Cloud Gateway 自定义异常处理

一、背景

平时我们在Spring MVC层面做自定义异常出处理都是通过@ControllerAdvice和@ExceptionHandler自定义不同类型异常的处理逻辑,如图
Spring Cloud Gateway 自定义异常处理_第1张图片
以上只是简单的同一处理异常,具体实现方式可根据项目自定义处理,这里就不展开细说。

不过本文的重点是在网关层自定义异常,因为请求通过网关再到服务,中间难免会有未处理好的异常抛出,加上网关还有限流,熔断等功能,难免有异常抛出,这个时候我们就需要对这些异常做个处理,
Spring Cloud Gateway默认的异常处理重点是在DefaultErrorWebExceptionHandler、DefaultErrorWebExceptionHandler、ErrorWebFluxAutoConfiguration这三个 类中,不过平时我们返回到客户端的异常格式都是统一的,网关默认的异常输出格式可能不能满足我们的要求,故此我们就得改造输出。

二、代码讲解

所谓的自定义异常其实就是模仿DefaultErrorWebExceptionHandler、DefaultErrorWebExceptionHandler、ErrorWebFluxAutoConfiguration这三个 类覆盖它们的异常处理,本文主要讲解使用操作方面,故原理不在此细说!

我们通过DefaultErrorWebExceptionHandler类中发现它继承了一个抽象异常处理器AbstractErrorWebExceptionHandler,而处理器中实现了ErrorWebExceptionHandler中的handle方法,重点就是在handle方法里,故此我们直接实现ErrorWebExceptionHandler接口,重写handle方法的逻辑。(废话不多数,直接上重点代码)

这里自定义了一个全局异常处理器类GlobalGatewayExceptionHandler ,模仿DefaultErrorWebExceptionHandler和AbstractErrorWebExceptionHandler类的处理

/**
 * @description: 网关异常全局处理
 */
@Slf4j
@Getter
@Setter
public class GlobalGatewayExceptionHandler implements ErrorWebExceptionHandler {

    private List<HttpMessageReader<?>> messageReaders = Collections.emptyList();
    private List<HttpMessageWriter<?>> messageWriters = Collections.emptyList();
    private List<ViewResolver> viewResolvers = Collections.emptyList();
    private ThreadLocal<ResponseResult> threadLocal=new ThreadLocal<>();

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
        log.error("网关异常全局处理,异常信息:{}",throwable.getMessage());
        //这里只是做个最简单的同一的异常结果输出,实际业务可根据throwable不同的异常处理不同的逻辑
        ResponseResult result = ResponseResult.businessError(ResultMessage.SERVICE_ERROR.getCode(),ResultMessage.SERVICE_ERROR.getMsg());
        threadLocal.set(result);
        ServerRequest newRequest = ServerRequest.create(exchange, this.messageReaders);
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse).route(newRequest)
                .switchIfEmpty(Mono.error(throwable))
                .flatMap((handler) -> handler.handle(newRequest))
                .flatMap((response) -> write(exchange, response));
    }

    /**
     * 统一返回指定异常信息(指定json格式输出)
     * @param request
     * @return
     */
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request){
        return ServerResponse.status(ResultMessage.SERVICE_ERROR.getCode())
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(threadLocal.get()));
    }

 	/**
     * 参考DefaultErrorWebExceptionHandler
     */
    private Mono<? extends Void> write(ServerWebExchange exchange, ServerResponse response) {
        exchange.getResponse().getHeaders().setContentType(response.headers().getContentType());
        return response.writeTo(exchange, new ResponseContext());
    }

	 /**
     * 参考DefaultErrorWebExceptionHandler
     */
    private class ResponseContext implements ServerResponse.Context {
        private ResponseContext() {
        }

        @Override
        public List<HttpMessageWriter<?>> messageWriters() {
            return GlobalGatewayExceptionHandler.this.messageWriters;
        }

        @Override
        public List<ViewResolver> viewResolvers() {
            return GlobalGatewayExceptionHandler.this.viewResolvers;
        }
    }
}

GlobalGatewayExceptionConfig 类就是模仿ErrorWebFluxAutoConfiguration类中的处理

/**
 * @description: 网关异常配置,覆盖默认的异常处理
 */
@Configuration
public class GlobalGatewayExceptionConfig {

    @Primary
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                                             ServerCodecConfigurer serverCodecConfigurer){
        GlobalGatewayExceptionHandler globalGatewayExceptionHandler =new GlobalGatewayExceptionHandler();
        globalGatewayExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));
        globalGatewayExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
        globalGatewayExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
        return globalGatewayExceptionHandler;
    }
}

最终的输出结果就是我们自定义的异常输出
{
“code”: 500,
“msg”: “该服务无法使用”,
“data”: null
}

以上示例只是代码片段,只讲解具体实现逻辑,具体实现方式可根据自己的情况定义,实现的方式有很多种,上面讲解的是直接实现ErrorWebExceptionHandler 中的handle方法,如果有兴趣的朋友也可以继承DefaultErrorWebExceptionHandler或者AbstractErrorWebExceptionHandler,然后重写它们的方法覆盖它们的逻辑,怎么喜欢怎么来,各位自己决定。具体就讲解这么多,有任何错误和建议,欢迎留言!

注:如果本博文有任何错误和建议,欢迎留言,共同学习,共同进步!

你可能感兴趣的:(Java)