springboot集成Gateway「全局异常」

309 阅读1分钟

平时Springboot工程开发,用@ControllerAdvice处理。因为Spring Cloud Gateway 是基于webflux反应式处理,在网关同样配置@ControllerAdvice是不能解决的。

如果网关不做全局异常处理,像404错误请求的话,会直接返回错误页面,而不是我们平时包装的JSON响应数据。

自定义异常处理逻辑

public class CustomWebExceptionHandler extends DefaultErrorWebExceptionHandler {


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

    @Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        Throwable error = super.getError(request);
        if (error instanceof org.springframework.cloud.gateway.support.NotFoundException) {
            httpStatus = HttpStatus.NOT_FOUND;
        } else if (error instanceof ResponseStatusException){
            ResponseStatusException responseStatusException = (ResponseStatusException) error;
            httpStatus = responseStatusException.getStatus();
        }
        return response(httpStatus.value(),error);
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    @Override
    protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
        int statusCode = (int) errorAttributes.get("code");
        return HttpStatus.valueOf(statusCode);
    }

    public Map<String, Object> response(int code, Throwable ex) {
        String errorMessage = ex.getMessage();
        Map<String, Object> map = new HashMap<>();
        map.put("code", code);
        map.put("message", errorMessage);
        return map;
    }

}

请求异常默认的是html,源代码如下:

@Override
protected RouterFunction<ServerResponse> getRoutingFunction(
			ErrorAttributes errorAttributes) {
       return RouterFunctions.route(acceptsTextHtml(), this::renderErrorView)
				.andRoute(RequestPredicates.all(), this::renderErrorResponse);
}

覆盖源码,修改为请求异常返回为json

@Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

重写getHttpStatus,默认是通过status来获取对应的HttpStatus的,源代码如下:

protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
	  int statusCode = (int) errorAttributes.get("status");
	  return HttpStatus.valueOf(statusCode);
}

因前端需求,需要给前端通过code返回响应码,所以我重写了getHttpStatus

@Override
    protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
        int statusCode = (int) errorAttributes.get("code");
        return HttpStatus.valueOf(statusCode);
    }

覆盖默认的配置

@Configuration
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfiguration {

    @Autowired
    private Environment env;

    @Resource
    private PushMessagegService pushMessagegService;

    private final ServerProperties serverProperties;

    private final ApplicationContext applicationContext;

    private final ResourceProperties resourceProperties;

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public ErrorHandlerConfiguration(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) {
        CustomWebExceptionHandler exceptionHandler = new CustomWebExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext,env,pushMessagegService);
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }

}