HttpClient异步方法(例如PostAsync,GetAsync等),如果抛出异常,则返回AggregateException。现在,聚合异常可以包含内部异常列表。我的问题是,有人可以提供一个示例,其中http客户端上的一个异步方法会导致多个内部异常?
是否可以安全地假设虽然可能存在内部异常列表,但在大多数情况下,您只会获得一个内部异常?
我知道他们为什么被抛出以及如何处理它。
为了更清楚,是否可以单次调用http客户端async方法抛出聚合异常,在其内部异常列表中有多个异常?
答案 0 :(得分:1)
如果您查看HttpClient.SendAsync
(这是用于发送所有请求的内部方法),您会发现正在创建的Task
是一个简单的TaskCompletionSource<HttpResponseMessage>
。在调用方法中,它多次设置this.SetTaskFaulted
,但总是在if-else
块内。 可能发生的是,当SetTaskFaulted
设置异常时,它可能会引发另一个异常:
private void SetTaskFaulted(HttpRequestMessage request, CancellationTokenSource cancellationTokenSource, TaskCompletionSource<HttpResponseMessage> tcs, Exception e, TimerThread.Timer timeoutTimer)
{
this.LogSendError(request, cancellationTokenSource, "SendAsync", e);
tcs.TrySetException(e);
HttpClient.DisposeCancellationTokenAndTimer(cancellationTokenSource, timeoutTimer);
}
DisposeCancellationTokenAndTimer
在内部部署CancellationToken
,并在finally
块内部配置计时器:
private static void DisposeCancellationTokenAndTimer(CancellationTokenSource cancellationTokenSource, TimerThread.Timer timeoutTimer)
{
try
{
cancellationTokenSource.Dispose();
}
catch (ObjectDisposedException)
{
}
finally
{
HttpClient.DisposeTimer(timeoutTimer);
}
}
计时器可能会从其Dispose
方法中抛出异常。虽然我确定非常罕见。
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
this.CheckDisposed();
HttpClient.CheckRequestMessage(request);
this.SetOperationStarted();
this.PrepareRequestMessage(request);
CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.pendingRequestsCts.Token);
TimerThread.Timer timeoutTimer = this.SetTimeout(linkedCts);
TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>();
try
{
base.SendAsync(request, linkedCts.Token).ContinueWithStandard(delegate(Task<HttpResponseMessage> task)
{
try
{
this.DisposeRequestContent(request);
if (task.IsFaulted)
{
this.SetTaskFaulted(request, linkedCts, tcs, task.Exception.GetBaseException(), timeoutTimer);
}
else
{
if (task.IsCanceled)
{
this.SetTaskCanceled(request, linkedCts, tcs, timeoutTimer);
}
else
{
HttpResponseMessage result = task.Result;
if (result == null)
{
this.SetTaskFaulted(request, linkedCts, tcs, new InvalidOperationException(SR.net_http_handler_noresponse), timeoutTimer);
}
else
{
if (result.Content == null || completionOption == HttpCompletionOption.ResponseHeadersRead)
{
this.SetTaskCompleted(request, linkedCts, tcs, result, timeoutTimer);
}
else
{
this.StartContentBuffering(request, linkedCts, tcs, result, timeoutTimer);
}
}
}
}
}
catch (Exception ex)
{
if (Logging.On)
{
Logging.Exception(Logging.Http, this, "SendAsync", ex);
}
tcs.TrySetException(ex);
}
});
}
catch
{
HttpClient.DisposeTimer(timeoutTimer);
throw;
}
return tcs.Task;
答案 1 :(得分:0)
如果您等待Task.WhenAll()
,并且该WhenAll中的多个任务抛出异常,则会聚合它们的异常。
TPL抛出像这样的聚合异常是设计的,因此一个任务的异常不会因为异常发生而丢失。
示例:
var failTask1 = Task.Run(() => { throw new Exception("Example 1"); });
var failTask2 = Task.Run(() => { throw new Exception("Another example"); });
await Task.WhenAll(failTask1, failTask2);