APIキーの安全な管理
APIキーなどの機密情報は、コード内に直接記述するのではなく、環境変数やシークレット管理機能 (たとえば、Azure Key Vaultなど)を使用するべきです。これは、コードがGitHubなどで公開された場合に漏洩してしまうことを防ぐためです。
ユーザーシークレット機能
アプリの開発中では、.NET Coreのユーザーシークレット機能を使用して、APIキーを安全に管理できます。この機能は、APIキーなどの機密情報をソースコードや構成ファイルとしてではなく、ローカルの別の場所に保存して、参照する機能です。Gitなどのバージョン管理システムに、誤ってAPIキーをコミットしてしまうリスクを減らせます。
まず、ImageGenerator.Sharedプロジェクトでユーザーシークレットを有効にします。ソリューションエクスプローラーでプロジェクトを右クリックし、[ユーザーシークレットの管理]を選択します。すると、必要なパッケージの追加を求めるダイアログが表示されて、「はい」を選択すると、secrets.jsonという空のJSONファイルが開かれます。

このJSONファイルに、次のようにAPIキーを設定します。
{ "OpenAI": { "ApiKey": "sk-proj-...." } }
sk-proj-....の部分には、実際に取得したAPIキーを入力します。このファイルは、デフォルトでは、%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.jsonに保存されます。<user_secrets_id> は、プロジェクトによって異なる一意の識別子です。
secrets.jsonは、プロジェクトのソースコードから分離されており、Gitなどのバージョン管理システムで誤ってコミットされるのを防ぎます。
設定クラスの作成
次に、この設定を読み込むためのクラスとして、ImageGenerator.SharedプロジェクトのServicesフォルダに、OpenAISettingsクラスを作成します。
public class OpenAISettings { public string? ApiKey { get; set; } // (1) }
このクラスは、単にAPIキーのプロパティを提供するだけです(1)。DIコンテナを通じて設定値を受け取るためのモデルとして機能します。
コンテナの登録
DIコンテナにAPIキーを登録するコードを追加します。MAUIプロジェクトのMauiProgram.csファイルとWebプロジェクトのProgram.csファイルに、次のコードを追加します。
builder.Services.AddSingleton<IImageGenerationService, MockImageService>(); // ユーザーシークレットから設定を読み込み、DIコンテナに登録 builder.Configuration.AddUserSecrets<OpenAISettings>(); var openAISettings = new OpenAISettings(); builder.Configuration.GetSection("OpenAI").Bind(openAISettings); builder.Services.AddSingleton(openAISettings);
AddUserSecrets<OpenAISettings>()でユーザーシークレットを読み込み、OpenAISettingsのインスタンスを作成し、secrets.jsonの「OpenAI」セクションの値をバインド(設定をクラスのプロパティに割り当て)しています。最後に、AddSingletonメソッドを使ってDIコンテナに登録しています。
この設定により、アプリケーション内のどこからでも、DIを通じてAPIキーへのアクセスが可能になります。
画像生成サービスの実装
それでは、実際にImage Generation APIを呼び出す画像生成サービスを実装していきましょう。前回作成したモックサービスの代わりに、APIを呼び出すサービスを作成します。

OpenAIImageServiceの作成
ImageGenerator.SharedプロジェクトのServicesフォルダに、新しいサービスクラス(OpenAIImageService)を作成します。
public class OpenAIImageService(OpenAISettings settings) : IImageGenerationService { // OpenAIクライアントを初期化 (1) private readonly ImageClient _client = new("dall-e-3", settings.ApiKey); public async Task<string> GenerateImageAsync(string prompt) { try { // 画像生成パラメータの設定 (2) ImageGenerationOptions options = new() { Quality = GeneratedImageQuality.Standard, // 品質 Size = GeneratedImageSize.W1024xH1024, // サイズ ResponseFormat = GeneratedImageFormat.Uri, // URLを返す Style = GeneratedImageStyle.Vivid // 画像スタイル }; // APIを呼び出し (3) var result = await _client.GenerateImageAsync(prompt, options); return result.Value.ImageUri.ToString(); } catch (Exception ex) { // エラーログを記録して再スロー Console.WriteLine($"画像生成エラー: {ex.Message}"); throw; } } }
ここでは、プライマリコンストラクタ構文でOpenAIImageServiceクラスを定義しています。DIコンテナから、OpenAISettingsインスタンスが自動的に渡されます。初期化部分では、dall-e-3モデルとDIコンテナから受け取ったAPIキーを指定して、ImageClientオブジェクトを生成しています(1)。このクライアントを通じてOpenAI APIと通信します。
GenerateImageAsyncメソッド内では、画像生成のためのパラメータを設定しています(2)。主なパラメータは次のとおりです。
プロパティ | 概要 |
Size | 画像のサイズ(1024x1024ピクセルなど) |
Quality | 画像の品質(標準または高品質) |
ResponseFormat | 応答形式(URLまたは画像データ) |
Style | 画像スタイル(VividまたはNatural) |
設定したパラメータを使ってAPIのGenerateImageAsyncメソッドを実行し、生成された画像のURLを受け取ります(3)。非同期処理(async/await)を使用することで、画像生成中もアプリのUIがフリーズしないようになっています。エラーが発生した場合には、ログに記録した上で、例外を再スローして呼び出し元に通知します。
サービスの登録
作成したOpenAIImageServiceをDIコンテナに登録します。MAUIプロジェクトのMauiProgram.csとWebプロジェクトのProgram.csの両方で、サービス登録部分を次のように変更します。これにより、前回のモックサービスの代わりに、実際にOpenAI APIを呼び出すサービスが使用されるようになります。
// 変更前 builder.Services.AddSingleton<IImageGenerationService, MockImageService>(); ↓ // 変更後 builder.Services.AddSingleton<IImageGenerationService, OpenAIImageService>();