2014-12-23 19 views
0

HttpClient 비동기 메소드 (예 : PostAsync, GetAsync 등)는 예외가 발생하면 AggregateException을 반환합니다. 이제 집계 예외에는 내부 예외 목록이 포함될 수 있습니다. 제 질문은 누군가가 http 클라이언트에서 비동기 메소드 중 하나가 둘 이상의 내부 예외로 이어지는 예제를 제공 할 수 있습니까?HttpClient 비동기 메소드, 집계 예외

잠재적으로 내부 예외 목록이 있지만 대부분의 경우 내부 예외는 하나만 있다고 가정하는 것이 안전합니까?

저는 왜 그들이 던져지고 있는지 그리고 어떻게 다루는 지 알고 있습니다.

이렇게 명확하게하기 위해 집계 예외를 throw하는 http 클라이언트 비동기 메서드에 대한 단일 호출이 내부 예외 목록에 두 개 이상의 예외를 포함 할 수 있습니까?

+1

'작업 '이 반환되고 하나 이상의 예외를 캡슐화 할 수 있기 때문에 'AggregationException'이 발생합니다. 이러한 비동기 메소드에 대해'await'하면 코드 내에 하나 이상의 예외가 발생했는지를 추측하지 않아도됩니다. –

+0

죄송합니다. 명확하지 않은 경우 왜 던져 졌는지 그리고 처리하는 방법을 알고 있습니다. 나는 위의 시나리오에 대한 예를 든 사람이 있는지 알고 싶었습니다. 개인적으로 만들 수 없기 때문에 교육 목적으로 만 필요합니다. – Mayoweezy

+2

하나의 예외를 던지는 것에 의존하지 않겠습니다. 아마도 일반적인 유스 케이스 일 것이지만, 항상 놀랄 수 있습니다. 교육 목적을위한 것이라면 아마도 대부분의 경우 * 단 하나의 예외가 발생하지만 결코 그것에 의존해서는 안된다고 말하는 것이 합리적 일 것입니다. 그들은'AggregationException.Flatten' (내부 예외가'AggregationException' 유형이었던 경우)을 호출 할 수 있고 주어진 enumerable을 반복 할 수 있습니다. –

답변

1

, 당신이 작성중인 Task 간단한 TaskCompletionSource<HttpResponseMessage> 것을 볼 수 있습니다. 호출 메서드 내에서 this.SetTaskFaulted을 여러 번 설정하지만 항상 if-else 블록 안에 설정합니다.

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); 
    } 
} 

무엇 잠재적으로 일어날 수있는 것은 SetTaskFaulted 예외를 설정할 때, 또 다른 예외를 설정할 수 있다는 것입니다 타이머가 잠재적으로 Dispose 메서드에서 예외를 throw 할 수 있습니다. 매우 드물 긴하지만 확신합니다.

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; 
0

Task.WhenAll()을 기다리고 있고 WhenAll에서 예외를 throw하는 여러 작업이있는 경우 해당 예외가 집계됩니다.

이와 같은 집계 예외를 throw하는 TPL은 의도적으로 설계된 것이므로 다른 작업에서 예외가 발생했기 때문에 한 작업에 대한 예외가 손실되지 않습니다.

예 :

당신이 (내부 방법은 모든 요청을 보내는 데 사용되고있는) HttpClient.SendAsync 내부를 보면
var failTask1 = Task.Run(() => { throw new Exception("Example 1"); }); 
var failTask2 = Task.Run(() => { throw new Exception("Another example"); }); 
await Task.WhenAll(failTask1, failTask2);