ASP.NET Core 支援在不同 runtime 環境可以進行不同的行為。應用程式啟動時,會讀取 ASPNETCORE_ENVIRONMENT
環境變數來判斷執行中的環境,並將值儲存在 IHostingEnvironment.Environment
屬性中。ASPNETCORE_ENVIRONMENT
可以是任何值,但 ASP.NET Core 架構中有內建三個值:
沒有設定 ASPNETCORE_ENVIRONMENT
環境變數時,預設為 Production
。
Windows 和 macOS 中,環境變數與其值不區分大小寫。 Linux 環境則預設為區分大小寫。
在 Properties/launchSettings.json 檔案中,可以設定開發時使用的環境。在這個檔案中設定的值,會覆寫系統中的設定。透過 MVC 範本建立的專案,會自動產生 launchSettings.json 檔案:
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:54061",
"sslPort": 44315
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"ironman2018": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}
我們可以透過修改 profile 中的 ASPNETCORE_ENVIRONMENT
值,來設定開發時使用的環境。
除了直接修改 Properties/launchSettings.json 檔案外,VS 也有提供 GUI 來編輯這個檔案。在 VS 中,「右鍵點選專案 > 屬性 > 偵錯頁籤」即可編輯。
說這麼多,到底要怎麼在應用程式中根據不同環境來進行不同的工作呢?用個簡單的例子來跟大家說明。完整範例程式碼可以參考這邊。
先建立一個 IEnvironmentsService
介面,包含 string GetEnvironmentName()
方法,會回傳 runtime 的環境名稱。並建立三個實作此介面的類別,分別代表 Development、Production 和 Rex 三種環境。
public interface IEnvironmentsService
{
/// <summary>
/// 回傳 runtime 的環境名稱
/// </summary>
/// <returns></returns>
string GetEnvironmentName();
}
public class DevelopmentEnvironmentsService : IEnvironmentsService
{
public string GetEnvironmentName()
{
return "Development";
}
}
public class ProductionEnvironmentsService : IEnvironmentsService
{
public string GetEnvironmentName()
{
return "Production";
}
}
public class RexEnvironmentsService : IEnvironmentsService
{
public string GetEnvironmentName()
{
return "Rex";
}
}
接下來在 Startup.ConfigureServices
方法中,依不同的環境將對應的服務加到依賴注入的服務容器中:
public void ConfigureServices(IServiceCollection services)
{
// ......
// other services registration
// ......
if (_env.IsDevelopment())
{
services.AddSingleton<IEnvironmentsService, DevelopmentEnvironmentsService>();
}
if (_env.IsProduction())
{
services.AddSingleton<IEnvironmentsService, ProductionEnvironmentsService>();
}
// 會忽略大小寫
if (_env.IsEnvironment("rEx"))
{
services.AddSingleton<IEnvironmentsService, RexEnvironmentsService>();
}
}
IHostingEnvironment
提供四種擴充方法來判斷 runtime 的環境,分別為 IsDevelopment
、IsStaging
、IsProduction
和 IsEnvironment
,前三個方法分別對應到 ASP.NET Core 架構中內建的三種環境類型,第四個方法則需傳入一個代表環境類型的字串,用來判斷是否符合 runtime 時的環境名稱。由於 IsEnvironment
方法在判斷時會忽略大小寫,所以建議使用這個方法而非 _env.EnvironmentName == "rEx"
。另外也可以透過 tag helper 來判斷 runtime 環境。
再來建立 EnviromentsController
,同時加入 Index
Action:
public class EnviromentsController : Controller
{
private readonly IEnvironmentsService _environmentsService;
public EnviromentsController(IEnvironmentsService environmentsService)
{
_environmentsService = environmentsService;
}
public IActionResult Index()
{
ViewBag.EnvironmentName = _environmentsService.GetEnvironmentName();
return View();
}
}
最後,建立 EnviromentsController/Index.cshtml
,對應到 Controller/Action 的 View:
@{
ViewData["Title"] = "Environments Example";
}
<h2>Environments Example</h2>
<hr/>
<h3>Injection in ConfigureServices</h3>
<p>Runtime Environment Name: @ViewBag.EnvironmentName</p>
<hr/>
<h3>Tag Helper</h3>
<environment include="Development">
Dev<br/>
</environment>
<environment include="Production">
Prod<br />
</environment>
<environment exclude="Rex">
Not from Rex<br />
</environment>
因為預設的 ASPNETCORE_ENVIRONMENT
環境變數值是 Development,瀏覽器上顯示的畫面會是這樣:
Startup
類別和方法除了在程式邏輯中判斷環境以外,也可以定義符合環境類型的 Startup
類別和方法,讓應用程式來判斷執行時的使用對象。
建立代表環境類型的 Startup{EnvironmentName}
類別,並修改 Main
方法中的 CreateWebHostBuilder
方法,就可以讓應用程式抓到對應的類別。
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
// 原本是這樣
// public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
// WebHost.CreateDefaultBuilder(args)
// .UseStartup<Startup>();
// 改成這樣來讓應用程式找到對應環境的 Startup 類別
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var startupAssemblyName = typeof(Startup).GetTypeInfo().Assembly.FullName;
return WebHost.CreateDefaultBuilder(args)
.UseStartup(startupAssemblyName);
}
}
public class StartupDevelopment
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IEnvironmentsService, DevelopmentEnvironmentsService>();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Enviroments}/{action=StartupClassConvention}/{id?}");
});
}
}
或者可以在原本的 Startup
類別中建立 Configure{EnvironmentName}
或 Configure{EnvironmentName}Services
方法,也可以讓應用程式在啟動時呼叫對應的方法。
public void ConfigureProduction(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Enviroments}/{action=StartupConfigureMethodConvention}/{id?}");
});
}