C# Xamarin Android项目调用API遇明文禁止与TLS包头解析错误如何解决
Xamarin Android 调用API两类报错修复方案
错误1:明文HTTP请求被禁止
报错信息
Message "One or more errors occurred. (Cleartext HTTP traffic to 194.141.118.43 not permitted)"
原因
Android 9.0(API 28)及以上系统默认禁用所有明文HTTP请求,防止数据传输泄露。
解决方案
两种方案二选一,生产环境推荐第二种:
- 临时测试方案:直接修改
Properties/AndroidManifest.xml,在<application>节点添加属性:<application ... android:usesCleartextTraffic="true"> - 安全配置方案:只开放指定IP的明文权限
- 在Android项目的
Resources/xml目录下新建network_security_config.xml文件,内容如下:<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="false">194.141.118.43</domain> </domain-config> </network-security-config> - 在
AndroidManifest.xml的<application>节点关联配置:<application ... android:networkSecurityConfig="@xml/network_security_config">
- 在Android项目的
错误2:TLS数据包头解析失败
报错信息
Message "One or more errors occurred. (Unable to parse TLS packet header)"
原因
该报错是HTTPS握手失败导致,常见原因有三个:
- 你请求的
194.141.118.43:3001端口实际运行的是HTTP服务,没有配置HTTPS证书,用HTTPS协议请求HTTP端口就会触发该错误 - 后端用的是自签名SSL证书,Android系统默认不信任私有证书
- Xamarin Android默认的TLS版本和后端服务要求的版本不兼容
解决方案
按优先级依次排查:
- 先确认端口对应的服务协议:直接在浏览器访问
http://194.141.118.43:3001和https://194.141.118.43:3001,看哪个能正常返回数据,如果只有HTTP能通,直接用HTTP+上述的明文权限配置即可 - 如果后端确实是HTTPS服务,先修改Android项目的TLS配置:右键Android项目→属性→Android选项→高级→SSL/TLS实现,选择
Native TLS 1.2+ - 如果是自签名证书问题,需要把后端的证书放到Android项目的
Resources/raw目录,然后在网络安全配置里添加信任规则:<network-security-config> <domain-config> <domain includeSubdomains="false">194.141.118.43</domain> <trust-anchors> <certificates src="@raw/你的证书文件名"/> </trust-anchors> </domain-config> </network-security-config>
代码优化建议
你当前的RestServiceData类有两个不合理的点,建议同步修改:
- 不要初始化多个
HttpClient实例,HttpClient设计为单例复用,频繁创建销毁会导致端口资源泄漏 - 不要用
Task.Run()+Wait()/Result的同步写法调用异步方法,很容易引发主线程死锁,建议把方法改成异步:
public class RestServiceData { // 单例复用HttpClient private static readonly HttpClient _httpClient = new HttpClient(); public async Task<WaterBindingData> GetWaterDataForecastAsync(string query, string query2) { WaterBindingData result = new WaterBindingData(); try { // 并发请求两个接口 var responseTask = _httpClient.GetAsync(query); var response2Task = _httpClient.GetAsync(query2); await Task.WhenAll(responseTask, response2Task); var response = responseTask.Result; var response2 = response2Task.Result; if (response.IsSuccessStatusCode && response2.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); var content2 = await response2.Content.ReadAsStringAsync(); var json = content2.Replace("\"ardaforecast\":[[", "\"ardaforecast\":[ {\"items\": [") .Replace("}],{\"fieldCount\"", "}],\"details\":{\"fieldCount\"") .Replace("}]}", "}}]}"); var waterData = JsonConvert.DeserializeObject<WaterDataJson>(json); var waterStations = JsonConvert.DeserializeObject<WaterStationsJson>(content); result.WaterData = waterData; result.WaterStation = waterStations; } } catch (Exception ex) { Debug.WriteLine("\t\tERROR {0}", ex.Message); } return result; } }
内容的提问来源于stack exchange,提问作者Blagovest Pizhev
相关产品推荐
相关产品推荐

