MAUI用プロキシサービスの実装
次に、MAUI側でサーバー経由で画像生成を行うサービスを実装します。
ProxyImageGenerationServiceの作成
ImageGeneratorプロジェクトのServicesフォルダに、ProxyImageGenerationServiceクラスを新規作成します。
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の登録は削除します。
// 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キー関連のサービスが不要になり、構成がシンプルになりました。
