C#循环内执行PostAsync操作报错,如何高效发起同步POST请求
报错原因
This instance has already started one or more requests. Properties can only be modified before sending the first request
该错误触发的核心原因是HttpClient实例在完成首次请求发送后,禁止修改BaseAddress、DefaultRequestHeaders等全局配置属性,你的代码将这类配置逻辑放在了循环内部,首次请求执行完成后,第二次循环修改配置就会触发异常。
此外频繁新建HttpClient还会导致TCP端口无法及时复用,长期运行的Hangfire作业很容易出现端口耗尽问题,本身也不符合HttpClient的最佳实践。
修复方案
你需要把HttpClient的全局配置逻辑移到循环外,如果不同请求需要使用不同的地址、请求头,不要修改HttpClient的全局属性,改用HttpRequestMessage为每个请求单独配置参数。同步调用建议使用GetAwaiter().GetResult()替代Result,避免异常堆栈丢失。
优化后的完整代码如下:
class myclass { // 静态单例复用HttpClient,底层自动维护连接池,避免端口耗尽问题 private static readonly HttpClient _httpClient = new HttpClient(); public void process() { // 全局统一配置只执行一次,放在循环外 _httpClient.BaseAddress = baseUri; _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.ConnectionClose = true; foreach(var item in list) { // 此处构造你当前请求对应的body内容 var body = 你的请求内容生成逻辑; // 同步等待请求完成 var response = _httpClient.PostAsync(url, body).GetAwaiter().GetResult(); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); saveResultInDB(result); } } }
不同请求配置不同的适配方案
如果每个循环的请求需要使用不同的BaseAddress、请求头,不要修改HttpClient全局属性,按如下方式单独构造每个请求:
foreach(var item in list) { // 每个请求单独构造消息,配置独有的地址、头信息 using (var request = new HttpRequestMessage(HttpMethod.Post, 你当前请求的完整URL)) { // 单独添加当前请求的头 request.Headers.TryAddWithoutValidation("自定义头", "对应值"); // 设置请求内容 request.Content = body; var response = _httpClient.SendAsync(request).GetAwaiter().GetResult(); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); saveResultInDB(result); } }
内容的提问来源于stack exchange,提问作者Anil
相关产品推荐
相关产品推荐

