以往我們在 .Net Fx 可以透過 Transforms 來切換不同的組態,而在 .NET Core 開始,則改用 Microsoft.Extensions.Configuration 來處理組態,上篇 也針對 Microsoft.Extensions.Configuration 進行了介紹,這篇將針對 .NET 應用程式注入環境的做法做一個簡單的介紹
開發環境
- .NET 6
- Rider 2022.2.2
環境變數
.NET Core 會從下列環境變數讀取
- DOTNET_ENVIRONMENT
- ASPNETCORE_ENVIRONMENT,預設 ASP.NET Core 會呼叫 WebApplication.CreateBuilder 建立應用程式,這時會讀取 ASPNETCORE_ENVIRONMENT,ASPNETCORE_ENVIRONMENT會覆寫 DOTNET_ENVIRONMENT的值
IHostEnvironment.EnvironmentName 有以下幾種狀態
- Development:開發環境,預設,在本地開發環境的 launchSettings.json 檔案設定 ASPNETCORE_ENVIRONMENT = Development。
- Staging:暫存環境(測試環境)。
- Production:正式環境,預設,當沒有設定 DOTNET_ENVIRONMENT 和 ASPNETCORE_ENVIRONMENT 時,使用 Production。
launchSettings.json 是給本地 IDE 使用通常不會上版控,就算要上版控也不要存放機密性資料
機密性資料的做法可以參考
IHostEnvironment.Environment 也有提供判斷目前應用程式是不是某一種環境,除了官方預設提供的三種環境之外,也可以自行擴充環境,這時候可以使用 IsEnvironment 判斷
根據環境切換組態設定
首先要先有 appsettings.json,然後再產生 appsettings.Development.json、appsettings.Staging.json、appsettings.Production.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Extension": {
"Version": "Default"
}
}
然後再產生
appsettings.Development.json
{
"Extension": {
"Version": "Development"
}
}
appsettings.Staging.json
{
"Extension": {
"Version": "Staging"
}
}
appsettings.Production.json
{
"Extension": {
"Version": "Production"
}
}
讀取組態設定檔時,先讀取 appsettings.json,指定該檔為必要檔案,再讀取 appsettings.{env.EnvironmentName}.json 檔案。當 Key 值重複時,後面載入的設定會蓋掉前面的組態。
var environmentName = builder.Environment.EnvironmentName;
var configRoot = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
.Build();
;
builder.Configuration.AddConfiguration(configRoot);
注入環境變數
了解了如何根據環境切換組態設定後,我們再來看看要怎麼樣注入環境
從 dotnet CLI
通過 --environment 注入
dotnet run --environment Staging
執行結果如下
從 IDE 的 launchSettings.json
不同的 IDE 所需要的 launch.json 都不太一樣,VS IDE / Rider 都有 IDE 可以產生 launchSettings.json
ASPNETCORE_ENVIRONMENT 節點在這個檔案
套用 launch profile
dotnet run --launch-profile "SampleApp"
設定 Process 的環境變數 (Windows)
生命週期僅在一個 Process,先透過指令碼設定環境變數,再執行 dotnet run
Cmd
set ASPNETCORE_ENVIRONMENT=Staging
dotnet run --no-launch-profile
PowerShell
$Env:ASPNETCORE_ENVIRONMENT = "Staging"
dotnet run --no-launch-profile
Rider
除了 launchSettings.json 能注入環境變數,Rider 也有提供 UI 注入環境,要稍微注意的是 Template 不太一樣
或者也是可以先用指令碼注入環境變數,再用只指令碼啟動 Rider
$Env:ASPNETCORE_ENVIRONMENT = "Staging"
cd "C:\Users\Yao Chang Yu\scoop\apps\Rider-EAP\current\IDE\bin\"
./rider64.exe
設定 Process 的環境變數 (macOS)
Bash
ASPNETCORE_ENVIRONMENT=Staging
dotnet run
或者是在應用程式執行之前使用export
export ASPNETCORE_ENVIRONMENT=Staging
web.config (Windows)
設定 web.config 檔案,在 aspNetCore 節點設定 <environmentVariables>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\Lab.Host.Env.WebApi.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Staging" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
IIS 部署 (Windows)
- 在發行設定檔(publish profile)指定 EnvironmentName 屬性,部署時就會在 web.config 設定環境變數
下面的例子是透過指令碼打包 web site 時設定 EnvironmentName 屬性。
msbuild
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" ^
Lab.Host.Env.WebApi.csproj ^
/p:DeployOnBuild=true ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false
msbuild 執行結果如下:
dotnet build
dotnet build ^
Lab.Host.Env.WebApi.csproj ^
/p:DeployOnBuild=true ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false
dotnet build 執行結果如下:
dotnet publish
dotnet publish ^
Lab.Host.Env.WebApi.csproj ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false
dotnet publish 執行結果如下:
msbuild、dotnet build、dotnet publish 都可以產生出 web.config 包含了 EnvironmentName 屬性
web.config 內容如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\Lab.Host.Env.WebApi.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Staging" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
這裡有有過往的打包指令
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" ^
Lab.Host.Env.WebApi.csproj ^
/p:DeployOnBuild=true ^
/p:WebPublishMethod=Package ^
/p:PackageAsSingleFile=true ^
/p:PackageLocation="bin\WebPackage" ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false
打包的效果如下
最後,通過打包好的 *deploy.cmd + WebDeploy Service 部署到 IIS
Lab.Host.Env.WebApi.deploy.cmd /y /m:https://$(WebDeploy.Server):8172/MSDeploy.axd /i:true /u:$(WebDeploy.UserId) /p:$(WebDeploy.Password) /a:Basic "-AllowUntrusted=True" "-setParam:name='IIS Web Application Name',value='$(WebDeploy.IISApp)'"
taskfile
當指令碼越來越多時,我們會用 task 集中管理,把剛剛提到的 dotnet run 放進去
# Taskfile.yml
version: "3"
dotenv: [ "secrets/secrets.env" ]
tasks:
webapi:
desc: WebApi Development
dir: "src/Lab.Host.Env.WebApi"
cmds:
- dotnet run --environment Staging
執行結果如下
作業系統的環境變數(Windows)
以下操作需要管理員的權限
Cmd
setx ASPNETCORE_ENVIRONMENT Staging /M
PowerShell
[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Staging", "Machine")
在 Windows 環境變數
從程式碼注入
通過 WebApplicationOptions 指定
WebApplication.CreateBuilder
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
EnvironmentName = Environments.Staging
});
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
builder.Environment
除了在 WebApplication.CreateBuilder 注入,也可以後續透過 builder.Environment 注入,Environment 並不是唯讀物件所以可以在任意階段注入你想要的,不過,建議集中建立應用程式的地方決定就好,不要有太多地方可以改變這個狀態
通過 System.Envrionment 設定
NET 提供 System.Environment.GetEnvironmentVariable、System.Environment.SetEnvironmentVariable 靜態方法讓我們操作環境變數
參考
在 ASP.NET Core 中使用多個環境 | Microsoft Docs
Visual Studio 發佈設定檔 (.pubxml) 以進行 ASP.NET Core應用程式部署 | Microsoft Docs
web.config 檔案 | Microsoft Docs
延伸閱讀
如何使用組態 Microsoft.Extensions.Configuration | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)
如何使用應用程式秘密組態 | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)
.NET Core / .NET Fx 應用程式如何在開發環境使用環境變數 | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)
範例位置
sample.dotblog/Host/Lab.Host.Env at master · yaochangyu/sample.dotblog (github.com)
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET