Xamarin.Forms中HttpClient请求抛出“发送请求时发生错误”问题排查求助
嘿,你碰到的这个问题其实是跨平台下HttpClient和WebRequest体系差异的典型坑——老WebRequest的证书验证设置没法直接复用给HttpClient,而且你的Basic认证格式可能也有问题。我来一步步帮你捋清楚:
核心问题拆解
你之前用ServicePointManager.ServerCertificateValidationCallback绕过证书验证的方式只对WebRequest生效,HttpClient有自己独立的请求处理链,在Xamarin的Android和UWP平台上不会自动继承这个设置。而先调用WebRequest再用HttpClient能工作,只是碰巧复用了WebRequest创建的ServicePoint,这完全是不可靠的跨平台解决方案。另外,你的Basic认证头构造不符合标准规范,这也会导致请求被拒绝。
解决方案步骤
1. 用自定义HttpClientHandler处理证书验证
别再依赖ServicePointManager了,直接给HttpClient配置专属的HttpClientHandler,它才是负责底层请求(包括证书验证)的核心组件:
public RestService() { // 修复Basic认证格式:标准要求是「用户名:密码」整体做Base64编码 // 还原你原逻辑里的用户信息格式 var rawCredential = $"{user.Replace("%CP1252%", "")}:{pswd}"; // 用你指定的CP1252编码处理 var credentialBytes = Encoding.GetEncoding("CP1252").GetBytes(rawCredential); var encodedAuth = Convert.ToBase64String(credentialBytes); // 创建自定义Handler,专门处理证书验证 var httpHandler = new HttpClientHandler(); httpHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, errors) => { // 注意:生产环境别直接返回true,建议验证证书的颁发者、有效期等信息 // 这里先暂时绕过测试 return true; }; // 用这个Handler实例化HttpClient client = new HttpClient(httpHandler); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth); // 强制使用安全TLS协议 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; }
2. 修正异步请求的写法
你的GetDatas方法可以简化,同时确保异步流程正确(别用Wait(),避免死锁):
public async Task<string> GetDatas(string url) { var uri = new Uri(url); try { var response = await client.GetAsync(uri); // 主动检查请求状态,失败时抛出异常便于排查 response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } catch (HttpRequestException e) { Console.WriteLine($"请求失败: {e.Message}"); if (e.InnerException != null) { Console.WriteLine($"内部错误详情: {e.InnerException.Message}"); } return string.Empty; } }
3. 平台特定配置
Android平台:
- 在
AndroidManifest.xml中添加网络权限:<uses-permission android:name="android.permission.INTERNET" /> - 如果ERP用的是自签名证书或HTTP协议(Android 9+默认禁止明文流量),需要在
Resources/xml下创建network_security_config.xml:
然后在<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">srverp</domain> </domain-config> </network-security-config>AndroidManifest.xml的application标签中引用:android:networkSecurityConfig="@xml/network_security_config"
- 在
UWP平台:
- 在
Package.appxmanifest中开启内网访问权限:勾选Private Networks (Client & Server) - 如果是自签名证书,需要将证书导入到UWP项目的证书存储中,或者在代码中额外处理信任逻辑。
- 在
4. 移除冗余的WebRequest调用
现在你可以直接调用HttpClient方法,不需要依赖WebRequest的"预热"了:
public async Task<IEnumerable<Operation>> GetItemsAsync(bool forceRefresh = false) { await GetOperationsAsync(); // 直接调用HttpClient方法 return Operations; }
为什么之前的临时方法能工作?
当你先调用WebRequest时,它会创建一个ServicePoint并应用你设置的证书验证回调,HttpClient在某些情况下会复用这个ServicePoint,所以请求能成功。但跨平台下这种复用行为完全不可预测,尤其是在Android上,所以必须给HttpClient单独配置Handler才是可靠方案。
内容的提问来源于stack exchange,提问作者Toinouchiwa

