在一次项目上线时,发现部分接口请求失败,但是无返回数据是会成功的问题。
开始排查
一开始以为是通信加解密导致的问题,但是部分接口可以,部分接口报错,又排除了这个选项。
于是考虑是网关问题,还是服务端代码问题。
于是找到我们的运维,手动写了一条curl命令,手动调服务端接口,接口返回正常
curl 'http://******/gw/Order/getOrderList' \
--header 'sign: 189762E1DCCDE0F5A93327FEB3469F45' \
--header 'timestamp: 1677636141' \
--header 'Authorization: Bearer *************************' \
找到问题
我们判断是网关导致的问题,我们先关闭了网关返回数据的加密,发现返回数据正常。
于是我们开始研究是否是代码问题,可是他在开发以及测试环境都是正常的,而且它只是对数据流进行了一次加密,生产环境也没办法调试。
var originBody = context.Response.Body;
try
{var memStream = new MemoryStream();context.Response.Body = memStream;await _next(context);memStream.Position = 0;var responseBody = await new StreamReader(context.Response.Body, Encoding.UTF8).ReadToEndAsync();var encreptedBody = context.EncryptResponse(encryptionConfig, responseBody);responseBody = Convert.ToBase64String(encreptedBody);context.Response.ContentLength = responseBody.Length;var memoryStreamModified = new MemoryStream();var sw = new StreamWriter(memoryStreamModified);sw.Write(responseBody);sw.Flush();memoryStreamModified.Position = 0;await memoryStreamModified.CopyToAsync(originBody);
}
catch (Exception ex)
{_logger.LogError(ex, "ResponseEncryptionMiddleware错误");
}
finally
{context.Response.ContentType = "text/plain";context.Response.Body = originBody;//context.Response.ContentType = "text/plain";
}
于是我们将单个接口关闭加密返回数据,并本地使用网关调用生产接口。也就是在生产的接口外面做一次代理。
我们请求了产生问题的接口,发现了问题,我们从读取到的context.Response.Body是乱码

可是这里怎么可能是乱码呢,我们查看了返回的编码方式,他是正常的application/json; charset=utf-8内容。

尝试了好久还是找不到问题,突然无意中发现返回的响应头中有两个未见的字段:Content-Encoding,Transfer-Encoding

感觉就是他们连个搞得鬼,Content-Encoding是返回数据流编码格式,Transfer-Encoding是传输编码。
查了下百度发现和Transfer-Encoding没啥关系,那主要原因就是Content-Encoding这个东西了
已知的压缩方式有5种:
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
Content-Encoding: identity
Content-Encoding: br
服务端返回编码压缩方式是根据客户端传入参数Accept-Encoding判断的,
Accept-Encoding: gzip
Accept-Encoding: compress
Accept-Encoding: deflate
Accept-Encoding: br
Accept-Encoding: identity
Accept-Encoding: *
使用Postman调用是移除掉Accept-Encoding 参数后,接口返回参数正常。
单独保留deflate参数,接口返回参数正常。看来就是返回的编码格式了。
参考了一下百度的代码,仅保留br编码,对返回的数据流就行解密,得到的数据正常。
using StreamReader reader = new StreamReader(new BrotliStream(memStream, CompressionMode.Decompress));
responseBody = await reader.ReadToEndAsync();
处理问题
于是根据编码格式对文件流进行不同方式的解压
if (context.Response.Headers.ContentEncoding.ToString().ToLower().Contains("br"))//比如编码
{using StreamReader reader = new StreamReader(new BrotliStream(memStream, CompressionMode.Decompress));responseBody = await reader.ReadToEndAsync();
}
else if (context.Response.Headers.ContentEncoding.ToString().ToLower().Contains("gzip"))//gzip编码
{using StreamReader reader = new StreamReader(new GZipStream(memStream, CompressionMode.Decompress));responseBody = await reader.ReadToEndAsync();
}
else//其他
{ responseBody = await new StreamReader(context.Response.Body, Encoding.UTF8).ReadToEndAsync();
}
可是又产生了新的问题,接口返回无响应,估计又是编码的问题。
我想既然解压了,是不是还要压缩一下,对返回数据进行压缩代码尝试,均以失败告终。
由于时间比较紧,还要忙其他的事情,只好不走压缩
在Request请求进来时,将压缩方式改为默认方式
context.Request.Headers.AcceptEncoding = default;
参考文章:
- https://blog.51cto.com/u_13758447/5985186