@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:【\n{}\n】,响应数据:【\n{}\n】", 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等统一校验处理