HttpContent 编码解析,解决汉字字符不能正确解析的问题

HttpContent 编码解析,解决汉字字符不能正确解析的问题

HttpClient中,存在一些汉字字符不能被正确解码的情况,反编译HttpContent,可以观察它是如何实现的?以便于找到原因:

编码解析

  • HttpContent 类中,预定义了4种字符编码
private static Encoding[] EncodingsWithBom = new Encoding[4]
{
    Encoding.UTF8,
    Encoding.UTF32,
    Encoding.Unicode,
    Encoding.BigEndianUnicode
};

  • HttpContent 默认字符编码是 UTF8
 private Encoding DefaultStringEncoding = Encoding.UTF8;
  • 判断字符编码过程

当我们接受到HttpContent 返回的流数据 bufferedContent 时,利用 MemoryStream的GetBuffer()方法,得到创建此流的无符号字节数组

 byte[] buffer = bufferedContent.GetBuffer();//返回创建此流的无符号字节数组
 int num2 = (int)bufferedContent.Length;

根据Headers设置判断字符类型,如果Headers.ContentType.CharSet 设置了字符编码,则encoding返回设定的编码

if (Headers.ContentType != null && Headers.ContentType.CharSet != null)
{
  try
 	{
	 encoding = Encoding.GetEncoding(Headers.ContentType.CharSet);
   }
 catch (ArgumentException innerException)
 {
     tcs.TrySetException(new InvalidOperationException(SR.net_http_content_invalid_charset, innerException));
   return;
 }
 }

如果 encoding 为空,遍历EncodingsWithBom,利用 ByteArrayHasPrefix 方法核对编码,看是否是预置编码。如果不是预置编码,则返回默认编码。

if (encoding == null)
{
	Encoding[] encodingsWithBom = EncodingsWithBom;
	foreach (Encoding encoding2 in encodingsWithBom)
	{
		byte[] preamble = encoding2.GetPreamble();
		if (ByteArrayHasPrefix(buffer, num2, preamble))
		{
			encoding = encoding2;
			num = preamble.Length;
			break;
		}
	}
}

encoding = (encoding ?? DefaultStringEncoding);

ByteArrayHasPrefix

private static bool ByteArrayHasPrefix(byte[] byteArray, int dataLength, byte[] prefix)
{
    if (prefix != null && byteArray != null && prefix.Length <= dataLength && prefix.Length != 0)
    {
        for (int i = 0; i < prefix.Length; i++)
        {
            if (prefix[i] != byteArray[i])
            {
                return false;
            }
        }
        return true;
    }
    return false;
}

解决对策

综上,部分汉字不能正确解码的原因,是因为预置编码集不包含所使用的编码,并且 Headers.ContentType.CharSet没有设定相应的字符编码所导致。

解决办法:
在返回HttpResponseMessage前, 设置charSet。


 string charSet="GB18030" ;//
 private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage req)
{
    var response = await httpClient.SendAsync(req, HttpCompletionOption.ResponseContentRead);

    //设置charSet
    var contentType = response.Content.Headers.ContentType;
    //自动判断字符集(不推荐使用)
    // string charSet = await getCharSetAsync(response.Content);
     contentType.CharSet = charSet;
    return response;

}

//如下是网上发现的一段代码:虽然汉字是乱码,描述字符编码的字符还应该是正确解析的。缺点是对返回数据进行了二次读取。

private async Task<string> getCharSetAsync(HttpContent httpContent)
{

    var charset = httpContent.Headers.ContentType.CharSet;
    if (!string.IsNullOrEmpty(charset))
        return charset;

    var content = await httpContent.ReadAsStringAsync();
    var match = Regex.Match(content, @"charset=(?<charset>.+?)""", RegexOptions.IgnoreCase);
    string charsetValue = null;
    if (!match.Success)
    {
        charsetValue = match.Groups["charset"].Value;
        if (charset == "gb2312")
            charset = "GB18030" ;         //"gbk";
    }
   //这里主要是解决charset=""这种情况,为null,程序就会默认为utf-8
    if (string.IsNullOrEmpty(charset))
        charset = "utf-8";
    return charsetValue;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值