• spring-cloud-gateway获取post请求body参数以及响应数据


    @Component
    @Slf4j
    @AllArgsConstructor
    public class HttpPostBodyFilter implements GlobalFilter, Ordered {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
    
            String method = request.getMethodValue();
            String contentType = request.getHeaders().getFirst("Content-Type");
            if ("POST".equals(method) && contentType.startsWith("multipart/form-data")){
                return DataBufferUtils.join(exchange.getRequest().getBody())
                        .flatMap(dataBuffer -> {
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            try {
                                String bodyString = new String(bytes, "utf-8");
                                log.info(bodyString);
                                exchange.getAttributes().put("POST_BODY",bodyString);
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                            DataBufferUtils.release(dataBuffer);
                            Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                                DataBuffer buffer = exchange.getResponse().bufferFactory()
                                        .wrap(bytes);
                                return Mono.just(buffer);
                            });
    
                            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                                    exchange.getRequest()) {
                                @Override
                                public Flux<DataBuffer> getBody() {
                                    return cachedFlux;
                                }
                            };
                            return chain.filter(exchange.mutate().request(mutatedRequest)
                                            .build());
                        });
            }
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
            return -200;
        }
    }

    主要思路就是在优先级最高的过滤器里面,CacheBodyGlobalFilter这个全局过滤器的目的就是把原有的request请求中的body内容读出来,并且使用ServerHttpRequestDecorator这个请求装饰器对request进行包装,重写getBody方法,并把包装后的请求放到过滤器链中传递下去。这样后面的过滤器中再使用exchange.getRequest().getBody()来获取body时,实际上就是调用的重载后的getBody方法,获取的最先已经缓存了的body数据。这样就能够实现body的多次读取了。

    过滤器优先级不一定是最高,但是要在要获取body之前执行,然后后面在身份鉴定的等过滤器里面,获取到body

    @Component
    @Slf4j
    public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
        @Override
        public int getOrder() {
            return -2;
        }
     
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest serverHttpRequest = exchange.getRequest();
            ServerHttpResponse originalResponse = exchange.getResponse();
            //如果是post请求,将请求体取出来,再写入
            HttpMethod method = serverHttpRequest.getMethod();
            //请求参数,post从请求里获取请求体
            String requestBodyStr = HttpMethod.POST.equals(method) ? resolveBodyFromRequest(serverHttpRequest) : null;
            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.buffer().map(dataBuffers -> {//解决返回体分段传输
                            StringBuffer stringBuffer = new StringBuffer();
                            dataBuffers.forEach(dataBuffer -> {
                                byte[] content = new byte[dataBuffer.readableByteCount()];
                                dataBuffer.read(content);
                                DataBufferUtils.release(dataBuffer);
                                try {
                                    stringBuffer.append(new String(content, "utf-8"));
                                } catch (Exception e) {
                                    log.error("--list.add--error", e);
                                }
                            });
                            String result = stringBuffer.toString();
                            //TODO,result就是response的值,想修改、查看就随意而为了
                            String url = serverHttpRequest.getPath().toString();
                            String urlParams = UrlUtil.getParamsByMap(serverHttpRequest.getQueryParams().toSingleValueMap());
     
                            JSONObject jsonObject = JSONObject.parseObject(result);
                            log.info("请求长度:" + StringUtils.length(requestBodyStr) + ",返回data长度:" + StringUtils.length(jsonObject.getString("data")));
                            log.info("请求地址:【{}】请求参数:GET【{}】|POST:【
    {}
    】,响应数据:【
    {}
    】", url, urlParams, requestBodyStr, result);
     
                            byte[] uppedContent = new String(result.getBytes(), Charset.forName("UTF-8")).getBytes();
                            originalResponse.getHeaders().setContentLength(uppedContent.length);
                            return bufferFactory.wrap(uppedContent);
                        }));
                    }
                    // if body is not a flux. never got there.
                    return super.writeWith(body);
                }
            };
            // replace response with decorator
            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        }
     
        /**
         * 从Flux<DataBuffer>中获取字符串的方法
         *
         * @return 请求体
         */
        private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
            //获取请求体
            Flux<DataBuffer> body = serverHttpRequest.getBody();
     
            AtomicReference<String> bodyRef = new AtomicReference<>();
            body.subscribe(buffer -> {
                CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                DataBufferUtils.release(buffer);
                bodyRef.set(charBuffer.toString());
            });
            //获取request body
            return bodyRef.get();
        }
    }

    这里可以把请求地址、参数、body、响应数据一起打印出来,测试的post请求

    请求参数有56.97kb,后端order-center的接收打印,把整个数据完全接收并返回出去

    网关的打印请求参数数据

    再来一个get请求的

    其他的put、delete等请求均试过正常请求

    用postman的压测结果和对比,数据都完全正常,并且能通过json格式化,说明数据格式也保持了一致

    代码还有很多可以优化改进的地方,根据自己的也无需求来,比如签名、token等统一校验处理

  • 相关阅读:
    AOP的相关理论介绍
    代码访问数据库时没有报错一直转圈,MySQL不同驱动版本的连接方式
    Linux(CentOS7)设置固定获取IP地址
    NodeJS的安装与配置
    IDEA启动tomcat访问localhost:8080出现404错误
    Win10笔记本电脑外接显示器,如何在笔记本合上的时候让他继续在显示器上使用
    SpringBoot集成Swagger-Bootstrap-UI(已改名为Knife4j)
    Redis学习之Jedis的操作和使用
    SpringBoot整合Swagger2,搭建Restful API在线文档
    IDEA创建SpringBoot项目时出现:Initialization failed for 'https://start.spring.io' Please check URL, network and proxy settings.
  • 原文地址:https://www.cnblogs.com/grimm/p/14032710.html
Copyright © 2020-2023  润新知