【SpringCloudAlibaba专题】Springcloud gateway之获取requestBody踩坑(G版本)

【SpringCloudAlibaba专题】Springcloud gateway之获取requestBody踩坑(G版本)_第1张图片

文章目录

  • 踩坑示范
  • 爬坑案例
    • CacheRequestBodyFilter
    • LoggerFilter

#前言
之前写 springcloud gateway收集日志,由于之前没有调研全面,导致了一个小坑,无法记录 post方法获取 requestBody

踩坑示范

ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
            @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.map(dataBuffer -> {
                        // probably should reuse buffers
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        String responseResult = new String(content, Charset.forName("UTF-8"));
                        normalMsg.append("status=").append(this.getStatusCode());
                        normalMsg.append(";header=").append(this.getHeaders());
                        normalMsg.append(";responseResult=").append(responseResult);
                        normalMsg.append(RESPONSE_TAIL);
                        log.info(normalMsg.toString());
                        return bufferFactory.wrap(content);
                    }));
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
public class RecorderServerHttpRequestDecorator extends ServerHttpRequestDecorator {

    private final List<DataBuffer> dataBuffers = new ArrayList<>();

    public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {
        super(delegate);
        super.getBody().map(dataBuffer -> {
            dataBuffers.add(dataBuffer);
            return dataBuffer;
        }).subscribe();
    }

    @Override
    public Flux<DataBuffer> getBody() {
        return copy();
    }

    private Flux<DataBuffer> copy() {
        return Flux.fromIterable(dataBuffers)
                .map(buf -> buf.factory().wrap(buf.asByteBuffer()));
    }


}
  1. 之前的思路是,直接Flux body = serverHttpRequest.getBody();,这种方式获取。
  2. 获取到body然后再将其包装成新的request传递下去(因为requestBody只能获取一次,其他filte就会获取不到)。

这里面会产生两个问题:

  1. 在封装的时候这个subscribe是异步的,可以没有把body的内容放进去,就直接放行了。
  2. 还有就是如果能放进去,最大也只能获取了1024b

爬坑案例

我定义两个filter,一个用做缓存,一个取出,什么意思呢?

  1. 先让请求走缓存filter,缓存放到exchange里面,作为它的一个变量。
  2. 从日志的filter取出来这个变量。

CacheRequestBodyFilter

@Slf4j
@Component
public class CacheRequestBodyFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中
        Object cachedRequestBodyObject = exchange.getAttributeOrDefault(FilterConstant.CACHED_REQUEST_BODY_OBJECT_KEY, null);
        // 如果已经缓存过,略过
        if (cachedRequestBodyObject != null) {
            return chain.filter(exchange);
        }
        // 如果没有缓存过,获取字节数组存入 exchange 的自定义属性中
        return DataBufferUtils.join(exchange.getRequest().getBody())
                .map(dataBuffer -> {
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    DataBufferUtils.release(dataBuffer);
                    return bytes;
                }).defaultIfEmpty(new byte[0])
                .doOnNext(bytes -> exchange.getAttributes().put(FilterConstant.CACHED_REQUEST_BODY_OBJECT_KEY, bytes))
                .then(chain.filter(exchange));
    }

    @Override
    public int getOrder() {
        return -100;
    }
}

LoggerFilter

给出核心代码,详细见github:

Object cachedRequestBodyObject = exchange.getAttributes().get(FilterConstant.CACHED_REQUEST_BODY_OBJECT_KEY);
        if (cachedRequestBodyObject != null) {
            byte[] body = (byte[]) cachedRequestBodyObject;
            String string = new String(body);
            log.info("request body:");
            log.info(string);
        }

可以给个post请求下网关,看是否能获取到requestBoy,如果可以的话,请给代码一个star,有什么建议欢迎下方评论。

github地址:https://github.com/fafeidou/fast-cloud-nacos/blob/master/fast-cloud-nacos-examples/cloud-rpc-examples/api-gateway/

你可能感兴趣的:(springcloud,alibaba,java)