Awara 2019-06-30
(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...