2015-12-03 18 views
6

.NET HttpClientクラス(.NET 4.5.1、System.Net.Http v4.0.0.0)の問題が発生しました。私はCancellationToken(Webサービス間で呼び出しを抽象化するNugetパッケージの一部として)を渡してHttpClient.GetAsyncと呼んでいます。呼び出しが行われる前にトークンが取り消された場合、要求は例外をスローすることなく処理されます。この現象は正しいとは思われません。.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

CancellationTokenが返すHTTPエラーコードを教えてください。 –

答えて

2

HttpClientキャンセルトークン自体はチェックされず、SendAsyncメソッドを呼び出すとメッセージハンドラに渡されます。 SendAsyncから返されたタスクの継続に登録され、メッセージハンドラから返されたタスクがキャンセルされた場合には、キャンセルされた独自のタスクが設定されます。

あなたのシナリオの問題は、キャンセルトークンを確認していないと思われるMockHttpMessageHandlerの実装にあります。

空のコンストラクタ経由でHttpClientが呼び出された場合、内部では、キャンセルトークンにデリゲートを登録して要求を打ち切り、タスクをキャンセルするというデリゲートを登録します。

+0

D'oh!それはいつも単純なものですね。これは実際には意味がありますが、私は 'HttpClient'が基になるハンドラのラッパーであることを知っていましたが、何とか取り消しトークンになったときの意味については考えていませんでした。この場合、実際には完全なHTTPリクエスト(または少なくとも実際のハンドラへの呼び出し)を行わない限り、基本的な実装を実際にテストすることはできないと思います。 –

+0

完全なHTTP要求を行う必要はありません。あなたのモックでは、 'SendAsync'のオーバーライドで、メソッドの始めにトークンをチェックできます。 – tzachs

+0

私は嘲笑のためにhttps://github.com/richardszalay/mockhttpを使用しています。私は彼のレポに問題を追加しますが、キャンセルトークンはキャンセルされなければならないので、モックなしでリクエストを実際に行う方が簡単かもしれないことに気付きました。 –

関連する問題