资讯专栏INFORMATION COLUMN

spring cloud gateway 源码解析(3)记录请求参数及返回的json

MangoGoing / 3130人阅读

摘要:一开始搜索读取参数的文章,方法大多是从里读一次,转成字符串,然后再把字符串转成重新放到里,如上面的方法我试过可以,但是是的时候会报错不知道是不是我姿势不对。

(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 filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ·······

        Mono 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 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 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 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 writeWith(Publisher body) {
            if (body instanceof Flux) {
                Flux fluxBody = (Flux) 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...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77398.html

相关文章

  • Spring Cloud Gateway修改请求和响应body内容

    摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览作为实战系列的第九篇,咱们聊聊如何用修改原始请求和响应内容,以及修改过程中遇到的问题首先是修改请求,如下图,浏览器是请求发起方,真实参数只有,经过网关时被塞欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Dock...

    ivyzhang 评论0 收藏0
  • spring cloud gateway 源码解析(1)整体流程

    摘要:公司要做自己的网关,于是先把的过了一遍,然后把源码在看了一遍,这样公司的需求就搞定了。包括动态路由,多纬度限流,记录请求参数及返回参数也可修改。至此,流程就走完了。 公司要做自己的网关,于是先把github的issue过了一遍,然后把gateway源码在看了一遍,这样公司的需求就搞定了。包括动态路由,多纬度限流,记录请求参数及返回参数(也可修改)。先从请求进入网关说起吧: 请求先进...

    miqt 评论0 收藏0
  • SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本

    摘要:将请求封装成将请求封装成的接口定义是但是最外层传进来的参数是和,需要将他们封装成,这个工作就是在中做的。其实主要任务就是将各种参数封装成除了和本次请求相关的和,还有会话管理器,编码解码器配置,国际化配置还有用于扩展。本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent接下来,将进入我们升级之路的又一大模块,即网关模块。网关模块我们废弃了...

    不知名网友 评论0 收藏0
  • Spring Cloud Gateway实战之五:内置filter

    摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览作为实战系列的第五篇,是时候了解过滤器的作用了,本篇咱们一起来了解内置好的过滤器,真是种类繁多功能强大过滤器顾名思义,就是在请求头部添加指定的内容带有的完整配欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Doc...

    reclay 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<