平时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;
}
}