2015-12-03 10 views
6

Ich stoße auf ein Problem mit der .NET HttpClient Klasse (.NET 4.5.1, System.Net.Http v4.0.0.0). Ich rufe HttpClient.GetAsync an und übergebe eine CancellationToken (als Teil eines Nuget-Pakets, das Aufrufe zwischen Webservices abstrahiert). Wenn das Token vor dem Aufruf abgebrochen wurde, wird die Anforderung durchlaufen, ohne eine Ausnahme auszulösen. Dieses Verhalten scheint nicht korrekt zu sein..NET HttpClient - abgebrochen CancellationToken Anfrage nicht abbrechen

Mein Test (unvollständig, nicht vollständig geschrieben - keine Ausnahme Prüfung):

[TestMethod] 
public async Task Should_Cancel_If_Cancellation_Token_Called() 
{ 
    var endpoint = "nonexistent"; 
    var cancellationTokenSource = new CancellationTokenSource(); 

    var _mockHttpMessageHandler = new MockHttpMessageHandler(); 
    _mockHttpMessageHandler 
     .When("*") 
     .Respond(HttpStatusCode.OK); 

    var _apiClient = new ApiClientService(new HttpClient(_mockHttpMessageHandler)); 
    cancellationTokenSource.Cancel(); 

    var result = await _apiClient.Get<string>(endpoint, null, cancellationTokenSource.Token); 
} 

Die Methode, die ich bin Tests:

public async Task<T> Get<T>(string endpoint, IEnumerable<KeyValuePair<string, string>> parameters = null, CancellationToken cancellationToken = default(CancellationToken)) 
{ 
    var builder = new UriBuilder(Properties.Settings.Default.MyEndpointHost + endpoint); 
    builder.Query = buildQueryStringFromParameters(parameters); 

    _httpClient.DefaultRequestHeaders.Accept.Clear(); 
    _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

    try 
    { 
     // After this, we really shouldn't continue. 
     var request = await _httpClient.GetAsync(builder.Uri, cancellationToken); 

     if (!request.IsSuccessStatusCode) 
     { 
      if (request.StatusCode >= HttpStatusCode.BadRequest && request.StatusCode < HttpStatusCode.InternalServerError) 
      { 
       throw new EndpointClientException("Service responded with an error message.", request.StatusCode, request.ReasonPhrase); 
      } 

      if (request.StatusCode >= HttpStatusCode.InternalServerError && (int)request.StatusCode < 600) 
      { 
       throw new EndpointServerException("An error occurred in the Service endpoint.", request.StatusCode, request.ReasonPhrase); 
      } 
     } 

     var json = await request.Content.ReadAsStringAsync(); 
     return JsonConvert.DeserializeObject<T>(json); 
    } 
    catch (Exception ex) 
    { 
     throw; 
    } 
} 

Ich weiß, dass ich den Status der Löschung überprüfen Token vor dem Aufruf HttpClient.GetAsync und werfen, wenn die Stornierung angefordert wurde. Ich weiß, dass ich auch einen Delegierten registrieren kann, um die HttpClient-Anfrage abzubrechen. Es scheint jedoch so, als ob die Übergabe des Tokens an die HttpClient-Methode das für mich übernehmen würde (oder sonst, was ist der Sinn?), Also frage ich mich, ob ich etwas verpasse. Ich habe keinen Zugriff auf den Quellcode HttpClient.

Warum überprüft HttpClient.GetAsync nicht mein Stornierungs-Token und bricht den Prozess ab, wenn ich es übergebe?

+0

Welche HTTP-Fehlercodes erwarten Sie von Ihrem CancellationToken? –

Antwort

2

HttpClient überprüft das Löschungs-Token nicht selbst, es gibt es an den Meldungshandler weiter, wenn es seine SendAsync Methode aufruft. Er registriert dann die Fortsetzung der von SendAsync zurückgegebenen Aufgabe und setzt seine eigene Aufgabe als abgebrochen, wenn die vom Nachrichtenhandler zurückgegebene Aufgabe abgebrochen wurde.

Also das Problem in Ihrem Szenario ist in Ihrer Implementierung von MockHttpMessageHandler, die scheint nicht die Löschung Token zu überprüfen.

Beachten Sie, dass, wenn HttpClient über seinen leeren Konstruktor aufgerufen wird, intern verwendet es HttpClientHandler, die einen Delegierten Register über die Aufhebung Token, das die Anforderung und löscht die Aufgabe abgebrochen.

+0

D'oh! Es ist immer etwas Einfaches, nicht wahr? Dies macht aber tatsächlich Sinn - ich wusste, dass 'HttpClient' ein Wrapper für den zugrunde liegenden Handler war, aber irgendwie habe ich nicht über die Auswirkungen nachgedacht, wenn es um das Abbruch-Token ging. In diesem Fall würde ich wahrscheinlich nicht in der Lage sein, die zugrunde liegende Implementierung zu testen, es sei denn, ich hätte tatsächlich eine vollständige HTTP-Anfrage gemacht (oder zumindest in den eigentlichen Handler gerufen). –

+0

Sie müssen keine vollständige HTTP-Anfrage stellen. In Ihrem Mock, in der Überschreibung von 'SendAsync', können Sie das Token am Anfang der Methode überprüfen. – tzachs

+0

Ich benutze https://github.com/richardszalay/mockhttp für das Mocking. Ich werde seinem Repo ein Problem hinzufügen, aber mir wurde klar, dass es einfacher sein könnte, die Anfrage ohne den Mock zu stellen, da das Stornierungs-Token dazu führen sollte, dass es trotzdem abgebrochen wird. –

Verwandte Themen