如何确保DelegatingHandlers在超时时抛出异常?

时间:2015-10-01 13:28:02

标签: c# asp.net-mvc asynchronous

我使用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在超时期间不会吞下异常?

2 个答案:

答案 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);
}