【问题标题】:.NET HttpClient - cancelled CancellationToken not cancelling request.NET HttpClient - 取消 CancellationToken 不取消请求
【发布时间】:2016-03-08 11:40:38
【问题描述】:

我遇到了 .NET HttpClient 类(.NET 4.5.1,System.Net.Http v4.0.0.0)的问题。我打电话给HttpClient.GetAsync,传入CancellationToken(作为抽象Web服务之间调用的Nuget包的一部分)。如果在进行调用之前令牌已被取消,则请求将通过而不会引发异常。这种行为似乎不正确。

我的测试(不完整,未完全编写 - 没有异常检查):

[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 在我传递它时不检查我的取消令牌并中止其进程?

【问题讨论】:

  • 您希望您的 CancellationToken 返回哪些 HTTP 错误代码?

标签: c# .net dotnet-httpclient cancellation


【解决方案1】:

HttpClient 不检查取消令牌本身,它在调用其SendAsync 方法时将其传递给消息处理程序。然后它注册到从SendAsync 返回的任务的延续,如果从消息处理程序返回的任务被取消,它会将自己的任务设置为已取消。

因此,您的方案中的问题在于您对 MockHttpMessageHandler 的实现,这似乎没有检查取消令牌。

请注意,如果HttpClient 通过其空构造函数调用,则它在内部使用HttpClientHandler 在取消令牌上注册一个委托,以中止请求并取消任务。

【讨论】:

  • 天啊!它总是很简单,不是吗。不过,这实际上是有道理的——我知道HttpClient 是底层处理程序的包装器,但不知何故,我没有考虑到取消令牌的含义。在这种情况下,我想除非我真的发出完整的 HTTP 请求(或至少调用到实际的处理程序中),否则我真的无法测试底层实现。
  • 您不必发出完整的 HTTP 请求。在您的模拟中,在SendAsync 的覆盖中,您可以检查方法开头的令牌。
  • 我正在使用github.com/richardszalay/mockhttp 进行模拟。我将在他的仓库中添加一个问题,但我意识到在没有模拟的情况下实际发出请求可能更容易,因为取消令牌应该会导致它被取消。
猜你喜欢
  • 2023-03-06
  • 1970-01-01
  • 2019-03-09
  • 1970-01-01
  • 2013-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
相关资源
最近更新 更多