ASP.NET Web API の HttpClient メッセージ ハンドラー

メッセージ ハンドラーは、HTTP 要求を受け取り、HTTP 応答を返すクラスです。

通常、一連のメッセージ ハンドラーは連鎖しています。 最初のハンドラーは HTTP 要求を受け取り、何らかの処理を行って、次のハンドラーにその要求を渡します。 ある時点で応答が作成され、連鎖をさかのぼります。 このパターンは、デリゲート ハンドラーと呼ばれます。

Diagram of message handlers chained together, illustrating process to receive an H T T P request and return an H T T P response.

クライアント側では、HttpClient クラスはメッセージ ハンドラーを使用して要求を処理します。 既定のハンドラーは HttpClientHandler で、ネットワーク経由で要求を送信し、サーバーから応答を取得します。 カスタム メッセージ ハンドラーをクライアント パイプラインに挿入できます。

Diagram of process to insert custom message handlers into client pipeline. Shows h t t p Client class that uses a message handler to process requests.

Note

ASP.NET Web API では、サーバー側のメッセージ ハンドラーも使用されます。 詳細については、HTTP メッセージ ハンドラーを参照してください。

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

カスタム メッセージ ハンドラーを記述するには、System.Net.Http.DelegatingHandler から派生し、SendAsync メソッドをオーバーライドします。 このメソッド シグネチャを次に示します。

Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken);

このメソッドは、HttpRequestMessage を入力として受け取り、HttpResponseMessage を非同期的に返します。 一般的な実装では、次の処理が行われます。

  1. 要求メッセージを処理します。
  2. base.SendAsync を呼び出して、内部ハンドラーに要求を送信します。
  3. 内部ハンドラーは応答メッセージを返します。 (この手順は非同期です。)
  4. 応答を処理し、呼び出し元に返します。

次の例は、送信要求にカスタム ヘッダーを追加するメッセージ ハンドラーを示しています。

class MessageHandler1 : DelegatingHandler
{
    private int _count = 0;

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        System.Threading.Interlocked.Increment(ref _count);
        request.Headers.Add("X-Custom-Header", _count.ToString());
        return base.SendAsync(request, cancellationToken);
    }
}

base.SendAsync の呼び出しは非同期です。 ハンドラーがこの呼び出しの後に何らかの処理を行う場合は、await キーワードを使用して、メソッドの完了後に実行を再開します。 次の例は、エラー コードをログに記録するハンドラーを示しています。 ログ自体はあまり興味深くはありませんが、この例ではハンドラー内の応答を取得する方法を示しています。

class LoggingHandler : DelegatingHandler
{
    StreamWriter _writer;

    public LoggingHandler(Stream stream)
    {
        _writer = new StreamWriter(stream);
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        if (!response.IsSuccessStatusCode)
        {
            _writer.WriteLine("{0}\t{1}\t{2}", request.RequestUri, 
                (int)response.StatusCode, response.Headers.Date);
        }
        return response;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _writer.Dispose();
        }
        base.Dispose(disposing);
    }
}

クライアント パイプラインへのメッセージ ハンドラーの追加

HttpClient にカスタム ハンドラーを追加するには、HttpClientFactory.Create メソッドを使用します。

HttpClient client = HttpClientFactory.Create(new Handler1(), new Handler2(), new Handler3());

メッセージ ハンドラーは、Create メソッドに渡す順序で呼び出されます。 ハンドラーは入れ子になっているため、応答メッセージは他の方向に移動します。 つまり、最後のハンドラーが最初に応答メッセージを取得します。