ASP.NET が技術検証で必要になって、最近いろんなツールが、.Net Core ベースに移行しているので.NET Core の方を使ってみることにした。Core になったからといって、大した違いはないだろう、、、と思っていたけどいきなり、AppSettings 周りのアーキテクチャが変更になっていてがっつり躓いた。従来は、Configuration Manager だったのだが、こいつが消滅している。インターネットを検索しても、現在のASP.NET Core のテンプレートと異なり混乱したので、自分のために整理しておく。
ASP.NET Core + AppSettings でやりたかったこと
凄く単純なことで、普通にAzure上だと WebApp の AppSettings からKey/Valueの値をとってきて、ローカル開発しているときは、ローカルのファイルから取ってきたい。という単純な事
Configuration Builder
AppSettings 周りを取得するためには、ConfigurationBuilder を使う。試用イメージを見てもらうほうがわかりやすいだろう。
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
こんな感じでコードを書くとappsettings.json
, appsettings.Development/Staging/Production.json
それと環境変数から、値を取得できるようになる。WebAppの AppSettingsの項目は、環境変数になるので、このコードを書いておけばローカルは、appsettings.json
もしくは、appsettings.環境名.json
のファイルに置いておいて、WebAppはいつもの、AppSettings に書けばいいということになる。ちなみに、appsettings.環境名.json
の環境名は、ASPNETCORE_ENVIRONMENT
で指定する。詳しくはこちらを参照
そして、そのあとは便利なメソッドがあるので、クラスを作っておくと、そこにそれらの値をバインドしてくれるようになる。実際わたしが作成したコードを見てみよう。
ASP.NET Core MVC のコンフィグ例
このあたりの情報は世の中に出回っている内容は若干古いようです。例えばよくあるのが、上記のコードをStartupに記述するというのがありますが、必要ありません。Startup.cs のコードを読んでみると、既にProgram.cs の方で上記のコードが実装済みらしいです。
Program.cs
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
WebHostのコードを読むとコメントに書いてあります。
WebHost
// Summary:
// Initializes a new instance of the Microsoft.AspNetCore.Hosting.WebHostBuilder
// class with pre-configured defaults.
//
// Parameters:
// args:
// The command line args.
//
// Returns:
// The initialized Microsoft.AspNetCore.Hosting.IWebHostBuilder.
//
// Remarks:
// The following defaults are applied to the returned Microsoft.AspNetCore.Hosting.WebHostBuilder:
// use Kestrel as the web server, set the Microsoft.AspNetCore.Hosting.IHostingEnvironment.ContentRootPath
// to the result of System.IO.Directory.GetCurrentDirectory, load Microsoft.Extensions.Configuration.IConfiguration
// from 'appsettings.json' and 'appsettings.[Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName].json',
// load Microsoft.Extensions.Configuration.IConfiguration from User Secrets when
// Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName is 'Development'
// using the entry assembly, load Microsoft.Extensions.Configuration.IConfiguration
// from environment variables, load Microsoft.Extensions.Configuration.IConfiguration
// from supplied command line args, configures the Microsoft.Extensions.Logging.ILoggerFactory
// to log to the console and debug output, enables IIS integration, enables the
// ability for frameworks to bind their options to their default configuration sections,
// and adds the developer exception page when Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName
// is 'Development'
public static IWebHostBuilder CreateDefaultBuilder(string[] args);
ですので、あなたが必要なことは、まずコンフィグのデータを保持するためのモデルを書くことです。
# appsettings.json の例
{
"CosmosDB": {
"EndPointUrl": "https://xxxxx.documents.azure.com:443/",
"AuthorizationKey": "SOMESECRET"
}
}
このデータを読み込むための設定をします。
Model の作成
appsettings.json
の構造に合わせてクラスを作ります。
AppSettings.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace PrintServerlessWeb.Models
{
public class AppSettings
{
public CosmosDB CosmosDB { get; set; }
}
public class CosmosDB
{
public string EndPointUrl { get; set; }
public string AuthorizationKey { get; set; }
}
}
Startup.cs
一部を書き換えます。services.Configure
に下記のようなコードを書くだけで、先ほどのモデルクラスにコンフィグをロードしてくれます。他に、特定の階層を指定してコードを書く方法もあります。興味がある人は次のページでサンプルを見てください。これは便利ですね。ちなみにASP.NET Coreではなくて、自分で素で書くときも、指定したクラスにロードしてくれるメソッドがあるので、これは便利ですね。ちなみに、services のインスタンスに足しておくと、Dependency Injection してくれて、コントローラーから取得できるようになります。
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<AppSettings>(Configuration);
}
HomeController.cs
コントローラー側に、自動で Dependency Injection してくれるので、そのコードを追加します。そしたら使いたい放題です。
HomeController.cs
private readonly AppSettings appSettings;
public HomeController(IOptions<AppSettings> optionsAccessor)
{
this.appSettings = optionsAccessor.Value;
}
:
public IActionResult Index()
{
Console.WriteLine("***************** start");
Console.WriteLine($"CosmosDB: EndPointUrl : {this.appSettings.CosmosDB.EndPointUrl}");
Console.WriteLine($"CosmosDB: AuthorizationKey: {this.appSettings.CosmosDB.AuthorizationKey}");
Console.WriteLine("***************** end");
return View();
}
:
ちなみに、DI は次のパッケージで出来るようになっているみたい。
Web App の AppSettings はどうなるか?
WebApp のコンフィグはどのようになるかというと、AppSettings の項目に
CosmosDB:EndPointUrl
といったjsonファイルの設定の構造に合わせてた書き方をします。すると、同じインターフェイスで読めるようになります。
これは、当初かなり違和感がありました。なんで、jsonファイルの方に合わせないといけないのか?と思いました。しかしよくよく考えると、コネクションストリングとかは、例えば、CosmosDBのコネクションストリングだったり、KeyVaultのコネクションストリングだったりするわけです。そうなると、COSMOSDB_CONNECTION_STRING
とか、KEY_VAULT_CONNECTION_STRING
になるわけで、結局のところ、CosmosDB:ConnectionString
と意味的に大差ありません。しかもコードの方は明確に環境変数の構造化ができるので、こっちのほうがいい気がしてきました。
これでしっかりと設定ができます。
Resource
- Working with Azure App Services Application Settings and Connection Strings in ASP.NET Core <- 決定版
- ASP.NET Core MVC App Settings <- 実例
- Configure an ASP.NET Core App <- 公式 Doc
- Working with multiple environments <- 複数の環境設定の公式 Doc
- DependencyInjection <- 内部で使われているDIライブラリ