【C#】HTTPメソッドとヘッダーを詳細に制御してAPIを呼び出す方法

目次

概要

HttpClient には GetAsyncPostAsync といった便利な短縮メソッドが用意されていますが、より細かい制御を行いたい場合は SendAsync メソッドを使用します。 HttpRequestMessage クラスを組み合わせることで、リクエストごとに個別のヘッダーを設定したり、標準メソッド以外(HEAD, OPTIONS, PATCH等)を使用したり、HTTPメソッドを動的に切り替えることが可能になります。

仕様(入出力)

  • 入力: Web APIのURL、検索キーワード。
  • 出力: 取得したJSONデータから条件に一致する項目を抽出してコンソールに表示。
  • 前提: .NET 6.0以上。System.Net.Http.Json を使用。

基本の使い方

  1. HttpRequestMessage のインスタンスを作成し、HTTPメソッド(GET, POST等)とURLを指定します。
  2. 必要に応じてリクエスト固有のヘッダーやコンテンツを設定します。
  3. 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("...")] 属性を使用するか、JsonSerializerOptionsPropertyNamingPolicy を設定してください。

注意点

  1. 使い捨ての制約: HttpRequestMessage インスタンスは一度送信すると再利用できません(一度送信するとDispose済みのような扱いになる実装が含まれるため)。同じ内容でもう一度送りたい場合は、新しいインスタンスを作成する必要があります。
  2. Usingの使用: HttpRequestMessageHttpResponseMessageIDisposable を実装しています。リソースリークを防ぐため、可能な限り using ステートメントで囲んで適切に破棄してください。
  3. ヘッダーの競合: HttpClient.DefaultRequestHeadersHttpRequestMessage.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.SendAsyncHttpRequestMessage を組み合わせる実装パターンは、REST APIを利用する上で最も柔軟性が高い手法です。リクエストごとに異なる認証トークンを付与したり、HEADやOPTIONSといった特殊なメソッドを扱ったりする場合に必須となります。簡易的な GetAsync などで対応しきれない要件が出てきた際は、この SendAsync パターンへ切り替えることで、HTTP通信の細部までコントロールすることが可能になります。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次