在使用异步方法时,当使用HttpClient时,标头会被设置为null。
在使用异步方法时,当使用HttpClient时,标头会被设置为null。
我正在使用.NET Framework 4.6.1。
我在我的Web API中有一个控制器,其中我有一个静态的HttpClient来处理所有的HTTP请求。在我将我的应用程序托管在IIS上之后,大约每个月一次,我会对我的应用程序的所有传入请求都收到以下异常:
System.ArgumentNullException: 值不能为空。 at System.Threading.Monitor.Enter(Object obj) at System.Net.Http.Headers.HttpHeaders.ParseRawHeaderValues(String name, HeaderStoreItemInfo info, Boolean removeEmptyHeader) at System.Net.Http.Headers.HttpHeaders.AddHeaders(HttpHeaders sourceHeaders) at System.Net.Http.Headers.HttpRequestHeaders.AddHeaders(HttpHeaders sourceHeaders) at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request) at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpClient.PutAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken) at Attributes.Controllers.AttributesBaseController.d__6.MoveNext() in D:\Git\PortalSystem\Attributes\Controllers\AttributesBaseController.cs:line 42
如果我重新启动IIS上的应用程序池,一切都会重新开始正常工作。这是我拥有的代码:
public class AttributesBaseController : ApiController { [Inject] public IPortalsRepository PortalsRepository { get; set; } private static HttpClient Client = new HttpClient(new HttpClientHandler { Proxy = null, UseProxy = false }) { Timeout = TimeSpan.FromSeconds(double.Parse(WebConfigurationManager.AppSettings["httpTimeout"])) }; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); protected async Task UpdateAttributes(int clientId, int? updateAttrId = null) { try { Client.DefaultRequestHeaders.Accept.Clear(); Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); #region Update Client Dossier !!! BELOW IS LINE 42 !!!! using (var response = await Client.PutAsync(new Uri(WebConfigurationManager.AppSettings["dossier"] + "api/dossier?clientId=" + clientId), null)) { if (!response.IsSuccessStatusCode) { logger.Error($"Dossier update failed"); } } #endregion #region Gather Initial Info var checkSystems = PortalsRepository.GetCheckSystems(clientId); var currentAttributes = PortalsRepository.GetCurrentAttributes(clientId, checkSystems); #endregion Listtasks = new List (); #region Initialize Tasks foreach (var cs in checkSystems) { if (!string.IsNullOrEmpty(cs.KeyValue)) { tasks.Add(Task.Run(async () => { var passedAttributes = currentAttributes.Where(ca => ca.SystemId == cs.SystemId && ca.AttributeId == cs.AttributeId && (ca.SysClientId == cs.KeyValue || ca.OwnerSysClientId == cs.KeyValue)).ToList(); if (cs.AttributeId == 2 && (updateAttrId == null || updateAttrId == 2)) { await UpdateOpenWayIndividualCardsInfo(passedAttributes, cs, clientId); } else if (cs.AttributeId == 3 && (updateAttrId == null || updateAttrId == 3)) { await UpdateEquationAccountsInfo(passedAttributes, cs, clientId); } else if (cs.AttributeId == 8 && (updateAttrId == null || updateAttrId == 8)) { await UpdateOpenWayCorporateInfo(passedAttributes, cs, clientId); } else if (cs.AttributeId == 9 && (updateAttrId == null || updateAttrId == 9)) { await UpdateEquationDealsInfo(passedAttributes, cs, clientId); } else if (cs.AttributeId == 10 && (updateAttrId == null || updateAttrId == 10)) { await UpdateOpenWayIndividualCardDepositsInfo(passedAttributes, cs, clientId); } else if (cs.AttributeId == 16 && (updateAttrId == null || updateAttrId == 16)) { await UpdateOpenWayBonusInfo(passedAttributes, cs, clientId); } else if (cs.AttributeId == 17 && (/*updateAttrId == null ||*/ updateAttrId == 17)) { await UpdateExternalCardsInfo(passedAttributes, cs, clientId); } if (cs.AttributeId == 18 && (updateAttrId == null || updateAttrId == 18)) { await UpdateCRSInfo(passedAttributes, cs, clientId); } else if (cs.AttributeId == 22 && (updateAttrId == null || updateAttrId == 22)) { await UpdateCardInsuranceInfo(passedAttributes, cs, clientId); } })); } } #endregion // Run all tasks await Task.WhenAny(Task.WhenAll(tasks.ToArray()), Task.Delay(TimeSpan.FromSeconds(double.Parse(WebConfigurationManager.AppSettings["taskWaitTime"])))); } catch (Exception ex) { logger.Error(ex); } } }
有人可以给我建议/帮助找出问题吗?我只是不知道问题是我如何使用带有任务的HttpClient还是在IIS上发生了一些错误。
从上面的内容可以看出,HttpClient
在使用异步方法时,使用DefaultRequestHeaders
时会出现头部被置空的问题。这个问题的出现是因为在DefaultRequestHeaders
的实现中,它使用了一个简单的字典来存储头部信息,而字典的Remove
方法并不是线程安全的。当我们在迭代字典的同时,又有其他线程在添加/删除元素时,就会导致头部信息被置空的情况发生。
要解决这个问题,有两个解决方法:
- 在静态构造函数中初始化
DefaultRequestHeaders
,然后不再对它进行修改。 - 使用
SendAsync
方法发送请求,并在自定义的请求头中添加头部信息,而不是使用PutAsync
方法。
在上面的内容中还提供了一个小的复现代码,用来演示这个问题的产生。通过多次运行这段代码,就可以看到线程在并发访问字典时可能会陷入无限循环的情况。
,通过修改代码,使用适当的方法来处理头部信息,就可以解决这个问题。在每个请求中添加特定的头部信息应该在HttpRequestMessage
中完成,而不是在HttpClient
的DefaultRequestHeaders
中进行修改。
感谢提供详细的解释。我已经修改了代码并在服务器上更新了应用程序。现在,我将监视它的行为。
这样修复了吗?