.NET HttpClient - 取消令牌无法取消请求

9
我遇到了一个与.NET HttpClient类相关的问题(.NET 4.5.1,System.Net.Http v4.0.0.0)。我调用HttpClient.GetAsync方法,并传入CancellationToken(作为抽象化WebService之间调用的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错误码? - IgorGanapolsky
1个回答

3
HttpClient并未直接检查取消标记,而是在调用其SendAsync方法时将其传递给消息处理程序。然后,它会在从SendAsync返回的任务上注册继续操作,并且如果来自消息处理程序的任务被取消,它将设置自己的任务为已取消。
因此,在您的方案中遇到问题的问题在于您对MockHttpMessageHandler实现似乎未检查取消标记。
请注意,如果通过空构造函数调用HttpClient,它将在内部使用HttpClientHandler,该处理程序在取消标记上注册委托以终止请求并取消任务。

1
哎呀!总是有些简单的问题,不是吗?但这其实是有道理的——我知道 HttpClient 是底层处理程序的包装器,但在涉及取消令牌时,我却没有考虑到其影响。在这种情况下,我想我只有真正发出完整的 HTTP 请求(或至少调用实际处理程序)才能测试底层实现。 - user677526
您无需进行完整的HTTP请求。在您的模拟中,通过重写SendAsync方法,可以在该方法的开头检查令牌。 - tzachs
我正在使用https://github.com/richardszalay/mockhttp进行模拟。我将在他的存储库中添加一个问题,但是我意识到如果取消令牌应该会导致它被取消,那么实际上不使用模拟进行请求可能会更容易。 - user677526

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接