HttpClient异步方法,聚合异常

时间:2014-12-23 11:27:10

标签: c# dotnet-httpclient aggregateexception

HttpClient异步方法(例如PostAsync,GetAsync等),如果抛出异常,则返回AggregateException。现在,聚合异常可以包含内部异常列表。我的问题是,有人可以提供一个示例,其中http客户端上的一个异步方法会导致多个内部异常?

是否可以安全地假设虽然可能存在内部异常列表,但在大多数情况下,您只会获得一个内部异常?

我知道他们为什么被抛出以及如何处理它。

为了更清楚,是否可以单次调用http客户端async方法抛出聚合异常,在其内部异常列表中有多个异常?

2 个答案:

答案 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);