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

无论在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);


猜你喜欢

转载自blog.csdn.net/lz710117239/article/details/80651361
今日推荐