[鐵人賽 Day16] ASP.NET Core 2 系列 - 多重環境組態管理 (Multiple Environments)

-- Pageviews

產品從開發到正式上線的過程中,通常都會有很多個環境,如:開發環境、測試環境及正式環境等。
每個環境的組態設定可能都略有不同,至少資料庫不會都連到同一個地方,因此就會有不同環境組態設定的需求。
ASP.NET Core 就提供了相關的環境 API,透過環境 API 取得執行環境的資訊,進而做對應處理。
本篇將介紹 ASP.NET Core 的多重環境組態管理。

iT 邦幫忙 2018 鐵人賽 - Modern Web 組參賽文章:
[Day16] ASP.NET Core 2 系列 - 多重環境組態管理 (Multiple Environments)

環境名稱

環境 API 可以讀取執行程式的環境資訊,例如:環境名稱、網站實體路徑、網站名稱等。
其中環境名稱就是用來判斷執行環境的主要依據,環境名稱是從系統變數為 ASPNETCORE_ENVIRONMENT 的內容而來。

ASP.NET Core 預設將環境分為三種:

  • Development:開發環境
  • Staging:暫存環境(測試環境)
  • Production:正式環境

要取得系統變數 ASPNETCORE_ENVIRONMENT,可以透過注入 IHostingEnvironment API。範例如下:

Startup.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
env.EnvironmentName = EnvironmentName.Development;

if (env.IsDevelopment()) {
// Do something...
}

app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"EnvironmentName: {env.EnvironmentName}\r\n"
+ $"IsDevelopment: {env.IsDevelopment()}"
);
});
}
}

網站啟動時,IHostingEnvironment 會從系統變數 ASPNETCORE_ENVIRONMENT 取得資料後,填入 EnvironmentName,該值也可以從程式內部直接指派。
環境名稱並沒有特定的限制,它可以是任意的字串,不一定要被預設的三種分類限制。

例如自訂一個 Test 的環境。如下:

Startup.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
env.EnvironmentName = "Test";

if (env.IsDevelopment()) {
// Do something...
} else if (env.IsEnvironment("test")) {
// Do something...
}

app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"EnvironmentName: {env.EnvironmentName}\r\n"
+ $"This is test environment: {env.IsEnvironment("test")}");
});
}
}

建議判斷環境透過 env.IsEnvironment("EnvironmentName")IsEnvironment() 會忽略大小寫差異。

組態設定

組態設定檔可以在不同環境都各有一份,或許大部分的內容是相同的,但應該會有幾個設定是不同的。如:資料庫連線字串。
環境名稱也可以跟組態設定檔混用,利用組態設定檔後帶環境名稱,作為不同環境會取用的組態設定檔。

例如:
有一個組態設定檔為 settings.json,內容如下:

Configuration\settings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"SupportedCultures": [
"en-GB",
"zh-TW",
"zh-CN"
],
"CustomObject": {
"Property": {
"SubProperty1": 1,
"SubProperty2": true,
"SubProperty3": "This is sub property."
}
},
"DBConnectionString": "Server=(localdb)\\mssqllocaldb;Database=MyWebsite;"
}

正式環境也建立一個名稱相同的檔案,並帶上環境名稱 settings.Production.json,內容如下:

Configuration\settings.Production.json

1
2
3
{
"DBConnectionString": "Data Source=192.168.1.5;Initial Catalog=MyWebsite;Persist Security Info=True;User ID=xxx;Password=xxx"
}

載入組態設定方式:

Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.IO;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;

namespace MyWebsite
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostContext, config) =>
{
var env = hostContext.HostingEnvironment;
config.SetBasePath(Path.Combine(env.ContentRootPath, "Configuration"))
.AddJsonFile(path: "settings.json", optional: false, reloadOnChange: true)
.AddJsonFile(path: $"settings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>()
.Build();
}
}
}

讀取組態設定檔時,會先讀取 settings.json 並設定 optional=false,指定該檔為必要檔案;再讀取 settings.{env.EnvironmentName}.json 檔案。 組態檔載入的特性是當遇到 Key 值重複時,後面載入的設定會蓋掉前面的設定。

以此例來說,當 settings.Production.json 載入後,就會把 settings.json 的 DBConnectionString 設定蓋掉,而 settings.json 其它的設定依然能繼續使用。

前篇[鐵人賽 Day15] ASP.NET Core 2 系列 - 組態設定 (Configuration) 有介紹讀取組態設定檔。

環境設定

利用系統變數 ASPNETCORE_ENVIRONMENT 能判斷環境的特性,可以在各環境的電腦設定環境名稱。
當程式佈署到該環境後,運行時就會套用該台電腦的系統變數。

Windows

Windows 系統變數的設定方式如下: 控制台 -> 系統及安全性 -> 系統 [鐵人賽 Day16] ASP.NET Core 2 系列 - 多重環境組態管理 (Multiple Environments) - 環境變數1
[鐵人賽 Day16] ASP.NET Core 2 系列 - 多重環境組態管理 (Multiple Environments) - 環境變數2

Windows 也可以用指令:

1
SETX ASPNETCORE_ENVIRONMENT "Production" /M

需要用系統管理員權限執行。
如果設定完沒有生效,試著重新登入或重開機。

Linux\macOS

Linux\macOS 可以在 /etc/profile 加入環境變數:

1
export ASPNETCORE_ENVIRONMENT="Production"

IIS

IIS 的 Web.config 也可以設定環境變數:

Web.config

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\MyWebsite.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>

Visual Studio Code

如果是用 VS Code 開發的話,可以在 launch.json 找到 ASPNETCORE_ENVIRONMENT 的設定如下:

launch.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
// ...
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
// ...
},
// ...
]
}

透過 VS Code 啟動 .NET Core Launch (web) 時,就會套用該設定的環境名稱。如下:

[鐵人賽 Day16] ASP.NET Core 2 系列 - 多重環境組態管理 (Multiple Environments) - Visual Studio Code

Visual Studio IDE

若以 Visual Studio IDE 開發(如 Visual Studio 2017),可以從 UI 設定環境名稱。如下:
[鐵人賽 Day16] ASP.NET Core 2 系列 - 多重環境組態管理 (Multiple Environments) -Visual Studio 2017

或者從 Properties\launchSettings.json 設定:

Properties\launchSettings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
// ...
"profiles": {
// ...
"MyWebsite": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Local"
},
"applicationUrl": "http://localhost:5000/"
}
}
}

用 Visual Studio 2017 啟動網站後,就會套用該設定的環境名稱。如下:
[鐵人賽 Day16] ASP.NET Core 2 系列 - 多重環境組態管理 (Multiple Environments) -Visual Studio 2017

參考

Working with multiple environments