现象
多个接口的请求参数中文偶尔部分乱码,乱码位置不固定
分析
根据日志分析,参数在达到应用服务时乱码,在应用服务前还有Nginx和网关,初步猜测网关出问题的可能性较大
部分乱码,估计是字节级别的处理问题,在网关项目里全局搜索stream 和byte。经过排查,在一处代码出现byte转字符串处理,代码如下
@Override
public Flux<DataBuffer> getBody()
{
Flux<DataBuffer> body = super.getBody();
return body.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);
String bodyStr = new String(content, StandardCharsets.UTF_8);
// 防xss攻击过滤
bodyStr = EscapeUtil.clean(bodyStr);
// 转成字节
byte[] bytes = bodyStr.getBytes();
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
});
}
当请求体过大时,网关会对请求体进行切割,切分为多个字节数组,一个中文字符占2~3个字节,如果切割数组时恰巧将一个中文的多个字节切分到2个数组中(一部分前,一部分后),再将切分后的字节数组转字符串时,就会出现乱码问题。
解决
修改这部分
@Override
public Flux<DataBuffer> getBody()
{
Flux<DataBuffer> body = super.getBody();
return body.buffer().map(dataBuffers -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
DataBufferUtils.release(join);
String bodyStr = new String(content, StandardCharsets.UTF_8);
// 防xss攻击过滤
bodyStr = EscapeUtil.clean(bodyStr);
// 转成字节
byte[] bytes = bodyStr.getBytes();
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
});
}
如何复现
知道问题问题为什么出现,就容易复现了,只需要将请求体多塞点中文,中文越多,请求体越大,越容易复现。
后记
项目作者也修复了这个问题:修复响应体过大出现的乱码问题