C# HttpClient GetAsync仅首次调用生效 后续调用挂死问题
问题根因
- 最核心的触发点:
HttpClient的GetAsync方法传入空字符串作为请求路径时,不会重新发起指向BaseAddress的新请求,而是会尝试读取当前关联连接上的下一个未被消费的响应帧。第一次调用时连接刚建立,响应帧正常返回;后续调用时连接上没有待读取的新响应,方法会无限等待数据传入直接挂死。 - 写法违背
HttpClient设计规范:每次请求都新建HttpClient和HttpClientHandler实例,会导致底层TCP连接池状态混乱,连接无法被正常释放和复用,进一步放大挂死问题。你之前尝试的单例复用方案没生效,大概率是没有正确释放响应对象,导致连接池占满,新请求排队等待可用连接最终挂死。 - 请求头格式非法:你代码里的
user-agent请求头被手动拆成了两行,实际发送的头里包含了非法的换行空白字符,会触发目标站点WAF的恶意请求检测规则,直接丢弃后续请求的响应包,导致客户端一直等不到返回数据。
正确可运行实现
核心调整点:全局单例复用HttpClient、明确传入请求路径不使用空字符串、修正非法请求头、及时释放响应资源、增加超时和异常校验。
using Newtonsoft.Json.Linq; using System.Net; // 全局单例初始化HttpClient,整个应用生命周期内只初始化一次 private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler { UseCookies = false, // 自动支持gzip/deflate解压,适配大部分站点的响应压缩配置 AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }) { BaseAddress = new Uri("https://www.mywebsite.com/api/"), Timeout = TimeSpan.FromSeconds(10) // 配置合理超时,避免无限挂起 }; static async Task Main(string[] args) { // 默认请求头只初始化一次,不需要每次请求清空重设 _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.Add("Cookie", "session=zgbEqIjfSC7M7QdTTpHDkpWLt"); // user-agent保持单行,不要插入换行 _httpClient.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"); _httpClient.DefaultRequestHeaders.Add("x-requested-with", "XMLHttpRequest"); _httpClient.DefaultRequestHeaders.Add("referer", "https://www.mywebsite.com/"); // 测试连续调用 await GetUserInfo("srmilton"); await GetUserInfo("srmilton"); while (true) { var res = await GetUserInfo("srmilton"); Console.WriteLine($"获取用户信息成功,id: {res.id}, secId: {res.sec_id}"); await Task.Delay(1000); // 增加合理请求间隔,避免被站点限流拦截 } } public static async Task<(string id, string sec_id)> GetUserInfo(string username) { // 明确传入完整接口路径,禁止传空字符串 using var response = await _httpClient.GetAsync("user/detail"); // 主动校验HTTP状态码,请求失败直接抛出明确异常,不会一直卡着 response.EnsureSuccessStatusCode(); string responseString = await response.Content.ReadAsStringAsync(); JObject jsonresponse = JObject.Parse(responseString); string id = jsonresponse["userInfo"]["user"]["id"].ToString(); string sec_id = jsonresponse["userInfo"]["user"]["secUid"].ToString(); return (id, sec_id); }
额外避坑说明
- 禁止在每次请求时用
using新建HttpClient实例,该类设计为全局单例复用,频繁创建销毁会导致套接字耗尽、连接状态异常等问题。 - 所有自定义请求头不要插入无意义的换行、多余空白字符,符合HTTP协议规范,避免被WAF误拦截。
- 所有
HttpResponseMessage对象要用using包裹,确保响应资源被及时释放,连接能正常归还连接池供后续请求复用。 - 不要用
.Result、.Wait()等方式阻塞异步调用,在控制台场景下直接用await即可,不会出现上下文同步导致的死锁。
内容的提问来源于stack exchange,提问作者SrMilton
相关产品推荐
相关产品推荐

