c# - .NET HttpClient - 取消 CancellationToken 不取消请求

标签 c# .net dotnet-httpclient cancellation

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

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

[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 之前检查取消 token 的状态,如果请求取消则抛出。我知道我也可以注册一个委托(delegate)来取消 HttpClient 请求。但是,似乎将 token 传递给 HttpClient 方法应该为我解决这个问题(或者,这有什么意义?)所以我想知道我是否遗漏了什么。我无权访问 HttpClient 源代码。

为什么 HttpClient.GetAsync 在我传入取消 token 时不检查我的取消 token 并中止其进程?

最佳答案

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

所以您的场景中的问题在于您的 MockHttpMessageHandler 实现似乎没有检查取消 token 。

请注意,如果通过其空构造函数调用 HttpClient,它会在内部使用 HttpClientHandler,后者会在取消 token 上注册一个委托(delegate),以中止请求并取消任务。

关于c# - .NET HttpClient - 取消 CancellationToken 不取消请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34073104/

相关文章:

.net - 使用HttpClient在PostAsync之后处理对象

C# HttpClient 上传速度慢

c# - Entity Framework 7 DbContext 脚手架

c# - 在不覆盖现有输出文件的情况下使用 Stream CopyTo()

c# - .Net 中的多个音频输出

c# - .Net 多个应用程序实例但单个文档

unit-testing - 如何使用 token 对帖子(web-api)调用进行单元测试?

c# - 为什么尝试添加到空值不会抛出 InvalidOperationException?

c# - 如何在 C# 中发现 onvif 设备

c# - 使用 CodeDom 分析代码结构?