【Spring Cloud Gateway】网关的统一异常处理方法

782 阅读1分钟

前言

由于Spring Clod Gateway使用了Web Flux实现,因此无法使用@ControllerAdvice的方法进行全局统一异常处理,需要使用ErrorWebExceptionHandler接口的实现类进行处理,因此我们继承DefaultErrorWebExceptionHandler实现自定义统一异常处理。

CustomErrorWebExceptionHandler实现

/**
 * 描述:自定义全局异常处理器
 *
 * @author xhsf
 * @create 2020/11/26 12:35
 */
public class CustomErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {

    public CustomErrorWebExceptionHandler(
            ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
            ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }

    /**
     * 获取异常属性
     * @param request ServerRequest
     * @param includeStackTrace includeStackTrace
     * @return Map<String, Object>
     */
    @Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        Map<String, Object> map = new HashMap<>();
        map.put("success", false);
        map.put("data", null);
        Throwable error = super.getError(request);
        map.put("message", error.getMessage());
        map.put("errorCode", ErrorCode.INVALID_PARAMETER);

        // 网关的异常
        if (error instanceof NotFoundException) {
            map.put("errorCode", ErrorCode.INVALID_PARAMETER_NOT_FOUND);
        }

        return map;
    }

    /**
     * 指定响应处理方法为JSON处理的方法
     *
     * @param errorAttributes ErrorAttributes
     * @return RouterFunction<ServerResponse>
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    /**
     * 获取对应的HttpStatus
     *
     * @param errorAttributes Map<String, Object>
     * @return HttpStatus
     */
    @Override
    protected int getHttpStatus(Map<String, Object> errorAttributes) {
        ErrorCode errorCode = (ErrorCode) errorAttributes.get("errorCode");
        return errorCode.getHttpStatus().value();
    }

}

这里重点是getErrorAttributes()getHttpStatus()方法。getErrorAttributes()用于获取异常,根据异常的类型自定义响应结果,这里我封装了项目统一使用的{success, data, message, errorCode}结构。

getHttpStatus()用于根据getErrorAttributes()的结果自定义HTTP状态码,因为ErrorCode里已经附带了HTTP状态码,因此直接取出即可。

ErrorHandlerConfig

该类用于配置自定义的handler。

/**
 * 描述:覆盖默认全局异常处理
 *
 * @author xhsf
 * @create 2020/11/26 12:38
 */
@Configuration
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfig {

    private final ServerProperties serverProperties;

    private final ApplicationContext applicationContext;

    private final ResourceProperties resourceProperties;

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public ErrorHandlerConfig(ServerProperties serverProperties,
                              ResourceProperties resourceProperties,
                              ObjectProvider<List<ViewResolver>> viewResolversProvider,
                              ServerCodecConfigurer serverCodecConfigurer,
                              ApplicationContext applicationContext) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
        CustomErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(
                errorAttributes, resourceProperties, serverProperties.getError(), applicationContext);
        exceptionHandler.setViewResolvers(viewResolvers);
        exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }

}