关于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);