委派处理程序应如何进行异步调用(ASP.NET MVC Web API)

本文关键字:NET ASP MVC Web API 调用 程序 处理 异步 何进行 委派 | 更新日期: 2023-09-27 18:37:06

我习惯于在调用内部处理程序的 SendAsync() 之前执行同步工作,并在内部处理程序通过完成完成后执行同步工作。 例如:

protected override Task<HttpResponseMessage> SendAsync( 
    HttpRequestMessage request, CancellationToken cancellationToken) 
{ 
    // do some sync work before inner handler here 
    var sendTask = base.SendAsync(request, cancellationToken); 
    return sendTask.ContinueWith( 
        task => { // do some sync work afterwards here }); 
} 

但是,我现在需要从委派处理程序中调用 IO 绑定操作。IO 绑定操作已包装为Task<bool>。我需要使用结果来确定是否继续使用内部处理程序。

例如,进行网络调用以授权请求。我必须这样做才能与现有系统集成。总的来说,我认为这个问题有有效的方案,它应该有一个可行的解决方案。

在这种情况下,实现 SendAsync 的正确方法是什么,以便我异步执行 IO 绑定任务,然后继续异步执行内部处理程序?

关键点是我想确保请求线程在任何时候都不会被阻塞。

委派处理程序应如何进行异步调用(ASP.NET MVC Web API)

好的,我想我已经破解了。我用一个身份验证方案来说明这一点:我想异步对用户进行身份验证,并使用结果来决定是返回 401 还是继续使用消息处理程序链。

核心问题是,在获得异步身份验证的结果之前,您无法调用内部处理程序 SendAsync()。

对我来说,关键的见解是使用 TaskCompletionSource (TCS) 来控制执行流程。这使我能够从TCS返回任务,并随时为其设置结果 - 最重要的是延迟调用SendAsync(),直到我知道我需要它。

因此,我设置了TCS,然后启动了一个任务来执行授权。在继续这个的过程中,我看一下结果。如果获得授权,我将调用内部处理程序链并附加一个延续(避免任何线程阻塞)以完成 TCS。如果身份验证失败,我只需在那里完成 TCS,然后使用 401。

这样做的结果是,两个异步任务依次执行,没有任何线程阻塞。我对此进行了负载测试,它似乎工作正常。

不过,在

.NET 4.5 中使用异步/等待语法要好得多......尽管TCS的方法基本上仍然在幕后进行,但代码要简单得多。

享受!

第一个代码片段是在 .NET 4.0 上使用 Web API Beta 构建的,第二个代码片段是在 .NET 4.5/Web API RC 上构建的。

protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
    // Authorize() returns a started
    // task that authenticates the user
    // if the result is false we should
    // return a 401 immediately
    // otherwise we can invoke the inner handler
    Task<bool> authenticationTask = Authorize(request);
    // attach a continuation...
    authenticationTask.ContinueWith(_ =>
    {
        if (authenticationTask.Result)
        {
            // authentication succeeded
            // so start the inner handler chain
            // and write the result to the
            // task completion source when done
            base.SendAsync(request, cancellationToken)
                .ContinueWith(t => taskCompletionSource.SetResult(t.Result));
        }
        else
        {
            // authentication failed
            // so complete the TCS immediately
            taskCompletionSource.SetResult(
                new HttpResponseMessage(HttpStatusCode.Unauthorized));
        }
    });
    return taskCompletionSource.Task;
}

这是一个.NET 4.5/Web API候选版本,它使用新的async/await语法更加性感:

protected override async Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    // Authorize still has a Task<bool> return type
    // but await allows this nicer inline syntax
    var authorized = await Authorize(request);
    if (!authorized)
    {
        return new HttpResponseMessage(HttpStatusCode.Unauthorized)
        {
            Content = new StringContent("Unauthorized.")
        };
    }
    return await base.SendAsync(request, cancellationToken);
}