演習 - HttpClient を使用して REST サービスを利用する
あなたは、エンジニアが顧客サイトの訪問時に使うアプリの一部として、エンジニアが電気コンポーネントの詳細を調べることができる機能を追加する必要があります。 この情報はデータベースに保持し、REST Web サービスを介してアクセスします。 また、管理者が同じ REST Web サービスを使ってデータベースに保持されているパーツの詳細を作成、削除、変更できるようにするインターフェイスを用意するように求められました。
この演習では、REST Web サービスを Azure にデプロイし、Web ブラウザーを使ってアクセスできることを確認します。 次に、REST Web サービスを使って電気コンポーネントの詳細を取得、追加、削除、更新する機能を既存のアプリに追加します。
この演習は、Azure サンドボックスを使って実行します。
ヒント
[コピー] ボタンを使用して、コマンドをクリップボードにコピーできます。 貼り付けるには、Cloud Shell ターミナル内で新しい行を右クリックして [貼り付け] を選択するか、Shift + Insert キーボード ショートカット (macOS では ⌘ + V) を使用します。
Parts REST Web サービスをデプロイする
Cloud Shell ウィンドウで、次のコマンドを実行して、Parts REST Web サービスなど、この演習のコードを含むリポジトリをクローンします。
git clone https://github.com/microsoftdocs/mslearn-dotnetmaui-consume-rest-services
Consume-REST-services フォルダーに移動します。
cd mslearn-dotnetmaui-consume-rest-services/src
次のコマンドを実行し、Azure Cloud Shell サンドボックスを使って Parts Web サービスをデプロイします。 このコマンドを実行すると、一意の URL を介してサービスを使用できるようになります。 この URL が表示されたら、メモします。 この URL を使って Web サービスに接続するようにアプリを構成します。
bash initenvironment.sh
Web サービスのコードを調べる
Note
この演習の残りの部分は、ローカルの開発用コンピューター上で実行します。
お使いのコンピューター上でコマンド プロンプト ウィンドウを開き、この演習用にリポジトリをクローンします。 コードは net-maui-learn-consume-rest-services リポジトリにあります。
git clone https://github.com/microsoftdocs/mslearn-dotnetmaui-consume-rest-services
Note
ビルドによって生成されたファイルが最大パス長を超えないようにするため、演習コンテンツを C:\dev などの短いフォルダー パスに複製またはダウンロードすることをお勧めします。
リポジトリのクローン内の src\webservice\PartsServer フォルダーに移動し、Visual Studio を使って PartsServer.sln ソリューション、またはVisual Studio Code 内のフォルダーを開いてください。 このソリューションには、先ほどの手順で Azure にデプロイした Web サービス用のコードのコピーが含まれています。
ソリューション エクスプローラー ウィンドウで Models フォルダーを展開します。 このフォルダーには 2 つのファイルが含まれています。
Part.cs。 Part クラスは、REST Web サービスによって提供されるパーツを表しています。 フィールドには、パーツ ID、パーツ名、パーツの種類、入手可能日 (パーツが最初に供給された日)、サプライヤー一覧が含まれています。 Href プロパティは、パーツの相対 URI を返します。REST クライアントはこの URI を使って、REST Web サービス内のこの特定のパーツを参照することができます。 Suppliers プロパティは、このパーツのサプライヤー一覧を文字列として返します。
PartsFactory.cs。 PartsFactory クラスは、ハードコーディングされた値の小さなセットを使って、サービスによって提供されたパーツ一覧を初期化します。 実際には、このデータはデータベースから取得されます。
ソリューション エクスプローラー ウィンドウで Controllers フォルダーを展開します。 このフォルダーには次のファイルが格納されています。
PartsController.cs。 PartsController クラスは、サービスの Web API を実装しています。 これに含まれているメソッドは、クライアント アプリケーションからすべてのパーツ一覧を取得する (Get)、パーツ ID を指定して特定のパーツの詳細を検索する (Get のオーバーロード バージョン)、パーツの詳細を更新する (Put)、新しいパーツを一覧に追加する (Post)、パーツを一覧から削除する (Delete) ものです。
LoginController.cs。 LoginController クラスは、Web サービスの簡単な認証形式を実装しています。 アプリから、このコントローラーに HTTP GET 要求を送信する必要があります。その結果、認可トークンが返されます。 この認可トークンは、PartsController に送信される要求の認証に使われます。
BaseController.cs。 BaseController クラスには、要求の認証に使うロジックが含まれています。 PartsController クラスは、このクラスを継承します。 クライアントが有効な認証トークンを指定せずに PartsController クラスのメソッドを呼び出そうとすると、HTTP 401 (権限がありません) 応答が返されます。
.NET MAUI クライアント アプリのコードを確認する
このモジュールでは、.NET 8.0 SDK を使います。 適切なコマンド ターミナルで次のコマンドを実行して、.NET 8.0 がインストールされていることを確認します。
dotnet --list-sdks
次の例のような出力が表示されます。
6.0.317 [C:\Program Files\dotnet\sdk]
7.0.401 [C:\Program Files\dotnet\sdk]
8.0.100 [C:\Program Files\dotnet\sdk]
8
で始まるバージョンが一覧に表示されていることを確実にします。 何も表示されない場合、またはコマンドが見つからない場合は、最新の .NET 8.0 SDK をインストールしてください。
PartsServer ソリューションを閉じ、クローンしたリポジトリの src\client\PartsClient フォルダー内にある PartsClient ソリューションを開きます。 このソリューションには、PartsServer Web サービスを使う .NET MAUI クライアント アプリ実装の一部が含まれています。
ソリューション エクスプローラー ウィンドウで Data フォルダーを展開します。 このフォルダーには、2 つのクラスのコードが含まれています。
PartsManager.cs。 PartsManager クラスには、クライアント アプリが REST Web サービスと対話するために使うメソッドが用意されています。 現在のところ、このクラスは未完成です。この演習で必要なコードを追加します。 完成すると、GetClient メソッドを使って REST Web サービスに接続できます。 GetAll メソッドを使って REST Web サービスからパーツ一覧を返すことができます。 Add メソッドを使って REST Web サービスが管理するパーツ一覧に新しいパーツを追加できます。 Update メソッドを使って REST Web サービスによって格納されたパーツの詳細を変更できます。また、Delete メソッドを使ってパーツを削除できます。
Part.cs。 Part クラスを使って、データベースに格納されたパーツをモデル化します。 PartID、PartName、PartAvailableDate、PartType、PartSuppliers の各フィールドにアプリケーションからアクセスするために使用できるプロパティを公開します。 このクラスには、SupplierString というユーティリティ メソッドも用意されており、アプリケーションはこれを使って、サプライヤー名を含む書式設定された文字列を取得することができます。
ソリューション エクスプローラー ウィンドウで、Pages フォルダーを展開します。 このフォルダーには、次の 2 つのページのマークアップとコードが含まれています。
PartsPage.xaml。 このページでは、CollectionView レイアウトと DataTemplate を使って、一覧として使用できるパーツの詳細を表示します。 DataTemplate では、データ バインディングを使い、表示されるデータを、Web サービスから取得したパーツに接続しています。 CollectionView で行を選択して AddPartPage 内のパーツを編集することも、[新しいパーツの追加] ボタンを選択して新しいパーツを追加することもできます。
AddPartPage.xaml。 このページでは、ユーザーが新しいパーツの詳細を入力し、保存することができます。 ユーザーは、パーツ名、パーツの種類、初期サプライヤーを指定できます。 パーツ ID とパーツの入手可能日は自動的に生成されます。
ソリューション エクスプローラー ウィンドウで ViewModels フォルダーを展開します。 このフォルダーには次の 2 つのクラスが含まれています。AddPartViewModel.cs と PartsViewModel.cs です。 これらは、各ページのビュー モデルであり、ページでデータを表示および操作するために必要なプロパティとロジックが含まれています。
サービスにサインインします。
REST サービスでは、認可トークンを取得するためには最初にサインインする必要があります。 ユーザー認証はありません。 最初に特定のエンドポイントを呼び出して認証トークンを取得し、次に HTTP ヘッダー内の後続の各要求でトークンをサーバーに送り返します。
Data フォルダー内の PartsManager.cs ファイルを開きます。
PartsManager クラスに、次のコード スニペットで定義されている BaseAddress と URL の静的フィールドを追加します。 URL GOES HERE というテキストを、先ほどメモした REST Web サービスの URL に置き換えます。
public class PartsManager { static readonly string BaseAddress = "URL GOES HERE"; static readonly string Url = $"{BaseAddress}/api/"; ... }
クラスの Url フィールドの後に、次のフィールドを追加します。 このフィールドは、ユーザーがサインインしたときに返される認可トークンを保持します。
private static string authorizationKey;
GetClient メソッドを見つけます。 現在、このメソッドからは NotImplementedException 例外がスローされます。 このメソッド内の既存のコードを次のコードに置き換えます。 このコードを実行して、HttpClient オブジェクトを作成し、REST Web サービスのログイン エンドポイントに要求を送信します。 サービスは、認可トークンを含むメッセージで応答します。 このトークンを逆シリアル化し、HttpClient オブジェクトを使って送信される後続の要求に対する既定の認可要求ヘッダーとして追加します。
private static async Task<HttpClient> GetClient() { if (client != null) return client; client = new HttpClient(); if (string.IsNullOrEmpty(authorizationKey)) { authorizationKey = await client.GetStringAsync($"{Url}login"); authorizationKey = JsonSerializer.Deserialize<string>(authorizationKey); } client.DefaultRequestHeaders.Add("Authorization", authorizationKey); client.DefaultRequestHeaders.Add("Accept", "application/json"); return client; }
GET 操作を実行してパーツの情報を取得する
PartsManager.cs ファイル内の GetAll メソッドを見つけます。 これは、パーツの列挙可能な一覧を返す非同期メソッドです。 このメソッドはまだ実装されていません。
このメソッド内にある NotImplementedException 例外をスローするコードを削除します。
Connectivity
クラスを使って、デバイスがインターネットに接続されているかどうかを確認します。 インターネットが存在しない場合は、空のList<Part>
を返します。if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return new List<Part>();
GetClient メソッドを呼び出して、操作する HttpClient オブジェクトを取得します。 GetClient は非同期なので、await 演算子を使って、このメソッドから返されたオブジェクトをキャプチャしてください。
HttpClient オブジェクトの GetStringAsync メソッドを呼び出し、ベース URL を指定して REST Web サービスからパーツの配列を取得します。 データは JSON 文字列として非同期で返されます。
JsonSerializer.Deserialize メソッドを使って、このメソッドから返された JSON 文字列を Part オブジェクト一覧に逆シリアル化します。 この一覧を呼び出し元に返します。
完成したメソッドは次のようになります。
public static async Task<IEnumerable<Part>> GetAll() { if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return new List<Part>(); var client = await GetClient(); string result = await client.GetStringAsync($"{Url}parts"); return JsonSerializer.Deserialize<List<Part>>(result, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, }); }
アプリケーションをビルドし、実行します。 アプリを起動すると、[Part List](パーツ一覧) ページが表示され、GetAll メソッドで取得したパーツ一覧が表示されます。 次の画像は、Android 上で実行しているアプリです。
データの閲覧が完了したら、アプリを閉じ、Visual Studio または Visual Studio Code に戻ります。
POST 操作を実行してデータベースに新しいパーツを追加する
PartsManager クラスで、Add メソッドを見つけます。 このメソッドには、パーツ名、サプライヤー、パーツの種類のパラメーターがあります。 このメソッドは非同期です。 このメソッドの目的は、データベースに新しいパーツを挿入し、新しく作成された項目を表す Part オブジェクトを返すことです。
メソッド内の既存のコードを削除します。
Connectivity
クラスを使って、デバイスがインターネットに接続されているかどうかを確認します。 インターネットが存在しない場合は、空のPart
を返します。if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return new Part();
新しい Part オブジェクトを作成します。 渡されたデータをフィールドに設定します。
- PartID フィールドに空の文字列を設定します。 この ID は REST Web サービスによって生成されます。
- サプライヤーの名前を保持する新しい List を作成します。
- PartAvailableDate フィールドを DateTime.Now に設定します。
- GetClient メソッドから HTTP クライアントを取得します。
var part = new Part() { PartName = partName, Suppliers = new List<string>(new[] { supplier }), PartID = string.Empty, PartType = partType, PartAvailableDate = DateTime.Now.Date };
GetClient メソッドを呼び出して、操作する HttpClient オブジェクトを取得します。
HttpRequestMessage
オブジェクトを作成します。 このオブジェクトは、Web サービスに送信される要求をモデル化するために使われます。 使う HTTP 動詞と、通信する Web サービスの URL を示すパラメーターを指定して開始します。var msg = new HttpRequestMessage(HttpMethod.Post, $"{Url}parts");
作成する Part の情報を含むペイロードを Web サービスに送信する必要があります。 このペイロードは JSON にシリアル化されます。 JSON ペイロードは
HttpRequestMessage.Content
プロパティに追加され、JsonContent.Create
メソッドを使ってシリアル化されます。msg.Content = JsonContent.Create<Part>(part);
次に、
HttpClient.SendAsync
関数を使ってメッセージを Web サービスに送信します。 その関数から、サーバー上の操作に関する情報を保持するHttpResponseMessage
オブジェクトが返されます。 たとえば、サーバーから渡された HTTP 応答コードや情報などです。var response = await client.SendAsync(msg); response.EnsureSuccessStatusCode();
上記では
response.EnsureSuccessStatusCode
メソッドが使用されていることに注意してください。 2xx HTTP 状態コード以外が返された場合、エラーがスローされます。Web サービスから、JSON でシリアル化されたオブジェクトなどの情報が返された場合、
HttpResponseMessage
からそれを読み取ることができます。 その後、JsonSerializer.Deserialize
を使用して JSON を逆シリアル化できます。var returnedJson = await response.Content.ReadAsStringAsync(); var insertedPart = JsonSerializer.Deserialize<Part>(returnedJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, });
最後に、新しく挿入された Part を返します。
return insertedPart;
アプリケーションをビルドし、実行します。 [Add New Part](新しいパーツの追加) ボタンを選び、名前、種類、サプライヤーを入力し、新しいパーツを作成します。 [保存] を選択します。 PartsManager クラスの Add メソッドが呼び出され、それによって Web サービス内に新しいパーツが作成されます。 操作が成功すると、パーツ一覧ページが再表示され、新しいパーツが一覧の一番下に表示されます。
データの閲覧が完了したら、アプリを閉じ、Visual Studio または Visual Studio Code に戻ります。
PUT 操作を実行して、データベース内のパーツの詳細を更新します。
PartsManager クラスで Update メソッドを見つけます。 これは、Part オブジェクトをパラメーターとして受け取る非同期メソッドです。 このメソッドに明示的な戻り値はありません。 ただし、戻り値の型は Task です。そのため、例外が呼び出し元に適切に返されます。 PUT 機能を実装してみましょう。
既存のコードを削除します。
前と同様に、インターネット接続を確認します。
if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return;
今回は PUT 操作とパーツ更新用の URL を指定して、新しく
HttpRequestMessage
を作成します。HttpRequestMessage msg = new(HttpMethod.Put, $"{Url}parts/{part.PartID}");
JsonContent.Create
関数と関数に渡された part パラメーターを使って、HttpRequestMessage
のContent
プロパティを設定します。msg.Content = JsonContent.Create<Part>(part);
GetClient メソッドから HTTP クライアントを取得します。
var client = await GetClient();
HttpClient
を使って要求を送信し、成功したことを確認します。var response = await client.SendAsync(msg); response.EnsureSuccessStatusCode();
アプリケーションをビルドし、実行します。 リストからいずれかのパーツを 1 つ選びます。 [パーツの追加] ページが表示されます。今回は既にプロパティが入力されています。 必要に応じて更新します。
[保存] を選択します。 PartsManager クラスの Update メソッドを呼び出して、Web サービスに変更内容を送信します。 成功すると、変更が反映されたパーツ一覧ページが再表示されます。
Note
前のタスクで追加したパーツは、[Part List](パーツ一覧) ページには表示されません。 アプリに使われるデータは、アプリを実行するたびに事前定義されたパーツのリストにリセットされます。 これは、アプリのテストに一貫性を持たせるためです。
DELETE 操作を実行してデータベースからパーツの詳細を削除する
PartsManager クラスで Delete メソッドを見つけます。 これは、partId 文字列を受け取り、Task を返す非同期メソッドです。
既存のコードを削除します。
インターネット接続を確認します。
if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return;
新しい
HttpRequestMessage
オブジェクトを作成します。 今回だけ DELETE HTTP 動詞と URL を指定して、パーツを削除します。HttpRequestMessage msg = new(HttpMethod.Delete, $"{Url}parts/{partID}");
GetClient メソッドから HTTP クライアントを取得します。
var client = await GetClient();
要求を Web サービスに送信します。 それが戻ったら、成功を確認します。
var response = await client.SendAsync(msg); response.EnsureSuccessStatusCode();
アプリケーションをビルドし、実行します。 リストからパーツを選び、[パーツの追加] ページで [削除] を選びます。 成功すると、[Part List](パーツ一覧) ページが再表示され、削除したパーツは表示されなくなります。