Spring Cloud Gateway изменяет ответ об ошибке прокси - PullRequest
0 голосов
/ 07 апреля 2020

У меня есть следующая конфигурация маршрутизации из базы данных

@Bean
    public RouteLocator routeLocator(
        final PathRoutePredicateFactory predicateFactory,
        final RouteConfigDao routeConfigDao) {

        return () -> routeConfigDao.findAll()
            .map(routeConfig -> Route.async()
                .asyncPredicate(predicateFactory.applyAsync(config -> config.setPatterns(of(routeConfig.getPath()))))
                .id(routeConfig.getName())
                .uri(routeConfig.getUrl())
                // .filter(gatewayFilterSupplier())
                .build());

    }

Все прокси-службы будут возвращать общий ответ json в случае сценария ошибки

{
  "errorCode": "ASD-325",
  "errorField": "Name"
}

В шлюзе I необходимо перехватить этот ответ об ошибке и сделать еще один вызов REST, чтобы получить сообщение об ошибке для errorCode и вернуться к клиенту.

Примечание: я использую Spring Webflux и R2DB C

1 Ответ

0 голосов
/ 09 мая 2020

Я нашел решение своей проблемы с помощью пружинного шлюза GlobalFilter. Используя GlobalFilter, я смог перехватить ответ нисходящего API и проверить ответ об ошибке.

    import com.rmurugaian.spring.cloud.spi.MessageServiceProvider;
    import org.reactivestreams.Publisher;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.core.io.buffer.DataBufferFactory;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;

    import java.nio.charset.StandardCharsets;

    @Component
    public class DownstreamErrorResponseFilter implements GlobalFilter, Ordered {

    private final MessageServiceProvider messagingService;

    public DownstreamErrorResponseFilter(final MessageServiceProvider messagingService) {
        this.messagingService = messagingService;
    }

    @Override
    public Mono<Void> filter(final ServerWebExchange exchange, final GatewayFilterChain chain) {
        final ServerHttpResponse originalResponse = exchange.getResponse();
        final DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        final ServerHttpResponseDecorator decoratedResponse =
                new ErrorResponseResolveDecorator(originalResponse, bufferFactory, messagingService);
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    private static class ErrorResponseResolveDecorator extends ServerHttpResponseDecorator {
        private final ServerHttpResponse originalResponse;
        private final DataBufferFactory bufferFactory;
        private final MessageServiceProvider messagingService;

        ErrorResponseResolveDecorator(
                final ServerHttpResponse delegate,
                final DataBufferFactory bufferFactory,
                final MessageServiceProvider messagingService) {

            super(delegate);
            this.originalResponse = delegate;
            this.bufferFactory = bufferFactory;
            this.messagingService = messagingService;
        }

        @SuppressWarnings("unchecked")
        @Override
        public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
            final HttpStatus statusCode = getStatusCode();
            if (statusCode.isError()) {
                if (body instanceof Flux) {
                    final Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    final Flux<DataBuffer> responseFlux =
                            fluxBody
                                    .map(DownstreamErrorResponseFilter::readAsString)
                                    .flatMap(messagingService::resolveErrorResponse)
                                    .map(String::getBytes)
                                    .map(bufferFactory::wrap);
                    return super.writeWith(responseFlux);
                }
            }
            return originalResponse.writeWith(body); // if body is not a flux. never got there.
        }
    }

    private static String readAsString(final DataBuffer dataBuffer) {
        final byte[] content = new byte[dataBuffer.readableByteCount()];
        dataBuffer.read(content);
        return new String(content, StandardCharsets.UTF_8);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
...