Spring 云网关的全局异常处理
Global Exception Handling with Spring Cloud Gateway
我正在使用 Spring Cloud Gateway Greenwich.SR1 和 Spring Boot 2.1.5。我正在尝试为我的下游服务创建一个网关。网关的部分工作是为下游请求提供全局错误页面。当下游服务 returns HTTP 403 响应时,我希望网关提供合适的错误页面。
我目前正在使用这样的自定义过滤器
public class ForbiddenFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Override
public String name() {
return "Forbidden";
}
@Override
public GatewayFilter apply(Object o) {
return (exchange, chain) -> chain.filter(exchange).then(
Mono.defer(() -> {
if (!exchange.getResponse().isCommitted() &&
HttpStatus.FORBIDDEN.equals(exchange.getResponse().getStatusCode())) {
return Mono.error(new ResponseStatusException(HttpStatus.FORBIDDEN));
}
return Mono.empty();
}));
}
}
我还在 src/main/resources/templates/error/
设置了一个 403.html
文件。
问题是网关 returns 403 响应的正文为空,而不是 html 文件的内容。在调试过程中,我可以看到 DefaultErrorWebExceptionHandler
以 Mono<ServerResponse>
的形式创建了正确的正文,但它从未被写入实际响应。
有没有其他方法可以让它发挥作用?
我使用自定义 ServerHttpResponseDecorator
解决了这个问题。代码的关键部分是覆盖 writeWith
方法以提供自定义正文:
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (shouldServeErrorPage(exchange)) {
exchange.getResponse().getHeaders().setContentLength(-1);
return errorWebExceptionHandler.handle(exchange, new ResponseStatusException(getHttpStatus(exchange)));
} else {
return getDelegate().writeWith(body);
}
}
@Override
public Mono<Void> writeAndFlushWith(
Publisher<? extends Publisher<? extends DataBuffer>> body) {
if (shouldServeErrorPage(exchange)) {
return writeWith(Flux.from(body).flatMapSequential(p -> p));
} else {
return getDelegate().writeAndFlushWith(body);
}
}
private boolean shouldServeErrorPage(ServerWebExchange exchange) {
HttpStatus statusCode = getHttpStatus(exchange);
return statusCode.is5xxServerError() || statusCode.is4xxClientError();
}
};
return chain.filter(exchange.mutate().response(responseDecorator).build());
推送了一个工作示例
我正在使用 Spring Cloud Gateway Greenwich.SR1 和 Spring Boot 2.1.5。我正在尝试为我的下游服务创建一个网关。网关的部分工作是为下游请求提供全局错误页面。当下游服务 returns HTTP 403 响应时,我希望网关提供合适的错误页面。
我目前正在使用这样的自定义过滤器
public class ForbiddenFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Override
public String name() {
return "Forbidden";
}
@Override
public GatewayFilter apply(Object o) {
return (exchange, chain) -> chain.filter(exchange).then(
Mono.defer(() -> {
if (!exchange.getResponse().isCommitted() &&
HttpStatus.FORBIDDEN.equals(exchange.getResponse().getStatusCode())) {
return Mono.error(new ResponseStatusException(HttpStatus.FORBIDDEN));
}
return Mono.empty();
}));
}
}
我还在 src/main/resources/templates/error/
设置了一个 403.html
文件。
问题是网关 returns 403 响应的正文为空,而不是 html 文件的内容。在调试过程中,我可以看到 DefaultErrorWebExceptionHandler
以 Mono<ServerResponse>
的形式创建了正确的正文,但它从未被写入实际响应。
有没有其他方法可以让它发挥作用?
我使用自定义 ServerHttpResponseDecorator
解决了这个问题。代码的关键部分是覆盖 writeWith
方法以提供自定义正文:
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (shouldServeErrorPage(exchange)) {
exchange.getResponse().getHeaders().setContentLength(-1);
return errorWebExceptionHandler.handle(exchange, new ResponseStatusException(getHttpStatus(exchange)));
} else {
return getDelegate().writeWith(body);
}
}
@Override
public Mono<Void> writeAndFlushWith(
Publisher<? extends Publisher<? extends DataBuffer>> body) {
if (shouldServeErrorPage(exchange)) {
return writeWith(Flux.from(body).flatMapSequential(p -> p));
} else {
return getDelegate().writeAndFlushWith(body);
}
}
private boolean shouldServeErrorPage(ServerWebExchange exchange) {
HttpStatus statusCode = getHttpStatus(exchange);
return statusCode.is5xxServerError() || statusCode.is4xxClientError();
}
};
return chain.filter(exchange.mutate().response(responseDecorator).build());
推送了一个工作示例