我使用HttpClient's PostAnsyc方法从我的MVC应用程序调用的服务代码中同步调用REST API,但我在DelegatingHandler中丢失了异常。
用法是同步的。我知道异步路径,它不适合我的用例。
以下是我试过的一些变种,它们在超时时没有抛出异常:
//controller action
[HttpPost]
public JsonResult Foo(int id)
{
try
{
var result = _businessService.Foo(id);
return Json(result, JsonRequestBehavior.DenyGet);
}
catch(Exception exception)
{
return Json(exception, JsonRequestBehavior.DenyGet);
}
}
//infrastructure code deep in my application
public HttpResponseMessage Post(Uri uri, StringContent content)
{
return _httpClient.PostAsync(uri, content).Result;
}
//DelegatingHandler code
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
base.SendAsync(request, cancellationToken)
.ContinueWith( t =>
{
if (t.IsFaulted)
{
if(t.Exception != null)
{
taskCompletionSource.TrySetException(t.Exception);
}
}
else if (t.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
try
{
LogResponse(t.Result);
taskCompletionSource.SetResult(t.Result);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
}
}, cancellationToken);
return taskCompletionSource.Task;
}
如何确保我的DelegatingHandler在超时期间不会吞下异常?
答案 0 :(得分:1)
public async Task<HttpResponseMessage> PostAsync(Uri uri, StringContent content)
{
var cancellation = new CancellationTokenSource();
var task = _httpClient.PostAsync(uri, content, cancellation.Token);
var timeout = Task.Delay(5000);
await Task.WhenAny(task, timeout);
if(timeout.IsCompleted)
{
cancellation.Cancel();
throw new TimeoutException();
}
else
return await task;
}
在尝试取消POST操作并抛出超时异常之前,此示例将提供5秒的超时。
不幸的是HttpClient
没有同步方法,所以无论你做什么,另一个线程池线程都在处理请求,你必须等待它。
另一种方法是使用WebRequest,但它不那么花哨,您必须自己序列化您的有效负载(这对NewtonSoft Json库来说并不重要)
答案 1 :(得分:1)
使用CancellationTokenSource
public async Task<HttpResponseMessage> PostAsync(Uri uri, StringContent content)
{
var cancellation = new CancellationTokenSource(5000); // Cancel after 5 seconds.
return await _httpClient.PostAsync(uri, content, cancellation.Token);
}