springcloud gateway 获取post请求体Json分段导致不全的解决方案


开发版本
springboot 2.0.8.RELEASE + springcloud Finchley.SR2 + spring cloud gateway

前端客户端采用post发送请求,content_type: application/json,
spring cloud gateway需要从request的中取出body进行网关的鉴权处理,然后把处理之后的数据重新封装到body中转发给下游业务系统,
最之前的方式采用的是以下方式读取请求体:
 AtomicReference bodyRef = new AtomicReference<>();//缓存读取的request body信息
               Flux fluxBody = exchange.getRequest().getBody();
               fluxBody.subscribe(buffer -> {
                     CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                     DataBufferUtils.release(buffer);
                     bodyRef.set(charBuffer.toString());
               });
               String bodyStr = bodyRef.get();


偶尔前端提出验签失败,检查上送请求体报文时发现错误:网关获取的body不完整。

尝试了网上的写法来获取body,于是改成了以下的写法来亲自验证,偶尔还是发现网关获取的body体不完整,说明此方法还是不行,有坑。

    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){
        //获取请求体
        Flux body = serverHttpRequest.getBody();
        StringBuilder sb = new StringBuilder();
        body.subscribe(buffer -> {
            List list = Lists.newArrayList();
            byte[] bytes = new byte[buffer.readableByteCount()];
            buffer.read(bytes);
            DataBufferUtils.release(buffer);
            String bodyString = new String(bytes, StandardCharsets.UTF_8);
            sb.append(bodyString);
        });
        return sb.toString();
    }


继续查询相关资料,有几篇文件介绍了要解决获取body不完整的情况得代用code路由的方式来配置,而不能采用yml,于是继续尝试。
    @Bean
    public RouteLocator tpauditRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r ->r
                        .readBody(String.class, requestBody -> {
                            //log.info("requestBody is {}", requestBody);
                            // 这里 r.readBody做了一个前置语言,这样就可以在filter中通过exchange.getAttribute("cachedRequestBodyObject"); 获取body体
                            return true;
                        }).and()
                        .path("/gw/xxxdmin/**")
                        .filters(f -> f.stripPrefix(1)
                                       .hystrix(h -> h.setName("Hystrix").setFallbackUri("forward:/timeoutfallback")))
                        .uri("lb://ADMIN/")
                )
                .route(r ->r
                        .readBody(String.class, requestBody -> {
                            //log.info("requestBody is {}", requestBody);
                            // 这里 r.readBody做了一个前置语言,这样就可以在filter中通过exchange.getAttribute("cachedRequestBodyObject"); 获取body体
                            return true;
                        }).and()
                        .path("/gw/xxxapp/**")
                        .filters(f -> f.stripPrefix(1)
                                       .hystrix(h -> h.setName("Hystrix").setFallbackUri("forward:/timeoutfallback")))
                        .uri("lb://APP/")
                )

                .build();
    }

然后移除yml中配置的路由:
      routes:
          
        - id:  xxxAPP
          uri: lb://xxxAPP
          predicates:
          - Path=${server.servlet.context-path}/xxxapp/**
          filters:
          - StripPrefix=1
          - name: RequestRateLimiter
            args:
              redis-rate-limiter.replenishRate: 1 #允许用户每秒处理多少个请求
              redis-rate-limiter.burstCapacity: 1 #令牌桶的容量,允许在一秒钟内完成的最大请求数
              key-resolver: "#{@addressKeyResolver}" #使用SpEL按名称引用bean
              # 降级配置
          - name: Hystrix
            args:
              name: default
              fallbackUri: forward:/timeoutfallback

业务处理过滤器中通过String bodyStr = exchange.getAttribute("cachedRequestBodyObject");方法获取请求体,
最终问题得以解决。

但本人喜欢采用yml配置文件的形式来做路由,所以对于yml路由的配置,此坑还需要进一步的排查解决。
 

你可能感兴趣的:(springcloud)