2015-12-03 16 views
6

Si è verificato un problema con la classe .NET HttpClient (.NET 4.5.1, System.Net.Http v4.0.0.0). Sto chiamando HttpClient.GetAsync, passando in un CancellationToken (come parte di un pacchetto Nuget che astrae le chiamate tra i servizi web). Se il token è stato cancellato prima che venga effettuata la chiamata, la richiesta passa senza generare un'eccezione. Questo comportamento non sembra corretto..NET HttpClient - Cancellazione annullataToken non annulla richiesta

Il mio test (incompleta, non pienamente scritta - nessun controllo eccezione):

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

Il metodo che sto testando:

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

So che posso controllare lo stato della cancellazione token prima di chiamare HttpClient.GetAsync e lanciare se la cancellazione è stata richiesta. So che posso anche registrare un delegato per annullare la richiesta HttpClient. Tuttavia, sembra che passare il token al metodo HttpClient dovrebbe occuparsi di questo per me (o, altrimenti, qual è il punto?), Quindi mi chiedo se mi manca qualcosa. Non ho accesso al codice sorgente HttpClient.

Perché HttpClient.GetAsync non controlla il mio token di annullamento e interrompe il processo quando lo passo?

+0

Quali codici di errore HTTP si aspettano che ReturnToken restituisca? –

risposta

2

HttpClient non verifica il token cancellazione sé, lo passa al gestore di messaggi quando chiama il suo metodo SendAsync. Quindi registra la continuazione sull'attività restituita da SendAsync e imposterà la propria attività come annullata se l'attività restituita dal gestore messaggi è stata annullata.

Quindi il problema nel tuo scenario è nella tua implementazione di MockHttpMessageHandler che sembra non controllare il token di cancellazione.

nota, che se HttpClient è chiamato tramite il suo costruttore vuoto, si utilizza internamente HttpClientHandler che registra un delegato sul token di cancellazione che interrompe la richiesta e annulla l'operazione.

+0

D'oh! È sempre qualcosa di semplice, no? Questo in realtà ha senso, però - Sapevo che 'HttpClient' era un wrapper per il gestore sottostante, ma in qualche modo non pensavo alle implicazioni quando si trattava del token di cancellazione. In questo caso, immagino che non sarei in grado di testare l'implementazione sottostante a meno che non avessi fatto una richiesta HTTP completa (o almeno chiamata nel gestore reale). –

+0

Non è necessario effettuare una richiesta HTTP completa. Nella tua simulazione, nella sovrascrittura di 'SendAsync', puoi controllare il token all'inizio del metodo. – tzachs

+0

Sto utilizzando https://github.com/richardszalay/mockhttp per il beffardo. Aggiungerò un problema al suo repository, ma mi sono reso conto che potrebbe essere più semplice effettuare effettivamente la richiesta senza il mock poiché il token di cancellazione dovrebbe comunque essere cancellato. –

Problemi correlati