WinForms发送HTTP请求到Facebook时抛出InvalidOperationException异常
问题背景
- 正在开发类Postman的HTTP请求应用,当前优先落地GET请求能力,用户输入URL后点击按钮会触发
RequestService.GetRequest()方法,完成请求发送、响应内容展示、HTTP状态码标识全流程。 - 现有实现测试时,向Google站点、GitHub用户仓库接口发送请求均运行正常,富文本框可正常加载返回数据;但向Facebook站点发送请求时无任何返回提示,也未弹出异常通知,同链接使用Postman请求可正常返回HTML页面。
- 调试时查看Visual Studio输出标签页,发现发起Facebook请求后立刻抛出报错:
Exception thrown: 'System.InvalidOperationException' in mscorlib.dll。初步怀疑是请求头配置问题但无法确认,缺少明确的排查方向和解决思路。
现有问题实现代码
public static async Task GetRequest(string url, RichTextBox ResponseRichTextBox, Label StatusCodeLabel) { string response = ""; var client = new HttpClient(); client.DefaultRequestHeaders.Add("User-Agent", "C# program"); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var resultContent = await client.GetStringAsync(url); var resultStatusCode = await client.GetAsync(url); Console.WriteLine("Output: " + resultContent.ToString()); ResponseRichTextBox.Text = resultContent.ToString(); if (resultStatusCode.IsSuccessStatusCode) StatusCodeLabel.Parent.BackColor = Color.MediumSeaGreen; else StatusCodeLabel.Parent.BackColor = Color.Crimson; StatusCodeLabel.Parent.Show(); StatusCodeLabel.Text = resultStatusCode.StatusCode.ToString(); }
问题根因
- 重复发起请求:代码对同一个目标URL连续发起两次GET请求,第一次调用
GetStringAsync拉取内容后,又调用GetAsync重复请求,既浪费连接资源,部分站点对非标准的高频重复请求会直接触发拦截。 - 请求头配置错误:代码硬编码添加了
Accept: application/json请求头,声明客户端只接受JSON格式响应,但Facebook首页返回的是text/html类型内容,GetStringAsync检测到响应内容类型与声明接受的类型不匹配时,会直接抛出InvalidOperationException,这就是调试时看到的报错来源。 - 缺少异常捕获:整个方法没有包裹异常处理逻辑,请求抛出异常时直接中断执行流程,既不会更新UI控件,也不会弹出错误提示,最终表现为用户侧看不到任何反馈。
- HttpClient使用不规范:每次请求都新建一个HttpClient实例,该对象底层占用套接字资源,频繁创建销毁会触发套接字耗尽问题,长期运行会出现随机连接故障。
修复方案
核心改动点
- 全局复用单例HttpClient实例:配置自动重定向、自动解压能力,对齐Postman、浏览器的默认请求行为,避免套接字耗尽问题
- 修正请求头配置:移除硬编码的JSON类型接受限制,默认配置接受所有内容类型,配置通用浏览器User-Agent降低被站点拦截概率
- 消除重复请求:单次请求复用同一个响应对象,同时从中读取状态码和响应内容,减少不必要的连接开销
- 补充异常捕获逻辑:覆盖全链路请求异常场景,异常时在UI给出明确提示,避免无响应的假死问题
修复后可运行代码
// 全局单例复用HttpClient,禁止每次请求新建实例 private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler() { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli }) { Timeout = TimeSpan.FromSeconds(30) }; // 静态构造函数初始化默认请求头,仅执行一次 static RequestService() { _httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); // 配置接受所有类型响应,对齐Postman默认行为 _httpClient.DefaultRequestHeaders.Accept.ParseAdd("*/*"); } public static async Task GetRequest(string url, RichTextBox ResponseRichTextBox, Label StatusCodeLabel) { try { // 仅发起一次GET请求,复用响应对象读取所有数据 using var response = await _httpClient.GetAsync(url); var resultContent = await response.Content.ReadAsStringAsync(); Console.WriteLine("Output: " + resultContent); // 若从非UI线程调用该方法,需追加Control.Invoke逻辑处理跨线程UI更新 ResponseRichTextBox.Text = resultContent; StatusCodeLabel.Parent.BackColor = response.IsSuccessStatusCode ? Color.MediumSeaGreen : Color.Crimson; StatusCodeLabel.Parent.Show(); StatusCodeLabel.Text = $"{(int)response.StatusCode} {response.StatusCode}"; } catch (Exception ex) { // 异常场景下给出明确反馈 ResponseRichTextBox.Text = $"请求失败:{ex.Message}"; StatusCodeLabel.Parent.BackColor = Color.Crimson; StatusCodeLabel.Parent.Show(); StatusCodeLabel.Text = "请求异常"; } }
验证说明
修复后请求Facebook站点时,不会再因内容类型不匹配抛出InvalidOperationException,可正常拉取返回的HTML内容;所有异常场景都会在响应框展示明确错误信息,不会出现无提示的假死情况;长期运行也不会出现套接字耗尽导致的连接故障。
内容的提问来源于stack exchange,提问作者Mario Mateaș
相关产品推荐
相关产品推荐

