如何将一部分请求发送给另一个 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))));
}
我有一个 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))));
}