欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于Spring-webflux编程中body只能获取一次的问题解决方案

程序员文章站 2024-02-03 11:21:10
...

无论在Spring5的webflux编程或者普通web编程中,只能从request中获取body一次,后面再获取就会报错,但我们有时候会需要获取body中的数据进行加签、验签,这个问题怎么解决呢。

ServerHttpRequestDecorator与ServerWebExchangeDecorator

在Spring-webflux编程中,为我们提供了ServerHttpRequest和ServerWebExchange的包装类,只要我们扩展这两个类,就可以扩展实现我们需要的功能,下面我献上我的示例。

1、封装ServerHttpRequestDecorator类型,构造方法中有个参数是ServerHttpRequest,通过封装getBody()方法,来缓存body内容进行第二次获取

public class PartnerServerHttpRequestDecorator extends ServerHttpRequestDecorator {
    private final LogUtil logger = new LogUtil(this.getClass());
    private final StringWriter cachedCopy = new StringWriter();
    private InputStream dataBuffer;
    private DataBuffer bodyDataBuffer;
    private int getBufferTime = 0;
    private byte[] bytes;

    PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) {
        super(delegate);
    }

    @Override
    public Flux<DataBuffer> getBody() {
//        return Flux.just(dataBuffer);
        if (getBufferTime == 0) {
            getBufferTime++;
            Flux<DataBuffer> flux = super.getBody();
            return flux
                    .publishOn(single())
                    .map(this::cache)
                    .doOnComplete(() -> trace(getDelegate(), cachedCopy.toString()));


        } else {
//            return Flux.just(dataBuffer);
            return Flux.just(getBodyMore());
        }

    }


    private DataBuffer getBodyMore() {
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
        DataBuffer dataBuffer = nettyDataBufferFactory.wrap(bytes);
        bodyDataBuffer = dataBuffer;
        return bodyDataBuffer;
    }

    private DataBuffer cache(DataBuffer buffer) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            dataBuffer = buffer.asInputStream();
//            json = mapper.readValue(dataBuffer, JSONObject.class);
            bytes = IOUtils.toByteArray(dataBuffer);
            NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
            DataBuffer dataBuffer = nettyDataBufferFactory.wrap(bytes);
            bodyDataBuffer = dataBuffer;
            return bodyDataBuffer;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void trace(ServerHttpRequest request, String requestBody) {
        logger.info(requestBody);
    }
}

2、因为在Filter中,传参都是以ServerWebExchange传递参数的,所以我们要再对ServerWebExchange进行一次封装,以便采用我们的requst。

public final class PartnerServerWebExchangeDecorator extends ServerWebExchangeDecorator {

    private final ServerHttpRequestDecorator requestDecorator;

    public PartnerServerWebExchangeDecorator(ServerWebExchange delegate) {
        super(delegate);
        this.requestDecorator = new PartnerServerHttpRequestDecorator(delegate.getRequest());
    }

    @Override
    public ServerHttpRequest getRequest() {
        return requestDecorator;
    }
}

3、然后我们随便找个Filter进行替换即可。
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {

        UsernamePasswordAuthenticationToken auth = (new UsernamePasswordAuthenticationToken("", "", null));
        SecurityContextImpl securityContext = new SecurityContextImpl();
        securityContext.setAuthentication(auth);

        //用serverExchange包装类替换SerberWebExchange,以便后续可以进行扩展操作
        PartnerServerWebExchangeDecorator exchangeDecorator = new PartnerServerWebExchangeDecorator(exchange);


        return this.requiresAuthenticationMatcher.matches(exchangeDecorator)

4、后面我们就要对获取的body进行验签了,获取的body格式为FLux<DataBuffer>,我们怎么从中获取到字符串呢,代码如下,可以把DataBuffer读取到一个字节数组里面,注意,读取完后,一定要调用release()方法释放DataBuffer,否则可能造成内存泄漏。

bytes = new byte[buffer.readableByteCount()];
            dataBuffer.read(bytes);
            //释放buffer资源
            DataBufferUtils.release(buffer);