概要
HttpClient には GetAsync や PostAsync といった便利な短縮メソッドが用意されていますが、より細かい制御を行いたい場合は SendAsync メソッドを使用します。 HttpRequestMessage クラスを組み合わせることで、リクエストごとに個別のヘッダーを設定したり、標準メソッド以外(HEAD, OPTIONS, PATCH等)を使用したり、HTTPメソッドを動的に切り替えることが可能になります。
仕様(入出力)
- 入力: Web APIのURL、検索キーワード。
- 出力: 取得したJSONデータから条件に一致する項目を抽出してコンソールに表示。
- 前提: .NET 6.0以上。
System.Net.Http.Jsonを使用。
基本の使い方
HttpRequestMessageのインスタンスを作成し、HTTPメソッド(GET, POST等)とURLを指定します。- 必要に応じてリクエスト固有のヘッダーやコンテンツを設定します。
HttpClient.SendAsyncにメッセージを渡して送信します。
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/data");
// このリクエスト専用のヘッダーを追加
request.Headers.Add("X-Custom-Header", "Value");
// 送信
using var response = await client.SendAsync(request);
コード全文
ここでは、テスト用API(JSONPlaceholder)から記事一覧を取得し、タイトルに特定のキーワードが含まれる記事のみを表示するコンソールアプリケーションを作成します。 SendAsync を使用して、リクエストごとに「リクエストID」ヘッダーを付与するシナリオです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers; // ヘッダー操作に必要
using System.Text.Json;
using System.Text.Json.Serialization; // プロパティマッピングに必要
using System.Threading.Tasks;
class Program
{
// HttpClientはシングルトンとして再利用する
private static readonly HttpClient _httpClient = new HttpClient();
static async Task Main()
{
// 1. 共通ヘッダーの設定(クライアント全体に適用)
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("MyTechBlogSearcher/1.0");
// APIのエンドポイント(記事データ)
string url = "https://jsonplaceholder.typicode.com/posts";
string filterKeyword = "optio"; // フィルタリング用キーワード
Console.WriteLine($"リクエスト作成: {url}");
try
{
// 2. リクエストメッセージの構築
// 明示的に GET メソッドを指定
using var request = new HttpRequestMessage(HttpMethod.Get, url);
// 【重要】このリクエストだけの独自ヘッダーを追加
// DefaultRequestHeadersではなく、request.Headersに追加する
request.Headers.Add("X-Request-ID", Guid.NewGuid().ToString());
// 3. 送信 (SendAsync)
using var response = await _httpClient.SendAsync(request);
if (response.StatusCode == HttpStatusCode.OK)
{
// レスポンスストリームを取得
using var stream = await response.Content.ReadAsStreamAsync();
// JSONデシリアライズ(大文字小文字を区別しない設定)
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var articles = await JsonSerializer.DeserializeAsync<List<Article>>(stream, options);
Console.WriteLine($"--- '{filterKeyword}' を含む記事 ---");
// LINQでフィルタリングして表示
if (articles != null)
{
var filtered = articles.Where(a => a.Title.Contains(filterKeyword));
foreach (var item in filtered)
{
Console.WriteLine($"[ID:{item.Id}] {item.Title}");
Console.WriteLine($"URL: https://example.com/posts/{item.Id}"); // 仮想URL
Console.WriteLine("------------------------------------------");
}
}
}
else
{
Console.WriteLine($"エラー: {response.StatusCode}");
}
}
catch (Exception ex)
{
Console.WriteLine($"例外発生: {ex.Message}");
}
}
}
// データモデルクラス
public class Article
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; } = string.Empty;
[JsonPropertyName("body")]
public string Body { get; set; } = string.Empty;
[JsonPropertyName("userId")]
public int UserId { get; set; }
}
カスタムポイント
- HTTPメソッドの変更:
HttpMethod.Getの部分をHttpMethod.Post,Put,Deleteに変更するだけで、あらゆる操作に対応できます。標準にないメソッド(例: PATCH)もnew HttpMethod("PATCH")として指定可能です。 - ボディ(Content)の設定: POSTやPUTを行う場合は、
request.Contentにデータをセットします。C#request.Content = new StringContent(jsonString, Encoding.UTF8, "application/json"); - プロパティ名のマッピング: JSON側の名前(例:
html_url)とC#側の名前(例:HtmlUrl)が異なる場合は、コード例のように[JsonPropertyName("...")]属性を使用するか、JsonSerializerOptionsでPropertyNamingPolicyを設定してください。
注意点
- 使い捨ての制約:
HttpRequestMessageインスタンスは一度送信すると再利用できません(一度送信するとDispose済みのような扱いになる実装が含まれるため)。同じ内容でもう一度送りたい場合は、新しいインスタンスを作成する必要があります。 - Usingの使用:
HttpRequestMessageとHttpResponseMessageはIDisposableを実装しています。リソースリークを防ぐため、可能な限りusingステートメントで囲んで適切に破棄してください。 - ヘッダーの競合:
HttpClient.DefaultRequestHeadersとHttpRequestMessage.Headersの両方に同じヘッダーを設定した場合、基本的にはマージされますが、値の重複等に注意が必要です。
応用
HEADメソッドによる存在確認
ファイルの中身をダウンロードせず、そのファイルが存在するか、サイズはいくつか等の情報(ヘッダー)だけを取得したい場合に SendAsync が役立ちます。
var headRequest = new HttpRequestMessage(HttpMethod.Head, "https://example.com/large-file.zip");
using var response = await _httpClient.SendAsync(headRequest);
if (response.StatusCode == HttpStatusCode.OK)
{
Console.WriteLine($"ファイルサイズ: {response.Content.Headers.ContentLength}");
}
まとめ
HttpClient.SendAsync と HttpRequestMessage を組み合わせる実装パターンは、REST APIを利用する上で最も柔軟性が高い手法です。リクエストごとに異なる認証トークンを付与したり、HEADやOPTIONSといった特殊なメソッドを扱ったりする場合に必須となります。簡易的な GetAsync などで対応しきれない要件が出てきた際は、この SendAsync パターンへ切り替えることで、HTTP通信の細部までコントロールすることが可能になります。
