2012-06-14 6 views
11

内部ハンドラのSendAsync()を呼び出す前に同期処理を実行し、内部ハンドラが完了して同期処理が完了した後に同期作業を実行することに満足しています。例:DelegatingHandlerは非同期呼び出しをどのように行うべきですか(ASP.NET MVC Web API)?

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バインドタスクを非同期で実行してから、内部ハンドラを非同期に実行し続けますか?

要点は、リクエストスレッドがいつでもブロックされていないことを確認したいということです。

答えて

15

いいえ、私はこれが割れていると思います。私は認証シナリオでこれを説明しています。ユーザーを非同期で認証し、その結果を使用して401を返すか、メッセージハンドラチェーンを続行するかを決定します。

主な問題は、非同期認証の結果が得られるまで内部ハンドラSendAsync()を呼び出すことができないことです。

私の主な洞察は、TaskCompletionSource(TCS)を使用して実行フローを制御することでした。これにより私はTCSからタスクを返し、好きなときに結果を設定できました。そして、最も重要なのは、SendAsync()を必要なことがわかるまで待つことです。

私はTCSをセットアップしてから、承認を行うタスクを開始しました。これに続き、私は結果を見る。承認されている場合は、内側のハンドラチェーンを呼び出し、(スレッドがブロックされないようにする)に続けて、TCSを完成させるを付けます。認証が失敗した場合は、TCSを完了してから401を実行するだけです。

この結果、両方の非同期タスクがスレッドブロッキングなしで順番に実行されます。私はこれをロードしてロードし、それは正常に動作するようです。

.NET 4.5では、async/awaitの構文を使った方がはるかにいいです... TCSのアプローチは基本的には基本的にはカバーではありますが、コードははるかに簡単です。

お楽しみください!

最初のスニペットは、.NET APIを使用して.NET 4.0でビルドされました.Web API Betaは.NET 4.5/Web API RCで2番目のスニペットです。ここで

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 /ウェブAPIのリリース候補版非同期/の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); 
} 
+0

はどのようにあなたが 'Authorize'を実現したのですか?あなたは新しい 'HttpClient'をインスタンス化しましたか? – JobaDiniz

+0

Authorizeの実装はここでは関係ありません。要求を許可するかどうかを決定するのは単なる非同期関数なので、実装する方法はあなた次第です。例えば、現在のユーザがいくつかの注文されたビジネスルールに基づいて現在のリクエストを行うためのアクセス権を持っているかどうかを確認するために、データベースをチェックすることができます。 –

+0

もう一つのhttpリクエストを作成するかどうかは不思議でした。私のシナリオでは、現在のものより前に別のhttpコールを行う必要があり、別の 'HttpClient'がインスタンス化されています。私は、ハンドラの中で現在のものを再利用できるかどうか疑問に思っていましたが、それはできないようです。 – JobaDiniz

関連する問題