このチュートリアルでは、GitHub 上の REST サービスに HTTP 要求を発行するアプリを構築します。 アプリは JSON 形式で情報を読み取り、JSON を C# オブジェクトに変換します。 JSON から C# オブジェクトへの変換は 逆シリアル化と呼ばれます。
このチュートリアルでは、次の方法を示します。
- HTTP 要求を送信します。
- JSON 応答を逆シリアル化します。
- 属性を使用して逆シリアル化を構成します。
このチュートリアルの 最後のサンプル に従う場合は、ダウンロードできます。 ダウンロード手順については、サンプルとチュートリアルを参照してください。
[前提条件]
- 最新の .NET SDK
- Visual Studio Code エディター
- C# DevKit
クライアント アプリを作成する
コマンド プロンプトを開き、アプリの新しいディレクトリを作成します。 現在のディレクトリにします。
コンソール ウィンドウで次のコマンドを入力します。
dotnet new console --name WebAPIClientこのコマンドは、基本的な "Hello World" アプリのスターター ファイルを作成します。 プロジェクト名は "WebAPIClient" です。
"WebAPIClient" ディレクトリに移動し、アプリを実行します。
cd WebAPIClientdotnet rundotnet runは、dotnet restoreを自動的に実行して、アプリに必要な依存関係を復元します。 また、必要に応じてdotnet build実行されます。 アプリの出力"Hello, World!"が表示されます。 ターミナルで Ctrl+キーを押してアプリを停止します。
HTTP 要求を行う
このアプリは GitHub API を呼び出して 、.NET Foundation の傘の下にあるプロジェクトに関する情報を取得します。 エンドポイントが https://api.github.com/orgs/dotnet/repos。 情報を取得するために、HTTP GET 要求を行います。 ブラウザーは HTTP GET 要求も行うので、その URL をブラウザーのアドレス バーに貼り付けて、受信して処理する情報を確認できます。
HttpClient クラスを使用して HTTP 要求を行います。 HttpClient では、実行時間の長い API に対して非同期メソッドのみがサポートされます。 そのため、次の手順では非同期メソッドを作成し、Main メソッドから呼び出します。
プロジェクト ディレクトリで
Program.csファイルを開き、その内容を次のように置き換えます。await ProcessRepositoriesAsync(); static async Task ProcessRepositoriesAsync(HttpClient client) { }このコード:
-
Console.WriteLineステートメントを、ProcessRepositoriesAsyncキーワードを使用するawaitの呼び出しに置き換えます。 - 空の
ProcessRepositoriesAsyncメソッドを定義します。
-
Programクラスでは、コンテンツを次の C# に置き換えることで、HttpClientを使用して要求と応答を処理します。using System.Net.Http.Headers; using HttpClient client = new(); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter"); await ProcessRepositoriesAsync(client); static async Task ProcessRepositoriesAsync(HttpClient client) { }このコード:
- すべての要求の HTTP ヘッダーを設定します。
- JSON 応答を受け入れる
Acceptヘッダー -
User-Agentヘッダー。 これらのヘッダーは GitHub サーバー コードによってチェックされ、GitHub から情報を取得するために必要です。
- JSON 応答を受け入れる
- すべての要求の HTTP ヘッダーを設定します。
ProcessRepositoriesAsyncメソッドで、.NET Foundation 組織のすべてのリポジトリの一覧を返す GitHub エンドポイントを呼び出します。static async Task ProcessRepositoriesAsync(HttpClient client) { var json = await client.GetStringAsync( "https://api.github.com/orgs/dotnet/repos"); Console.Write(json); }このコード:
- メソッドの呼び出しから返されたタスク HttpClient.GetStringAsync(String) 待機します。 このメソッドは、指定された URI に HTTP GET 要求を送信します。 応答の本文は Stringとして返されます。これは、タスクの完了時に使用できます。
- 応答文字列
jsonがコンソールに出力されます。
アプリをビルドして実行します。
dotnet runProcessRepositoriesAsyncにawait演算子が含まれるようになったため、ビルド警告はありません。 出力は JSON テキストの長い表示です。
JSON 結果を逆シリアル化する
次の手順では、データをフェッチして処理する方法を簡略化します。 GetFromJsonAsync 📦 NuGet パッケージの一部である拡張メソッドを使用して、JSON 結果を取得してオブジェクトに逆シリアル化します。
Repository.csという名前 の ファイルを作成し、次のコードを追加します。
public record class Repository(string Name);上記のコードでは、GitHub API から返される JSON オブジェクトを表すクラスを定義しています。 このクラスを使用して、リポジトリ名の一覧を表示します。
リポジトリ オブジェクトの JSON には多数のプロパティが含まれていますが、逆シリアル化されるのは
Nameプロパティのみです。 シリアライザーは、ターゲット クラスに一致するものがない JSON プロパティを自動的に無視します。 この機能により、大きな JSON パケット内のフィールドのサブセットのみを操作する型を簡単に作成できます。次のポイントで使用する
GetFromJsonAsyncメソッドには、プロパティ名に関しては大文字と小文字が区別されないという利点がありますが、C# の規則では 、プロパティ名の最初の文字を大文字にします。HttpClientJsonExtensions.GetFromJsonAsync メソッドを使用して JSON をフェッチし、C# オブジェクトに変換します。 GetStringAsync(String) メソッドの
ProcessRepositoriesAsyncの呼び出しを次の行に置き換えます。var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos");更新されたコードは、 GetStringAsync(String) を HttpClientJsonExtensions.GetFromJsonAsyncに置き換えます。
GetFromJsonAsyncメソッドの最初の引数は、await式です。await式はコードのほぼすべての場所に表示できますが、これまでは代入ステートメントの一部としてのみ見てきました。 次のパラメーターrequestUriは省略可能であり、clientオブジェクトの作成時に既に指定されている場合は指定する必要はありません。 要求を送信する URI をclientオブジェクトに指定していないので、ここで URI を指定しました。 最後の省略可能なパラメーターであるCancellationTokenは、コード スニペットでは省略されています。GetFromJsonAsyncメソッドはジェネリックです。つまり、フェッチされた JSON テキストから作成する必要があるオブジェクトの種類の型引数を指定します。 この例では、別の汎用オブジェクトであるList<Repository>であるSystem.Collections.Generic.List<T>に逆シリアル化します。List<T>クラスは、オブジェクトのコレクションを格納します。 型引数は、List<T>に格納されているオブジェクトの型を宣言します。 JSON テキストはリポジトリ オブジェクトのコレクションを表しているため、型引数はRepositoryレコードです。各リポジトリの名前を表示するコードを追加します。 次のように読み取った行を置き換えます。
Console.Write(json);次のコードを使用します。
foreach (var repo in repositories ?? Enumerable.Empty<Repository>()) Console.WriteLine(repo.Name);ファイルの先頭には、次の
usingディレクティブが存在する必要があります。using System.Net.Http.Headers; using System.Net.Http.Json;アプリを実行します。
dotnet run出力は、.NET Foundation の一部であるリポジトリの名前の一覧です。
コードをリファクタリングする
ProcessRepositoriesAsync メソッドは非同期処理を実行し、リポジトリのコレクションを返すことができます。
Task<List<Repository>>を返すようにそのメソッドを変更し、コンソールに書き込むコードを呼び出し元の近くに移動します。
結果が
ProcessRepositoriesAsyncオブジェクトの一覧であるタスクを返すように、Repositoryのシグネチャを変更します。static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)JSON 応答の処理後にリポジトリを返します。
var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos"); return repositories ?? new();このメソッドを
Task<T>としてマークしているため、コンパイラによって戻り値のasyncオブジェクトが生成されます。Program.cs ファイルを変更し、
ProcessRepositoriesAsyncの呼び出しを次のように置き換えて結果をキャプチャし、各リポジトリ名をコンソールに書き込みます。var repositories = await ProcessRepositoriesAsync(client); foreach (var repo in repositories) Console.WriteLine(repo.Name);アプリを実行します。
出力は同じです。
その他のプロパティを逆シリアル化する
次の手順では、GitHub API によって返される JSON ペイロードからさらに多くのプロパティを処理するようにコードを拡張します。 すべてのプロパティを処理する必要はないでしょうが、追加の C# 機能をいくつか追加すると、その他の機能が示されます。
Repositoryクラスの内容を次のrecord定義に置き換えます。 必ずSystem.Text.Json.Serialization名前空間をインポートし、[JsonPropertyName]属性を適用して JSON フィールドを C# プロパティに明示的にマップします。using System.Text.Json.Serialization; public record class Repository( string Name, string Description, [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl, Uri Homepage, int Watchers, [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc );Uri型と
int型には、文字列表現との間で変換する機能が組み込まれています。 JSON 文字列形式からそれらのターゲット型に逆シリアル化するために追加のコードは必要ありません。 JSON パケットにターゲット型に変換されないデータが含まれている場合、シリアル化アクションは例外をスローします。JSON では、多くの場合、プロパティ名に
lowercaseまたはsnake_caseが使用されます。html_urlやpushed_atなどのフィールドは、C# PascalCase の名前付け規則に従っていません。[JsonPropertyName]を使用すると、大文字と小文字が異なる場合やアンダースコアが含まれている場合でも、これらの JSON キーが対応する C# プロパティに正しくバインドされます。 この方法では、C# で PascalCase プロパティ名を許可しながら、予測可能で安定した逆シリアル化が保証されます。 さらに、GetFromJsonAsyncメソッドは、プロパティ名の照合時にcase-insensitiveされるため、それ以上の変換は必要ありません。Program.cs
foreachループを更新して、プロパティ値を表示します。foreach (var repo in repositories) { Console.WriteLine($"Name: {repo.Name}"); Console.WriteLine($"Homepage: {repo.Homepage}"); Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}"); Console.WriteLine($"Description: {repo.Description}"); Console.WriteLine($"Watchers: {repo.Watchers:#,0}"); Console.WriteLine(); }アプリを実行します。
リストに追加のプロパティが含まれるようになりました。
日付プロパティを追加する
最後のプッシュ操作の日付は、JSON 応答で次の形式で書式設定されます。
2016-02-08T21:27:00Z
この形式は世界協定時刻 (UTC) を対象としているため、逆シリアル化の結果は、DateTime プロパティがKindUtc値になります。
タイム ゾーンで表される日付と時刻を取得するには、カスタム変換メソッドを記述する必要があります。
Repository.csでは、日付と時刻の UTC 表現のプロパティと、ローカル時刻に変換された日付を返す読み取り専用の
LastPushプロパティを追加します。ファイルは次のようになります。using System.Text.Json.Serialization; public record class Repository( string Name, string Description, [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl, Uri Homepage, int Watchers, [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc ) { public DateTime LastPush => LastPushUtc.ToLocalTime(); }LastPushプロパティは、 アクセサーのgetを使用して定義されます。setアクセサーはありません。setアクセサーを省略することは、C# で読み取り専用プロパティを定義する 1 つの方法です。 (はい、C# では 書き込み専用 プロパティを作成できますが、値は制限されています)。Program.csに別の出力ステートメント を追加します。
Console.WriteLine($"Last push: {repo.LastPush}");完全なアプリは、次の Program.cs ファイルのようになります。
using System.Net.Http.Headers; using System.Net.Http.Json; using HttpClient client = new(); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter"); var repositories = await ProcessRepositoriesAsync(client); foreach (var repo in repositories) { Console.WriteLine($"Name: {repo.Name}"); Console.WriteLine($"Homepage: {repo.Homepage}"); Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}"); Console.WriteLine($"Description: {repo.Description}"); Console.WriteLine($"Watchers: {repo.Watchers:#,0}"); Console.WriteLine($"{repo.LastPush}"); Console.WriteLine(); } static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client) { var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos"); return repositories ?? new List<Repository>(); }アプリを実行します。
出力には、各リポジトリへの最後のプッシュの日時が含まれます。
次のステップ
このチュートリアルでは、Web 要求を行い、結果を解析するアプリを作成しました。 これで、アプリのバージョンが 完成したサンプルと一致するようになります。
JSON シリアル化を構成する方法の詳細については、「 .NET で JSON をシリアル化および逆シリアル化 (マーシャリングおよびマーシャリング解除) する方法」を参照してください。
.NET