2015-12-03 75 views
6

我遇到的問題與.NET HttpClient類(.NET 4.5.1,System.Net.Http V4.0.0.0)。我打電話HttpClient.GetAsync,在CancellationToken傳遞(如抽象web服務之間呼叫的NuGet包的一部分)。如果在進行調用之前令牌已被取消,則請求會通過而不會引發異常。這種行爲似乎不正確。.NET的HttpClient - 取消的CancellationToken不會取消請求

我的測試(不完全的,不完全寫入 - 也不例外檢查):

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

我用於測試的方法:

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

我知道,我可以檢查取消狀態令牌,然後致電HttpClient.GetAsync,並在請求取消時拋出。我知道我也可以註冊一個委託來取消HttpClient請求。然而,看起來好像將令牌傳遞給HttpClient方法應該爲我處理這個問題(或者,別的,有什麼意義?),所以我想知道我是否錯過了一些東西。我無權訪問HttpClient源代碼。

爲什麼HttpClient.GetAsync不檢查我的取消標記,並中止其進程當我通過它?

+0

的HTTP錯誤代碼做你希望你的CancellationToken返回? –

回答

2

HttpClient不檢查取消標記本身,它在調用SendAsync方法時將它傳遞給消息處理程序。然後它註冊到從SendAsync返回的任務的延續部分,如果從消息處理程序返回的任務被取消,它將自己的任務設置爲取消。

因此,在您的方案的問題是在你執行MockHttpMessageHandler這似乎不檢查取消標記。

請注意,如果HttpClient通過其空的構造函數被調用,它將在內部使用HttpClientHandler,它在註銷令牌上註冊一個委派,用於中止請求並取消該任務。

+0

D'oh!這總是很簡單,不是嗎。但是,這實際上確實有道理 - 我知道'HttpClient'是底層處理程序的包裝器,但不知何故,當它涉及到取消標記時,我沒有考慮它的含義。在這種情況下,我想我真的不能測試底層的實現,除非我實際完成了一個完整的HTTP請求(或者至少被調用到實際的處理程序中)。 –

+0

您不必提出完整的HTTP請求。在你的模擬中,在'SendAsync'的覆蓋中,你可以在方法的開頭檢查標記。 – tzachs

+0

我使用https://github.com/richardszalay/mockhttp進行嘲諷。我會在他的回購中添加一個問題,但是意識到,實際上可以更容易地在沒有模擬的情況下發出請求,因爲取消令牌應該導致它被取消。 –