使用HttpClient请求返回错误HTTP状态码的问题排查
问题描述
我写了个Windows Forms应用用来批量检查URL的HTTP状态码,但结果不稳定:本该返回404的URL,批量检查时偶尔会返回200;多次运行同一批URL,每次返回错误状态码的URL都不一样。但单独请求这些URL或者用专门的工具测试时,都能得到正确的404结果。附上代码:
private async void CheckStatusButton_Click(object sender, EventArgs e) { List<string> urlsToCheck = URLs.Text.Split('\n').ToList(); List<string> statusCodeResults = new List<string>(); foreach (string item in urlsToCheck) { using (HttpClientHandler handler = new HttpClientHandler()) { handler.AllowAutoRedirect = true; using (HttpClient client = new HttpClient(handler)) { client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true }; try { using (HttpResponseMessage response = await client.GetAsync(item)) { if (response.StatusCode.ToString() == string.Empty) { break; } if ((int)response.StatusCode == 200) { } if ((int)response.StatusCode == 404) { } if ((int)response.StatusCode == 301) { } statusCodeResults.Add(((int)response.StatusCode).ToString()); } } catch (Exception ex) { statusCodeResults.Add("999" + item.ToString()); continue; } } } } foreach (var item in statusCodeResults) { statusCodes.AppendText(item + Environment.NewLine); } MessageBox.Show("Done"); }
分析与解决办法
1. 核心问题:HttpClient重复创建导致连接异常
每次循环都创建新的HttpClient和HttpClientHandler是大忌。.NET中HttpClient是设计为复用的,频繁创建会导致Socket资源耗尽,引发连接池混乱,最终出现各种异常的响应结果。
解决:
把HttpClient改成静态单例,在应用启动时初始化一次,整个应用生命周期复用:
// 类级别的静态字段 private static readonly HttpClientHandler _httpHandler = new HttpClientHandler { AllowAutoRedirect = true }; private static readonly HttpClient _httpClient = new HttpClient(_httpHandler); // 静态构造函数初始化请求头 static YourFormClassName() { _httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true }; }
之后在循环里直接复用_httpClient即可。
2. URL处理不严谨
用Split('\n')分割的URL可能包含\r(Windows换行符)、空格或者空字符串,请求这种无效URL时,服务器可能会重定向到默认页面(返回200),导致错误结果。
解决:
清理每个URL,过滤无效项:
List<string> urlsToCheck = URLs.Text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(url => url.Trim()) .Where(url => !string.IsNullOrWhiteSpace(url)) .ToList();
3. 自动重定向掩盖真实状态码
AllowAutoRedirect = true时,HttpClient会自动跟随所有重定向,最终返回的是重定向后的状态码。比如某个404的URL被服务器重定向到了首页(200),你得到的就是200,而不是原始的404。
解决:
如果需要获取原始状态码,关闭自动重定向,手动处理:
_httpHandler.AllowAutoRedirect = false;
之后可以检查response.StatusCode是否是3xx,再手动获取重定向地址处理,或者直接记录原始状态码。
4. 结果记录无对应关系
现在只记录状态码,无法知道哪个URL对应哪个结果,排查问题困难。
解决:
把结果改成存储URL和状态码的键值对,比如用Dictionary<string, string>:
Dictionary<string, string> statusCodeResults = new Dictionary<string, string>(); // 成功时 statusCodeResults[url] = ((int)response.StatusCode).ToString(); // 异常时 statusCodeResults[url] = $"999 - {ex.Message}"; // 输出时 foreach (var kvp in statusCodeResults) { statusCodes.AppendText($"{kvp.Key}: {kvp.Value}{Environment.NewLine}"); }
5. 冗余代码与异常处理优化
代码里那些空的if块可以直接删掉;捕获Exception太宽泛,建议捕获特定的HttpRequestException,同时记录更详细的错误信息,方便排查。
修改后的完整示例代码
public partial class YourFormClassName : Form { private static readonly HttpClientHandler _httpHandler = new HttpClientHandler { AllowAutoRedirect = false // 根据需求选择是否开启自动重定向 }; private static readonly HttpClient _httpClient = new HttpClient(_httpHandler); static YourFormClassName() { _httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true }; } public YourFormClassName() { InitializeComponent(); } private async void CheckStatusButton_Click(object sender, EventArgs e) { List<string> urlsToCheck = URLs.Text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(url => url.Trim()) .Where(url => !string.IsNullOrWhiteSpace(url)) .ToList(); Dictionary<string, string> statusCodeResults = new Dictionary<string, string>(); foreach (string url in urlsToCheck) { try { using (HttpResponseMessage response = await _httpClient.GetAsync(url)) { statusCodeResults[url] = $"{(int)response.StatusCode} ({response.StatusCode})"; } } catch (HttpRequestException ex) { statusCodeResults[url] = $"999 - 请求异常: {ex.Message}"; } catch (Exception ex) { statusCodeResults[url] = $"999 - 未知异常: {ex.Message}"; } } statusCodes.Clear(); foreach (var kvp in statusCodeResults) { statusCodes.AppendText($"{kvp.Key}: {kvp.Value}{Environment.NewLine}"); } MessageBox.Show("Done"); } }
内容的提问来源于stack exchange,提问作者Hunter Nelson

