使用Spring Cloud Gateway实现全局异常处理

5
我正在使用Spring Boot 2.1.5和Spring Cloud Gateway Greenwich.SR1。我试图为我的下游服务创建一个网关,其中之一的工作是为下游请求提供全局错误页面。当下游服务返回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的文件。
问题在于,网关返回了一个403响应,但是内容为空,而不是html文件的内容。在调试期间,我可以看到 DefaultErrorWebExceptionHandler 已经创建了正确的 body,格式为Mono<ServerResponse>,但是它从未写入到实际的响应中。
有没有其他方法可以让它工作?

也许是因为其他东西已经向响应写入了内容? - spencergibb
客户端的响应为空。在此过滤器中返回了 Mono.error。 - tine2k
你是在收到响应后才这样做的,因此我有疑问。 - spencergibb
1个回答

5
我使用自定义的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());

我已经在 https://github.com/tine2k/scg-global-error-page 上提交了可工作的示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接