Глобальная обработка исключений с помощью Spring Cloud Gateway - PullRequest
0 голосов
/ 24 мая 2019

Я использую Spring Cloud Gateway Greenwich.SR1 с Spring Boot 2.1.5.Я пытаюсь создать шлюз для моих последующих сервисов.Часть работы шлюза - предоставить глобальную страницу ошибок для последующих запросов.Когда нисходящий сервис возвращает ответ 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();
                }));
    }
}

У меня также есть файл 403.html в настройке src/main/resources/templates/error/.

Проблема в том, чтоШлюз возвращает ответ 403 с пустым телом вместо содержимого HTML-файла.Во время отладки я вижу, что DefaultErrorWebExceptionHandler создает правильное тело в форме Mono<ServerResponse>, но фактический ответ никогда не записывается.

Есть ли другой способ заставить это работать?

1 Ответ

0 голосов
/ 05 июня 2019

Я решил эту проблему с помощью пользовательского 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

...