SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

マルチターゲットアプリ開発の新しいアプローチ~.NET 9 新テンプレートの基本~

.NET MAUIアプリ構築で、より安全なプロキシ方式を実装する

マルチターゲットアプリ開発の新しいアプローチ~.NET 9 新テンプレートの基本~ 第7回

MAUI用プロキシサービスの実装

 次に、MAUI側でサーバー経由で画像生成を行うサービスを実装します。

ProxyImageGenerationServiceの作成

 ImageGeneratorプロジェクトのServicesフォルダに、ProxyImageGenerationServiceクラスを新規作成します。

[リスト3]ProxyImageGenerationService.cs
using System.Net.Http.Json;

public class ProxyImageGenerationService(IHttpClientFactory httpClientFactory) : IImageGenerationService
{
    public async Task<string> GenerateImageAsync(string prompt, int quality, int size)
    {
        var apiServerUrl = GetApiServerUrl();

        // DIコンテナに登録された設定済みのクライアントを取得
        var httpClient = httpClientFactory.CreateClient("ApiClient");     // (1)
        var request = new ImageGenerationRequest(prompt, quality, size);  // (2)

        // サーバーにPOSTリクエストを送信 (3)
        using var response = await httpClient.PostAsJsonAsync(
            $"{apiServerUrl}/api/generate-image", request);

        if (!response.IsSuccessStatusCode)
        {
            // エラー応答の処理 (4)
            var problem = await response.Content.ReadFromJsonAsync<ProblemResponse>();
            throw new Exception(problem?.Detail ?? "画像生成に失敗しました");
        }

        // 成功応答の解析 (5)
        var result = 
                await response.Content.ReadFromJsonAsync<ImageGenerationResponse>();
        if (result == null || string.IsNullOrEmpty(result.ImageUrl))
        {
            throw new Exception("サーバーからの応答を解析できませんでした");
        }

        return result.ImageUrl;
    }

    // プラットフォームに応じたURLを返す(前回のMauiKeyStorageと同じ) (6)
    private static string GetApiServerUrl()
    {
        ~中略~
    }
}

 IHttpClientFactoryからHttpClientを取得し(1)、ImageGenerationRequestを作成します(2)。HttpClientの取得には、名前付きクライアント「ApiClient」を指定しています。

 前回はDEBUGとリリースとで、名前付き/なしを区別していましたが、今回は「ApiClient」に統一しました。これで利用側のコードがシンプルになります。

 「ApiClient」という名前の中身(HttpClientHandlerの設定など)は、後述するMauiProgram.csのAddHttpClient("ApiClient")で構成します。ProxyImageGenerationServiceは、IImageGenerationServiceインターフェイスを実装するため、前回までに追加した品質とサイズの引数もそのままサーバーへ渡します。

 PostAsJsonAsyncメソッドでは、オブジェクトをJSON形式にシリアライズしてPOSTリクエストが送信されます(3)。リクエストの応答が成功ステータス(200番台)以外だった場合は、ボディをProblemResponseとして読み取り、detailメッセージを例外としてスローします(4)。

 このとき、サーバー側が返すJSONのキーは小文字の「detail」ですが、ReadFromJsonAsyncメソッドでは、Webアプリ向けのデフォルト設定で大文字小文字を区別しないため、C#レコード側の「Detail」プロパティへと自動的にマッピングされます。

 成功時は、ImageGenerationResponseオブジェクトにバインドされ、画像URLを返します(5)。GetApiServerUrlメソッドは、前回のMauiKeyStorageと同様、プラットフォームに応じたURLを返すようにします(6)。

 なお、前回まではusing varでHttpClientを宣言していましたが、IHttpClientFactory経由で取得したHttpClientは、ファクトリ側がライフタイムを管理するため、明示的なDisposeは不要です。今回からusingを外しています(つけても動作上の問題はありません)。

DIの変更

 次に、MAUI側のDI設定を変更して新しいサービスに差し替えます。

MauiProgram.csの変更

 ImageGeneratorプロジェクトのMauiProgram.csで、画像生成サービスの登録を変更します。OpenAIImageServiceの代わりにProxyImageGenerationServiceを登録します。

 また、サーバー経由で画像生成を行うため、MAUI側ではAPIキーを扱う必要がなくなります。そのため、前回のIApiKeyStorageとApiKeyServiceの登録は削除します。

[リスト4]MauiProgram.csの一部(サービス登録部分)
// HttpClientFactoryの登録(開発環境では証明書検証をスキップ)
#if DEBUG
builder.Services.AddHttpClient("ApiClient")
    .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
    {
        ServerCertificateCustomValidationCallback =
            (message, cert, chain, errors) => true
    });
#else
builder.Services.AddHttpClient("ApiClient");
#endif

// フォームファクター
builder.Services.AddSingleton<IFormFactor, FormFactor>();

// 画像生成サービス(サーバー経由)
builder.Services.AddSingleton<IImageGenerationService, ProxyImageGenerationService>();

// 画像保存サービス
builder.Services.AddSingleton<IImageSaveService, MauiImageSaveService>();

 API通信用の名前付きHttpClient(ApiClient)を登録し、ProxyImageGenerationServiceからも利用します。ApiClientの登録では、DEBUGビルドのときだけHttpClientHandlerでServerCertificateCustomValidationCallbackを設定し、証明書検証をスキップしています。

 これにより、開発時はAndroidエミュレータからローカルAPIサーバー(自己署名証明書)への接続を許可し、リリースビルドでは通常のHttpClientが登録されるため、本番では証明書検証が有効になります。

 APIキー関連のサービスが不要になり、構成がシンプルになりました。

次のページ
動作確認

この記事は参考になりましたか?

マルチターゲットアプリ開発の新しいアプローチ~.NET 9 新テンプレートの基本~連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 高江 賢(タカエ ケン)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/24585 2026/06/26 09:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング