7Gateway 异常处理 · SpringCloud微服务实战 · 看云
7. Gateway 异常处理
导航
跟入门篇讲
Zuul的时候一样,Spring Cloud Gateway的全局异常处理也不能直接用@ControllerAdvice来处理,通过自定义异常处理器来实现业务需求。
网关是给接口做代理转发的,后端对应的都是REST API,返回数据格式一般都是JSON。如果不做处理,当发生异常时,Gateway默认给出的错误信息是页面,不方便前端进行异常处理。
需要对异常信息进行处理,返回JSON格式的数据给客户端。
本节代码地址
GitHub: https://github.com/xuyisu/fw-sping-cloud/tree/master/fw-cloud-gateways/fw-cloud-gateways-gateway
7.1 应用配置
这里的配置只是简单的代码配置,只有是当前系统时间是2020年1月8号以后的都可以转发,否则就会返回404
server:
port: 8711
spring:
application:
name: fw-gateways-gateway
profiles:
active: exception_route
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
---
spring:
cloud:
gateway:
routes:
- id: exception_route
uri: lb://fw-cloud-ribbon-server
predicates:
- After=2020-01-08T18:30:11.965+08:00[Asia/Shanghai]
profiles: exception_route
7.2 自定义异常
先看默认异常处理
在org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler中的getRoutingFunction()方法就是控制返回格式的,原代码如下,默认异常信息返回的是HTML格式。这里我们自定义的时候需要改变为JSON的。
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(this.acceptsTextHtml(), this::renderErrorView).andRoute(RequestPredicates.all(), this::renderErrorResponse);
}
原始的方法是通过status来获取对应的HttpStatus的,对应的是整形数字,如果你的状态字段不是status,需要重写此方法。
protected int getHttpStatus(Map<String, Object> errorAttributes) {
return (Integer)errorAttributes.get("status");
}
重写后的如下
@Slf4j
public class FwGatewayExceptionHandler extends DefaultErrorWebExceptionHandler {
public FwGatewayExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
ErrorProperties errorProperties, ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
}
@Override
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
Throwable error = super.getError(request);
log.error(
"请求发生异常,请求URI:{},请求方法:{},异常信息:{}",
request.path(), request.methodName(), error.getMessage()
);
String errorMessage;
if (error instanceof NotFoundException) {
String serverId = StringUtils.substringAfterLast(error.getMessage(), "Unable to find instance for ");
serverId = StringUtils.replace(serverId, "\"", StringUtils.EMPTY);
errorMessage = String.format("无法找到%s服务", serverId);
} else if (StringUtils.containsIgnoreCase(error.getMessage(), "connection refused")) {
errorMessage = "目标服务拒绝连接";
} else if (error instanceof TimeoutException) {
errorMessage = "访问服务超时";
} else if (error instanceof ResponseStatusException
&& StringUtils.containsIgnoreCase(error.getMessage(), HttpStatus.NOT_FOUND.toString())) {
errorMessage = "未找到该资源";
} else {
errorMessage = "网关转发异常";
}
Map<String, Object> errorAttributes = new HashMap<>(2);
errorAttributes.put("msg", errorMessage);
errorAttributes.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
return errorAttributes;
}
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
@Override
protected int getHttpStatus(Map<String, Object> errorAttributes) {
return (int) errorAttributes.get("code");
}
}
7.3 覆盖默认的异常处理
重写errorWebExceptionHandler()方法,里面的实现用我们自己定义的异常处理(FwGatewayExceptionHandler)来实现,否则仍然走默认的。
@Configuration
public class FwGatewayErrorConfigure {
private final ServerProperties serverProperties;
private final ApplicationContext applicationContext;
private final ResourceProperties resourceProperties;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public FwGatewayErrorConfigure(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) {
FwGatewayExceptionHandler exceptionHandler = new FwGatewayExceptionHandler(
errorAttributes,
this.resourceProperties,
this.serverProperties.getError(),
this.applicationContext);
exceptionHandler.setViewResolvers(this.viewResolvers);
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
return exceptionHandler;
}
}
7.4 应用启动
ribbon-server 不启动
验证出现异常时候的统一处理,浏览器或Postman输入localhost:8711/user/1
控制台错误如下:
可以看到错误信息被统一处理了


