如何将一部分请求发送给另一个 url

How to send a portion of the requests to another url

我有一个 Web 服务,它使用 Spring Rest 控制器(使用 Netty 而不是 Apache Tomcat)处理 GET/POST/PUT HTTP 请求。我希望过滤所有进入我的服务的请求,当一个请求配置了某个 header 时,我想将这个特定的请求发送到一个完全不同的 URL,同时将响应返回到相同的发送原始请求的实体。

这是我的代码:

@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);
    }
}

通过此实现,请求只是传递到我的正常休息控制器,而不会到达其他服务。我在这里错过了什么?

您应该添加@Order 并指定正确的顺序,然后设置debug = true 以查看过滤器执行情况。 另见 spring webflux webfilters

最终我通过使用 Spring 的 WebClient (https://www.baeldung.com/spring-5-webclient) 解决了这个问题,以便将请求重新发送到外部服务。

 @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))));
    }