mcpField
M
maui-rest-api

maui-rest-api

github.com/davidortinau開発17660 回インストール16 週間前に更新
リポジトリ

maui-rest-api について

.NET MAUI で REST API を利用する際の注意点と実践方法をまとめたガイドです。HttpClient の管理、非同期処理、レスポンス、JSON、モバイルのネットワーク設定を扱います。

主な機能

  1. 01依存性注入による HttpClient の再利用
  2. 02ブロッキングを避けた async/await
  3. 03デシリアライズ前のステータス確認
  4. 04ネットワークと JSON のエラー処理
  5. 05ローカル開発用ネットワーク設定

活用シーン

  1. 01リクエストごとの生成によるソケット枯渇を防止
  2. 02UI スレッドのデッドロックを回避
  3. 03失敗または不正形式の API 応答を処理
  4. 04モバイルエミュレーターから開発サーバーへ接続

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/davidortinau/maui-skills --skill maui-rest-api

インストール後、Claude Code で/maui-rest-apiを入力して読み込まれたか確認します。

同梱ファイルreferences/rest-api-reference.md

詳細

REST API の利用 — 注意点とベストプラクティス

よくある間違い

1. リクエストごとに HttpClient を作成する
// ❌ Creates socket exhaustion — each instance opens a new connection
public async Task<List<Item>> GetItemsAsync()
{
    using var client = new HttpClient();
    var response = await client.GetAsync("https://api.example.com/items");
    // ...
}

// ✅ Register once in DI, inject everywhere
builder.Services.AddSingleton(sp => new HttpClient
{
    BaseAddress = new Uri("https://api.example.com")
});
2. .Result または .Wait() で処理をブロックする
// ❌ Deadlocks on the UI thread
var items = _apiService.GetItemsAsync().Result;

// ✅ Always use async/await
var items = await _apiService.GetItemsAsync();
3. ステータスを確認する前にデシリアライズする
// ❌ Tries to deserialize error HTML/JSON as your model
var content = await response.Content.ReadAsStringAsync();
var items = JsonSerializer.Deserialize<List<Item>>(content, _jsonOptions);

// ✅ Check status first
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var items = JsonSerializer.Deserialize<List<Item>>(content, _jsonOptions) ?? [];
4. サービスメソッドに BaseAddress をハードコードする
// ❌ Absolute URIs in every method — hard to change, easy to typo
await _httpClient.GetAsync("https://api.example.com/api/items");

// ✅ Set BaseAddress in DI, use relative URIs in methods
await _httpClient.GetAsync("api/items");
5. ネットワーク障害時のエラー処理がない
// ❌ Crashes on network timeout, DNS failure, etc.
var items = await _apiService.GetItemsAsync();

// ✅ Catch both network and deserialization errors
try
{
    var items = await _apiService.GetItemsAsync();
}
catch (HttpRequestException ex) { /* network or HTTP error */ }
catch (JsonException ex) { /* malformed response */ }

プラットフォーム固有の落とし穴

⚠️ エミュレーター/シミュレーターでは平文 HTTP がブロックされます

http:// を使うローカル開発サーバーは、デフォルトでブロックされます。例外を設定してください。

  • Android:10.0.2.2 に対して cleartextTrafficPermitted="true" を指定した network_security_config.xml が必要です
  • iOS/Mac Catalyst:Info.plist に NSAllowsLocalNetworking が必要です
⚠️ Android エミュレーターでは localhost に 10.0.2.2 を使用します

Android エミュレーターでは、10.0.2.2 がホストマシンに割り当てられます。localhost はエミュレーター自体を指します。

// ❌ On Android emulator, this hits the emulator, not your dev machine
new Uri("http://localhost:5000")

// ✅ Use the emulator's host loopback address
new Uri("http://10.0.2.2:5000")

iOS シミュレーターでは localhost をそのまま使用できます。

⚠️ JSON の大文字小文字が一致しない

API では通常 camelCase が使われますが、C# のプロパティは PascalCase です。JsonSerializerOptions を指定しないと、デシリアライズ時にエラーが発生せず、既定値が返されます。

// ❌ Properties stay null/default — no error thrown
JsonSerializer.Deserialize<Item>(content);

// ✅ Configure casing policy
private static readonly JsonSerializerOptions _jsonOptions = new()
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    PropertyNameCaseInsensitive = true
};

判断の指針

シナリオエラー処理の方法
予期しない失敗(認証が必要なエンドポイント)EnsureSuccessStatusCode() — HttpRequestException をスローします
ステータスコードに応じた分岐が必要IsSuccessStatusCode または response.StatusCode を確認します
ネットワークが不安定な可能性がある(モバイル)HttpRequestException を try/catch で囲みます
レスポンス形式が変わる可能性があるJsonException も捕捉します

チェックリスト

  • HttpClient はシングルトンまたは IHttpClientFactory を通じて登録する — リクエストごとに作成しない
  • BaseAddress は DI で設定し、サービスメソッドでは相対 URI を使用する
  • CamelCase ポリシーを指定した JsonSerializerOptions を一貫して適用する
  • デシリアライズの前に IsSuccessStatusCode または EnsureSuccessStatusCode() を確認する
  • ViewModel の呼び出しでは HttpRequestException と JsonException を try/catch で捕捉する
  • すべての API 呼び出しで async/await を使う — .Result や .Wait() は使わない
  • サービスインターフェイスのパターンを使い、ViewModel が抽象化に依存するようにする
  • ローカル開発用の Android 平文通信設定(10.0.2.2)
  • ローカル開発用の iOS NSAllowsLocalNetworking

コメント