3月第2週の振り返り

3月第2週(3/2-3/8)

ASP.NET Core MVC

今週も引き続き環境変数等の扱いを調査した。なかなか複雑で混乱しやすいところであり、ASP.NET特有の部分も多いため時間を要した。

ASP.NET Core 3.1においてWeb MVCアプリケーションを作成すると、ProgramクラスCreateHostBuilder(string[] args)メソッドにおいてHost.CreateDefaultBuilder(args).ConfigureWebHostDefaults(...)が返されるようになっている。

docs.microsoft.com

上記ドキュメントで説明されているように、CreateDefaultBuilderでは

  1. 各種環境変数を読み込む
  2. DOTNET_で始まる環境変数を参照し設定を行う

等の処理を行う。DOTNET_接頭辞のある環境変数は接頭辞なしでもアクセス可能になる*1("DOTNET_abc"でも"abc"でもよい)。

次にConfigureWebHostDefaultsでは

  1. ASPNETCORE_で始まる環境変数を参照し設定を行う
  2. ASPNETCORE_FORWARDEDHEADERS_ENABLEDがtrue/1であればx-forwarded-forを有効にするミドルウェアを自動追加する

等の処理を行う。ASPNETCORE_接頭辞のある環境変数が接頭辞なしでもアクセス可能になる("ASPNETCORE_xyz"でも"xyz"でもよい)。

Startupクラス(Startup.cs)の中ではIConfiguration型 Configurationが供給(DI)される。Configurationはkey-valueセットなのでキー名を指定してConfiguration["pqr"]で環境変数を取得することができる。

では開発環境において同一名の環境変数は―すなわち様々な場所に書き込まれた環境変数は―どの順番で優先されるのだろうか。後に読み込まれたものが優越するのは明らかだが、接頭辞なしでアクセスできるようになるとされているDOTNET_xyzやASPNETCORE_xyzはどこに位置するのか。Configuration["xyz"]の値を表示させる実験の結果は以下である。

  1. launchSettings.jsonのprofiles:【IIS Expressなど】:environmentVariables:xyz(優先)
  2. マシン環境変数のxyz
  3. secrets.jsonのxyz
  4. appsettings.Development.jsonのxyz
  5. appsettings.jsonのxyz

  1. launchSettings.jsonの(略):environmentVariables:ASPNETCORE_xyz
  2. マシン環境変数ASPNETCORE_xyz
  3. ...中略...

  1. launchSettings.jsonの(略):environmentVariables:DOTNET_xyz
  2. マシン環境変数DOTNET_xyz(劣後)
  3. ...後略...

このことから、同一接頭辞キーについて書き込み場所による優先順位は1.から5.で示された通りであり、接頭辞の優先度は接頭辞なし>ASPNETCORE_>DOTNET_ということが分かる。接頭辞の違いがまず考慮され、同一接頭辞中では書き込み場所によって順位が定められている。

Configuration["ASPNETCORE_xyz"]など接頭辞付きでアクセスした場合は、フルネームなので当然完全一致するキーの中だけで読み込み順に従って値が決定される。

結論として外部にプロジェクトコードが公開されても安全が保たれ、かつ環境ごとに異なる値に設定できるようにするためのシークレット書き込み場所は

ということになろう。結局全部マシン環境変数である。Windows Visual StudioについてはVS起動時にマシン環境変数が読み込まれて維持され、VSの起動中は再読み込みされないようなので、頻繁に書き換えて挙動をチェックしたり、あるいは開発環境用に多数存在する設定を社内で簡単に共有したりするにはsecrets.jsonを使うといったところだろうか。

DOTNET_/ASPNETCORE_接頭辞つきのものを省略して参照するのは予期せぬオーバーライドを引き起こしかねないのでやめておいたほうがよさそうだ。CreateDefaultBuilderによるプロバイダをクリアしてConfigurationBuilder().AddEnvironmentVariables(prefix: "YOURPREFIX_")を利用することもできるが、最初からYOURPREFIX_付きで指定すべきであろう。