2

我一直在尝试使用 Spring Cloud Gateway,并且正在尝试修改响应正文。使用响应装饰器,我可以看到主体被修改,但是,缓冲区大小仍然是原始响应的大小。有没有办法将缓冲区大小扩展到新响应主体的大小?

public class ModifyBodyGatewayFilterImpl implements GatewayFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        logger.info("\n\nexchange.getAttributes():\n {}\n\n", exchange.getAttributes());

        ServerHttpResponse response = exchange.getResponse();
        DataBufferFactory dataBufferFactory = response.bufferFactory();

        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {

            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

                Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;

                Flux<? extends DataBuffer> f = flux.flatMap( dataBuffer  -> {

                    byte[] origRespContent = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(origRespContent);

                    System.out.println("content::: " + (new String(origRespContent)));

                    //alocating a new buffer size does not help.
                    DataBuffer b = dataBufferFactory.allocateBuffer(256);
                    b.write("0123456789abcdefg".getBytes());

                    return Flux.just(b);
                });

                return super.writeWith(f);
            }
        };

        ServerWebExchange swe = exchange.mutate().response(decoratedResponse).build();
        return chain.filter(swe);
    }
}

示例: 预期的重写响应为0123456789abcdefgIf original content is 11 bytes <p>test</p>,则重写响应被截断为0123456789a

4

5 回答 5

3

你可以用这个

// prepare the mono to be returned
    DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();
    ObjectMapper objMapper = new ObjectMapper();
    byte[] obj;
    try {
        obj = objMapper.writeValueAsBytes(response);
        return exchange.getResponse().writeWith(Mono.just(obj).map(r -> dataBufferFactory.wrap(r)));
    } catch (JsonProcessingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return exchange.getResponse().setComplete();

其中response是您想要的任何对象,它必须是可序列化的。

于 2018-09-13T12:37:36.130 回答
3

我使用buffer()方法解决了这个问题:

public class ModifyBodyGatewayFilterImpl implements GatewayFilter {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {


    ServerHttpResponse response = exchange.getResponse();
    DataBufferFactory dataBufferFactory = response.bufferFactory();

    ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {

      @Override
      public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        if (body instanceof Flux) {
          Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;

          return super.writeWith(flux.buffer().map(dataBuffers -> {
            ByteOutputStream outputStream = new ByteOutputStream();
            dataBuffers.forEach(i -> {
              byte[] array = new byte[i.readableByteCount()];
              i.read(array);
              outputStream.write(array);
            });
            outputStream.write("0123456789abcdefg".getBytes());
            return dataBufferFactory.wrap(outputStream.getBytes());
          }));
        }
        return super.writeWith(body);
      }
    };

    ServerWebExchange swe = exchange.mutate().response(decoratedResponse).build();
    return chain.filter(swe);
  }
}

它应该将所有响应块放在缓冲区中,当通量完成时应该释放这些块。

于 2018-02-08T21:26:24.347 回答
2

您还需要重写content-length标题。像这样:

byte[] bytes = "0123456789abcdefg".getBytes();
DataBuffer b = dataBufferFactory.wrap(bytes);
response.getHeaders().setContentLength(bytes.length);

我希望这有帮助 :)

于 2019-12-11T12:56:02.123 回答
1

我遇到了同样的问题,数据是在两个缓冲区中接收的。基于@Alex 解决方案,我通过加入但没有ByteOutpoutStream.

在我的解决方案中,我使用了DefaultDataBufferFactoryjoin()方法。

@Override
public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {

    if (body instanceof Flux) {
        Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
            var joinedBuffers = new DefaultDataBufferFactory().join(dataBuffers);
            
            byte[] content = new byte[joinedBuffers.readableByteCount()];
            joinedBuffers.read(content);
            final var responseBody = new String(content, StandardCharsets.UTF_8);
            // modify body
            return bufferFactory.wrap(responseBody.getBytes());
        }));
    }
    return super.writeWith(body);
}
于 2020-09-18T14:51:43.093 回答
0

我正在寻找一种无需处理Flux(即,Mono很好)即可修改响应的通用方法(我基本上是在修改 JSON 正文),因此我开始研究 SCGModifyResponseBodyGatewayFilterFactory今天的工作并借助信息从这个要点这个博客条目

我能够想出一个快速而肮脏的解决方案并将其写在这里。基本上我只是修改了ModifyResponseBodyGatewayFilterFactory今天正在做的事情并针对我的用例进行了一些更改。解决方案的片段在这里:

@SuppressWarnings("unchecked")
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

    Class inClass = String.class;
    Class outClass = String.class;

    String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType);

    ClientResponse clientResponse = prepareClientResponse(body, httpHeaders);

    // TODO: flux or mono
    Mono modifiedBody = extractBody(exchange, clientResponse, inClass)
        .flatMap(originalBody -> Mono.just(applyTransform((String) originalBody, config))
        .switchIfEmpty(Mono.empty());

    BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
    CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange,
        exchange.getResponse().getHeaders());
    return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
        Mono<DataBuffer> messageBody = writeBody(getDelegate(), outputMessage, outClass);
        HttpHeaders headers = getDelegate().getHeaders();
        if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)
            || headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
            messageBody = messageBody.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
        }
        // TODO: fail if isStreamingMediaType?
        return getDelegate().writeWith(messageBody);
    }));
}

同样,此解决方案可能不是这里需要的,但希望这将帮助需要类似解决方案的人。

于 2021-01-05T23:53:52.727 回答