5

我正在使用 Spring 5、Netty 和 Spring webflux 来开发 API 网关。有时我希望网关停止请求,但我也想读取请求的主体以记录它,例如并向客户端返回错误。

我尝试通过订阅正文在 WebFilter 中执行此操作。

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    if (enabled) {
        logger.debug("gateway is enabled. The Request is routed.");
        return chain.filter(exchange);
    } else {
        logger.debug("gateway is disabled. A 404 error is returned.");

        exchange.getRequest().getBody().subscribe();
        exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().allocateBuffer(0)));
    }
}

当我这样做时,它在正文内容很小的情况下起作用。但是当我有一个大婴儿时,只读取通量的第一个元素,所以我不能拥有整个身体。知道怎么做吗?

4

2 回答 2

5

这里的问题是您在过滤器中手动订阅,这意味着您正在从管道的其余部分断开请求的读取。Callingsubscribe()为您提供了一个Disposable帮助您管理底层证券的Subscription.

所以你需要把整个过程变成一个单独的管道,有点像:

Flux<DataBuffer> requestBody = exchange.getRequest().getBody();
// decode the request body as a Mono or a Flux
Mono<String> decodedBody = decodeBody(requestBody); 
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return decodedBody.doOnNext(s -> logger.info(s))
                  .then(exchange.getResponse().setComplete());

请注意,解码整个请求正文Mono意味着您的网关必须在内存中缓冲整个请求正文。

DataBuffer故意是低级类型。如果您想将其解码(即实现示例decodeBody方法)作为字符串,您可以使用DecoderSpring 中的各种实现之一,例如StringDecoder.

现在,因为这是一个相当大且复杂的空间,您可以使用和/或查看Spring Cloud Gateway,它可以做到这一点,甚至更多。

于 2018-04-26T13:31:01.170 回答
5

1.将“ readBody() ”添加到发布路线:

builder.routes()
.route("get_route", r -> r.path("/**")
    .and().method("GET")
    .filters(f -> f.filter(myFilter))
    .uri(myUrl))
.route("post_route", r -> r.path("/**")
    .and().method("POST")
    .and().readBody(String.class, requestBody -> {return true;})
    .filters(f -> f.filter(myFilter))
    .uri(myUrl))

2.然后您可以在过滤器中获取正文字符串:

String body = exchange.getAttribute("cachedRequestBodyObject");

优点:

  1. 没有阻挡。

  2. 无需重新填充主体以进行进一步处理。

适用于 Spring Boot 2.0.6.RELEASE + Sring Cloud Finchley.SR2 + Spring Cloud Gateway。

于 2018-10-24T04:54:18.953 回答