.NET HttpClient - 取消 CancellationToken 不取消请求
.NET HttpClient - cancelled CancellationToken not cancelling request
我 运行 遇到了 .NET HttpClient
class(.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
在我传入取消令牌时不检查我的取消令牌并中止其进程?
HttpClient
本身不检查取消令牌,它在调用其 SendAsync
方法时将其传递给消息处理程序。然后它注册到从 SendAsync
返回的任务的延续,如果从消息处理程序返回的任务被取消,它将把自己的任务设置为已取消。
所以你的场景中的问题是你的 MockHttpMessageHandler
的实现似乎没有检查取消令牌。
请注意,如果通过其空构造函数调用 HttpClient
,它会在内部使用 HttpClientHandler
,它会在取消令牌上注册一个委托,以中止请求并取消任务。
我 运行 遇到了 .NET HttpClient
class(.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
在我传入取消令牌时不检查我的取消令牌并中止其进程?
HttpClient
本身不检查取消令牌,它在调用其 SendAsync
方法时将其传递给消息处理程序。然后它注册到从 SendAsync
返回的任务的延续,如果从消息处理程序返回的任务被取消,它将把自己的任务设置为已取消。
所以你的场景中的问题是你的 MockHttpMessageHandler
的实现似乎没有检查取消令牌。
请注意,如果通过其空构造函数调用 HttpClient
,它会在内部使用 HttpClientHandler
,它会在取消令牌上注册一个委托,以中止请求并取消任务。