How to send a portion of the requests to another url

693 views Asked by At

I have a web service which handles GET/POST/PUT HTTP requests using a Spring Rest Controller (using Netty and not Apache Tomcat). I wish to filter all the requests coming in my service, and when a request has a certain header configured, I want to send this specific request to a whole different URL, while returning the response to the same entity which sent the original request.

Here is my code:

@Component
public class MyWebFilter implements WebFilter {

    @Autowired
    private SomeService someService;
    private final Logger log = LoggerFactory.getLogger(MyWebFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        HttpHeaders headers = serverWebExchange.getRequest().getHeaders();
        if (headers.containsKey("someHeader")) {
            if (someService.askSomething(Objects.requireNonNull(headers.get("someHeader")))) {
                URI originalUri = serverWebExchange.getRequest().getURI();
                log.info("Redirecting request with URI {} to some service", originalUri.getPath());
                try {
                    URI someUri = new URI("http",
                            originalUri.getUserInfo(),
                            someService.getHost(),
                            someService.getPort(),
                            originalUri.getPath(),
                            originalUri.getQuery(),
                            originalUri.getFragment());
                    ServerHttpRequest newRequest =  serverWebExchange.getRequest().mutate().uri(someUri).build();
                    ServerWebExchange newExchange = serverWebExchange.mutate().request(newRequest).build();
                    return webFilterChain.filter(newExchange);
                } catch (URISyntaxException e) {
                    throw new IllegalStateException(e.getMessage(), e);
                }
            }
        }
        return webFilterChain.filter(serverWebExchange);
    }
}

With this implementation the request is simply passed through to my normal rest controller and does not reach the other service. What am I missing here?

2

There are 2 answers

0
Eilon Benami On BEST ANSWER

Eventually I solved this by using Spring's WebClient (https://www.baeldung.com/spring-5-webclient) in order to resend the request to the external service.

 @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        HttpHeaders headers = serverWebExchange.getRequest().getHeaders();
        if (headers.containsKey("someHeader")) {
            if (someService.askSomething(Objects.requireNonNull(headers.get("someHeader")))) {
                URI originalUri = serverWebExchange.getRequest().getURI();
                log.info("Redirecting a {} HTTP request with path {} to some service",serverWebExchange.getRequest().getMethod(), originalUri.getPath());
                return sendToSomeService(serverWebExchange.getRequest(), originalUri.getPath()).flatMap(body -> writeResponse(serverWebExchange, body));

            }
        }
        return webFilterChain.filter(serverWebExchange);
    }

    public Mono<String> sendToSomeService(ServerHttpRequest request, String path) {
        return this.webClient.method(Objects.requireNonNull(request.getMethod())).uri(path)
                .body(BodyInserters.fromDataBuffers(request.getBody()))
                .headers(getHttpHeadersConsumer(request))
                .retrieve()
                .bodyToMono(String.class);
    }

    @NotNull
    private Consumer<HttpHeaders> getHttpHeadersConsumer(ServerHttpRequest request) {
        return new Consumer<HttpHeaders>() {
            @Override
            public void accept(HttpHeaders httpHeaders) {
                for (Map.Entry<String, List<String>> header : request.getHeaders().entrySet()) {
                    httpHeaders.set(header.getKey(), header.getValue().get(0));
                }
            }
        };
    }


    private Mono<Void> writeResponse(ServerWebExchange exchange, String message) {
        exchange.getResponse().getHeaders().add("Content-Type", "application/json");
        return exchange
                .getResponse()
                .writeWith(
                        Flux.just(
                                exchange.getResponse().bufferFactory().wrap(message.getBytes(StandardCharsets.UTF_8))));
    }
1
user6064168 On

you should add @Order and specify correct order, and then set debug = true to see the filter execution. see also spring webflux webfilters