(1)一開始搜尋讀取引數的文章,方法大多是從body裡讀一次DataBuffer,轉成字串,然後再把字串轉成DataBuffer重新放到body裡,如:
http://www.cnblogs.com/cafeba…
(2)
上面的方法我試過可以,但是Content-Type是multipart/form-data的時候會報錯
java.lang.IllegalStateException: Only one connection receive subscriber allowed.
不知道是不是我姿勢不對。
而且如果我們在代理到第三方服務的時候才讀取body,這樣效率應該會高一些
看NettyRoutingFilter類裡的filter方法
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
·······
Mono<HttpClientResponse> responseMono = this.httpClient.request(method, url, req -> {
final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
.headers(httpHeaders)
.chunkedTransfer(chunkedTransfer)
.failOnServerError(false)
.failOnClientError(false);
if (preserveHost) {
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
proxyRequest.header(HttpHeaders.HOST, host);
}
if (properties.getResponseTimeout() != null) {
proxyRequest.context(ctx -> ctx.addHandlerFirst(
new ReadTimeoutHandler(properties.getResponseTimeout().toMillis(), TimeUnit.MILLISECONDS)));
}
return proxyRequest.sendHeaders()
//這裡的是ReactorServerHttpRequest呼叫了getBody()方法,
//所以我們只要重寫ReactorServerHttpRequest的getbody方法,加上我們讀取的邏輯就行了,
//gateway給我們提供了裝飾類ServerHttpRequestDecorator,我們只需把過濾器的優先順序設定高於NettyRoutingFilter(實際他已經是倒數第二的優先順序了),且把ReactorServerHttpRequest替換成ServerHttpRequestDecorator就行了
.send(request.getBody().map(dataBuffer ->
((NettyDataBuffer) dataBuffer).getNativeBuffer()));
});
給個stackoverflow的虛擬碼
ServerHttpRequestDecorator decoratedRequest = new ServerHttpRequestDecorator(request) {
@Override
public Flux<DataBuffer> getBody() {
StringBuilder sb=new StringBuilder();
return super.getBody().map(dataBuffer -> {
// probably should reuse buffers
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
byte[] uppedContent = new String(content, Charset.forName("UTF-8")).toUpperCase().getBytes();
return bufferFactory.wrap(uppedContent);
}) ;
}
};
//再說說記錄返回的json,在NettyRoutingFilter類的154行,請求第三方服務反回後,
// Defer committing the response until all route filters have run
// Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter
//註釋說了把返回結果放進ServerWebExchange 的引數裡了,並且在NettyWriteResponseFilter讀取
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
看NettyWriteResponseFilter的讀取程式碼,
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.defer(() -> {
//拿出NettyRoutingFilter放進去的Response
HttpClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);
if (clientResponse == null) {//空的就不過直接到下一個過濾器
return Mono.empty();
}
log.trace("NettyWriteResponseFilter start");
ServerHttpResponse response = exchange.getResponse();
NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();
//TODO: what if it`s not netty
final Flux<NettyDataBuffer> body = clientResponse.receive()
.retain() //TODO: needed?
.map(factory::wrap);
MediaType contentType = null;
try {
contentType = response.getHeaders().getContentType();
} catch (Exception e) {
log.trace("invalid media type", e);
}
//判斷contentType是不是text/event-stream或者application/stream+json
//反正上面兩種型別的結果我們肯定不用記錄,所以我們重寫response的writeWith方法就好,
//跟上面一樣gateway也提供了個裝飾器類ServerHttpResponseDecorator
return (isStreamingMediaType(contentType) ?
response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body));
}));
}
上stackoverflow的虛擬碼
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
// probably should reuse buffers
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
byte[] uppedContent = new String(content, Charset.forName("UTF-8")).toUpperCase().getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body); // if body is not a flux. never got there.
}
};
參考:
https://stackoverflow.com/que…
該如果返回的資料長度很長的話,資料可能會讀不完全,如果出現讀取時擷取了中文字元,導致長度變多1位,進而json的右括號消失,也可參考下面連結
參考解決方案:
https://stackoverflow.com/que…