しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Web API よりも HttpClient に注目したい

NuGet で公開されていた System.Net.Http.dll が .NET Framework 4.5 では BCL 入りを果たしました。このアセンブリには HttpClient という全く新しい HTTP のクライアントが入っています。

これって元々は ASP.NET Web API 向けに HTTP を綺麗にラップしたクラスなんですが、正直なところ WebClient とかそんなレベルじゃないぐらい高機能で使い勝手がいいんですよ。

REST 対応

HttpClient に用意されている主な HTTP 通信用のメソッドは以下の通りです。

  • GetAsync
  • PostAsync
  • PutAsync
  • DeleteAsync

はい、見事に HTTP 動詞です。これで RESTful な API でも簡単に叩くことが出来るし、HttpWebRequest で何故か HTTP 動詞を文字列で指定していた世界からおさらば出来ますね。

高レベルな API

HttpClient では HTTP リクエスト・レスポンスを HttpRequestMessage, HttpResponseMessage として扱います。

例えば、今までは POST でフォームデータを送信しようとすると、文字列連結で Key-Value なデータを作成してリクエストのストリームに書き込んだりしましたね。

var request = (HttpWebRequest)WebRequest.Create("http://localhost/");

request.Method = "POST";
request.ContentType = "application/x-www-form-encoded";

var stream = request.GetRequestStream();

var writer = new StreamWriter(stream, Encoding.UTF8);

var formData = "foo=111&bar=222&baz=333";

writer.WriteLine(formData);

...

何とも低レベルな文字列処理とかストリームがずらずらと並んでいます。HttpWebRequest は設計が古すぎて、もはや骨董品とかいうレベルじゃありません。

これが HttpClient を使うと、こんなにもエレガントに書けます。

var httpClient = new HttpClient();

var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    { "foo", "111" },
    { "bar", "222" },
    { "baz", "333" },
});

var response = await httpClient.PostAsync("http://localhost/", content);

ポストするデータ形式ごとに HttpContent の派生クラスが用意されているので、面倒なマルチパート周りの処理も必要なくなりました。

全てが非同期処理

用意されているメソッドが全て非同期版なので、await と組み合わせることでもう何も怖くない。

var httpClient = new HttpClient();

var response = await httpClient.GetAsync("http://www.microsoft.com/");

var contents = await response.Content.ReadAsStringAsync();

// これは上のコードと同じ
//var contents = await httpClient.GetStringAsync("http://www.microsoft.com/");

処理のデリゲーティング

HttpClient コンストラクタに HttpMessageHandler を実装したクラスを指定することで、その HttpClient クラス経由での HTTP リクエスト全てに対して独自の処理を追加することが出来るようになっています。

HttpMessageHandler は抽象クラスになっていて肝心の HTTP リクエストを投げる部分すら実装されていないので、通常は DelegationHandler を継承して SendAsync メソッドをオーバーライドしたほうが楽です。

public class MyMessageHandler : DelegationHandler
{
    public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // HttpRequestMessage を使って何かする

        // 元々の SendAsync を呼び出す
        var response = base.SendAsync(request, cancellationToken);

        // response を使って何かする (ContinueWith で繋いだり)
        return response;
    }
}

そして実際に使うときには以下のようにコンストラクタに指定するだけです。

var httpClient = new HttpClient(new MyMessageHandler());

とまあ、リクエストとレスポンスを使って何かする必要がある場合*1には DelegationHandler を使うことで、HttpClient の呼び出しなどを弄る必要なく対応できるわけですね。

OAuth 実装に関しては Web API を開発している Henrik F Nielsen 氏が Code Recipe にサンプルコードを公開されているので参照してください。素晴らしいアイディアです。

http://code.msdn.microsoft.com/Extending-HttpClient-with-8739f267

*1:OAuth とか OAuth とか