WebAPIでメッセージハンドラーを操作する方法

Web APIのメッセージハンドラーは、着信要求がHttpControllerDispatcherに到達する前に、それを処理、編集、または拒否する機会を提供します。メッセージハンドラーは、リクエスト処理パイプラインのかなり早い段階で実行されるため、WebAPIで横断的関心事を実装するのに最適な場所です。

カスタムメッセージハンドラーの実装

すべてのメッセージハンドラは、クラスHttpMessageHandlerから派生します。独自のメッセージハンドラーを作成するには、DelegatingHandlerクラスを拡張する必要があります。DelegatingHandlerクラスは、HttpMessageHandlerクラスから派生していることに注意してください。

次のWebAPIコントローラーについて考えてみます。

public class DefaultController : ApiController

    {

        public HttpResponseMessage Get()

        {

            return Request.CreateResponse(HttpStatusCode.OK, "Inside the Default Web API Controller...");           

        }

    }

メッセージハンドラーを作成するには、DelegatingHandlerクラスを拡張し、SendAsyncメソッドをオーバーライドする必要があります。

public class Handler : DelegatingHandler

    {

        protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

           return base.SendAsync(request, cancellationToken);

        }

    }

Web APIリクエスト処理パイプラインには、いくつかの組み込みメッセージハンドラーが含まれています。これらには次のものが含まれます。

  • HttpServer-これは、ホストから要求を取得するために使用されます
  • HttpRoutingDispatcher-これは、構成されたルートに基づいて要求をディスパッチするために使用されます
  • HttpControllerDispatcher-これは、それぞれのコントローラーにリクエストを送信するために使用されます

メッセージハンドラーをパイプラインに追加して、次の1つ以上の操作を実行できます。

  • 認証と承認を実行します
  • 着信要求と発信応答のログ
  • 応答ヘッダーを応答オブジェクトに追加します
  • リクエストヘッダーを読み取るか変更する

次のコードスニペットは、WebAPIで単純なメッセージハンドラーを実装する方法を示しています。

public class Handler : DelegatingHandler

{

protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

            var response = new HttpResponseMessage(HttpStatusCode.OK)

            {

                Content = new StringContent("Inside the message handler...")

            };

            var task = new TaskCompletionSource();

            task.SetResult(response);

            return await task.Task;

        }

}

メッセージハンドラーは要求メッセージを処理しません。応答メッセージを作成して返します。以下のコードリストに示すように、着信リクエストで何もしたくない場合は、SendAsyncメソッドのベースバージョンを呼び出すこともできます。

public class Handler : DelegatingHandler

{

protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

            return await base.SendAsync(request, cancellationToken);

        }

}

SendAsyncメソッドで送信されるHttp要求と応答をログに記録するコードを記述することもできます。

Web APIを実行するには、以下のようなテストメソッドを使用できます。

 [TestMethod]

        public void WebAPIControllerTest()

        {

            HttpClient client = new HttpClient();

            var result = client.GetAsync(new Uri("//localhost//api/default/")).Result;

            string responseMessage = result.Content.ReadAsStringAsync().Result;

            Assert.IsTrue(result.IsSuccessStatusCode);

        }

テストメソッドを実行すると、「デフォルトのWeb APIコントローラーの内部...」というメッセージが応答メッセージとして返され、テストに合格します。ああ!メッセージハンドラーを作成しましたが、まだメッセージ処理パイプラインに登録していません。

ここで、カスタムハンドラーが存在する場所をWebAPIインフラストラクチャに通知する必要があります。これを行うには、パイプラインにカスタムハンドラーを登録する必要があります。以下に示すように、WebApiConfigクラスのRegisterメソッドで作成したカスタムメッセージハンドラーを登録できます。

public static void Register(HttpConfiguration config)

{

    GlobalConfiguration.Configuration.MessageHandlers.Add(new Handler());

}

テストメソッドを再度実行すると、「ログメッセージハンドラ内...」というテキストメッセージが応答メッセージとして返され、テストに合格します。

以下のコードスニペットに示すように、複数のメッセージハンドラーをメッセージ処理パイプラインに登録することもできることに注意してください。

public static void Register(HttpConfiguration config)

{

    GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageHandlerA());

    GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageHandlerB());

    GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageHandlerC());

}

メッセージハンドラーは、パイプラインに追加された順序で実行され、応答は逆の順序で返されます。つまり、着信要求時に、メッセージハンドラは登録された順序で実行されます。発信応答中、プロセスは逆になります。したがって、メッセージハンドラは、パイプラインへの登録とは逆の順序で実行されます。

着信リクエストを検査し、リクエストに有効なAPIキーが含まれているかどうかを確認するメッセージハンドラーを実装することもできます。APIキーが存在しないか有効でない場合、適切なエラーメッセージが返されます。次のコードリストは、これを行う方法を示しています-とにかくAPIキーを検証するコードを書くのはあなたに任せています。

protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

            string key = HttpUtility.ParseQueryString(request.RequestUri.Query).Get("key");

            string errorMessage = "You need to specify the api key to access the Web API.";

            try

            {

                if (!string.IsNullOrWhiteSpace(key))

                {

                    return base.SendAsync(request, cancellationToken);

                }

                else

                {

                    HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.Forbidden, errorMessage);

                    throw new HttpResponseException(response);

                }

            }

            catch

            {

                HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.InternalServerError, "An unexpected error occured...");

                throw new HttpResponseException(response);

            }

        }