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

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<String> bodyRef = new AtomicReference<>();//缓存读取的request body信息
               Flux<DataBuffer> 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<DataBuffer> body = serverHttpRequest.getBody();
        StringBuilder sb = new StringBuilder();
        body.subscribe(buffer -> {
            List<String> 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路由的配置,此坑还需要进一步的排查解决。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Cloud Gateway 是一个反向代理和路由器,它可以拦截进入的请求并将它们路由到不同的服务。Spring Cloud Gateway 可以通过以下步骤获取请求: 1. 在 Spring Cloud Gateway 的路由配置中,定义一个过滤器: ```java @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("path_route", r -> r.path("/foo/**") .filters(f -> f.filter(new RequestBodyFilter())) .uri("http://localhost:8080")) .build(); } ``` 这里定义了一个名为 `RequestBodyFilter` 的过滤器,用于获取请求。 2. 实现 `RequestBodyFilter` 过滤器: ```java public class RequestBodyFilter implements GatewayFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return exchange.getRequest().getBody() .flatMap(body -> { // 处理请求 byte[] bytes = new byte[body.readableByteCount()]; body.read(bytes); String requestBody = new String(bytes); System.out.println(requestBody); // 重新设置请求 DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); exchange.getRequest().mutate().body(buffer); return chain.filter(exchange); }); } } ``` 这里的 `filter` 方法会获取请求,并进行处理。处理完成后,需要重新设置请求,以便后续的过滤器或路由器可以正确地处理请求。 3. 在请求中发送请求: ```bash curl -X POST http://localhost:8080/foo -d '{"name": "John"}' ``` 这里使用 `curl` 命令发送一个 POST 请求,并在请求中包含 JSON 数据。Spring Cloud Gateway 会拦截这个请求,并使用定义的过滤器获取请求。 以上就是 Spring Cloud Gateway 获取请求的方法。需要注意的是,获取请求可能会影响性能,因此应该谨慎使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值