我已经尝试过一些Spring Cloud Gateway,我试图修改响应体。使用响应装饰器,我能够看到主体被修改,但是,缓冲区大小仍然是原始响应的大小。有没有办法将缓冲区大小扩展到新响应主体的大小?
Eigen::internal::set_is_malloc_allowed(false);
示例:预期重写的响应为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);
}
}
如果原始内容为11字节0123456789abcdefg
,则重写的响应将被截断为<p>test</p>
。
答案 0 :(得分:1)
我使用 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);
}
}
它应该将所有响应块放在缓冲区中,当磁通完成时应该释放它。
答案 1 :(得分:0)
您可以使用
// 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
是您想要的任何对象,它必须可序列化。
答案 2 :(得分:0)
您还需要重写content-length
标头。
像这样:
byte[] bytes = "0123456789abcdefg".getBytes();
DataBuffer b = dataBufferFactory.wrap(bytes);
response.getHeaders().setContentLength(bytes.length);
我希望这会有所帮助:)
答案 3 :(得分:0)
我遇到了同样的问题,在两个缓冲区中接收到数据。基于@Alex解决方案,我通过加入而没有ByteOutpoutStream
对其进行了改进。
在我的解决方案中,我使用了DefaultDataBufferFactory
和join()
方法。
@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);
}
答案 4 :(得分:0)
我一直在寻找一种通用的方法来修改响应(我基本上是在修改 JSON 正文)而无需处理 Mono
(即 ModifyResponseBodyGatewayFilterFactory
很好)所以我开始借助来自 this gist 和 this blog entry 的信息,看看 SCG 的 ModifyResponseBodyGatewayFilterFactory
今天做了什么。
我能够想出一个快速而肮脏的解决方案并将其写下来here。基本上,我只是修改了 @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);
}));
}
今天正在做的事情,并针对我的用例进行了一些更改。解决方案的一个片段在这里:
#include <QtWidgets>
class Helper: public QObject{
public:
Helper(QLineEdit *le): QObject(le), m_le(le){
m_le->installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event){
if(watched == m_le && event->type() == QEvent::Show){
QString filename = m_le->text();
QFileInfo fi(filename);
QString base = fi.baseName();
m_le->setSelection(0, base.length());
}
return QObject::eventFilter(watched, event);
}
private:
QLineEdit* m_le;
};
class StyledItemDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex &index) const{
QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index);
if(QLineEdit *le = qobject_cast<QLineEdit *>(editor)){
new Helper(le);
}
return editor;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeWidget w;
w.setItemDelegate(new StyledItemDelegate);
for(const QString & filename: {"foo.txt", "foo.tar.gz", "foo.cpp"}){
auto item = new QTreeWidgetItem({filename});
item->setFlags(item->flags() | Qt::ItemIsEditable);
w.addTopLevelItem(item);
}
w.show();
return a.exec();
}
同样,这个解决方案可能不是这里需要的,但希望这能帮助需要类似解决方案的人。