ツナ缶雑記

ぐうたらSEのブログです。主にマイクロソフト系技術を中心に扱います。

HttpClientを使ってHTTPリクエストの送信前後に任意の処理を差し込む方法

f:id:masatsuna:20200219004757p:plain

.NET Framework 4.5で追加されたHttpClient、非常に便利に使うことができていい感じですよね。 Web APIへのアクセスを簡単に実装できて素晴らしい。

docs.microsoft.com

そんなHttpClientですが、HTTPリクエストを送信する前後に、自由に処理を差し込むことができるようになっています。 本稿ではHttpClientがどのような仕組みで動作するかを簡単に触れて、HTTPリクエストの送信前後に任意の処理を差し込む方法について書いてみようと思います。

前提とする環境

HttpClientの仕組み

HttpClientクラス自身は、HTTPリクエストを送信する機能を持っていません。 実際にHTTPリクエストを送信する処理を行っているのは、HttpClientHandlerという名前のクラスです。

docs.microsoft.com

HttpClientHandlerの内部ではHttpWebRequestを使ってHTTPリクエストを送信しています。

docs.microsoft.com

さて、HttpClientを使ってHTTPリクエストを送信する際、簡単にやると以下のようなコードを書くことになります。

using System.Net.Http;
using System.Threading.Tasks;

public class Hoge
{
    private static HttpClient httpClient = new HttpClient();

    public Task<HttpResponseMessage> Get(string url)
    {
        return httpClient.GetAsync(url);
    }
}

実はHttpClientのコンストラクターには、先ほど説明したHttpClientHandlerのベースクラスであるHttpMessageHandlerのオブジェクトを受け取ることできるオーバーロードが存在します。 上記の例ではHttpClientのインスタンスを生成する際、引数なしコンストラクターを使用していますが、あえてこれを明示して実装するなら、以下のように記述できます。

private static HttpCleint httpClient = new HttpClient(new HttpClientHandler());

これで引数なしコンストラクターを呼び出したのと等価なコードになります。

処理を差し込む方法

さて、HttpClientの仕組みを理解したら、続いて処理を差し込む方法も見ておきましょう。

処理を差し込む場合、DelegatingHandlerを用いるのが非常に便利です。 DelegatingHandlerは別のHttpMessageHandlerクラスに処理を丸ごと移譲することができるようになっています。 移譲するHttpMessageHandlerのオブジェクトをInnerHandlerプロパティに設定することで処理を移譲する仕組みです。 すなわち、実際にHTTPリクエストを送信する処理は、あらかじめ準備されているHttpClientHandlerを使い、差し込みたい処理だけ自分で頑張ってDelegatingHandlerを継承したクラスに実装するのが定石です。

HTTPリクエストを送信する際、SendAsyncメソッドが呼び出されます。 ここに差し込みたい処理を実装します。 処理を差し込むポイントは3か所作ることができます。

  1. HTTPリクエスト送信前
  2. HTTPリクエスト送信直後(レスポンスがまだないタイミング)
  3. HTTPレスポンス受信後の処理

ここに、差し込みたい処理を好きなように実装しましょう。 コードで示すと以下のような形になります。

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class CustomDelegatingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // HTTP リクエスト送信前の処理

        var result = base.SendAsync(request, cancellationToken);

        // HTTP リクエスト送信後の処理

        var response = await result;

        // HTTP レスポンス受信後の処理

        return response;
    }
}

そして最後に、作成したDelegatingHandlerを継承したクラスを、HttpClientのコンストラクターで指定します。 またInnerHandlerプロパティにHttpClientHandlerのオブジェクトを設定し、HTTPリクエストを送信する処理は移譲するようにしておきます。

private static HttpClient httpClient = new HttpClient(
    new CustomDelegatingHandler
    {
        InnerHandler = new HttpClientHandler()
    });

1点だけ注意があります。 DelegatingHandlerのSendAsyncメソッドは、スレッドセーフに実装しなければなりません。 特にWebアプリケーションで使用する場合、複数のスレッドから同時にこのメソッドが呼び出されることになります。

まとめ

HttpClientを使って、HTTPリクエストの送信前後に任意の処理を差し込む方法について説明しました。 非常に簡単に実装できるので、是非使ってみてください。