HostingEnvironment是承載應用當前執行環境的描述,它是對所有實現了IHostingEnvironment介面的所有類型以及對應對象的統稱。HostingEnvironment類型是對IHostingEnvironment介面的預設實現。對於通過HostingEnvironment的四... ...
HostingEnvironment是承載應用當前執行環境的描述,它是對所有實現了IHostingEnvironment介面的所有類型以及對應對象的統稱。如下麵的代碼片段所示,一個HostingEnvironment對象承載的執行環境的描述信息體現在定義這個介面的6個屬性上。ApplicationName和EnvironmentName分別代表當前應用的名稱和執行環境的名稱。WebRootPath和ContentRootPath是指向兩個根目錄的路徑,前者指向的目錄用於存放可供外界通過HTTP請求訪問的資源,後者指向的目錄存放的則是應用自身內部所需的資源。至於這個介面的ContentRootFileProvider和WebRootFileProvider屬性返回的則是針對這兩個目錄的FileProvider對象。如下所示的HostingEnvironment類型是對IHostingEnvironment介面的預設實現。[本文已經同步到《ASP.NET Core框架揭秘》之中]
1: public interface IHostingEnvironment
2: {
3: string ApplicationName { get; set; }
4: string EnvironmentName { get; set; }
5: IFileProvider ContentRootFileProvider { get; set; }
6: string ContentRootPath { get; set; }
7: IFileProvider WebRootFileProvider { get; set; }
8: string WebRootPath { get; set; }
9: }
10:
11: public class HostingEnvironment : IHostingEnvironment
12: {
13: string ApplicationName { get; set; }
14: string EnvironmentName { get; set; }
15: IFileProvider ContentRootFileProvider { get; set; }
16: string ContentRootPath { get; set; }
17: IFileProvider WebRootFileProvider { get; set; }
18: string WebRootPath { get; set; }
19: }
一、ApplicationEnvironment
接下來我們會對HostingEnvironment對象承載的執行環境描述信息的來源進行詳細介紹,不過在此之前我們有必要來瞭解另一個名為ApplicationEnvironment的類型,它定義在 “Microsoft.Extensions.PlatformAbstractions”這個NuGet包中。我們從其命名也可以看出這個對象描述的也是與執行環境相關的信息,而它承載的這些信息提下在如下四個屬性成員上,它們分別表示應用的名稱、基路徑、版本和採用的.NET Framework。
1: public class ApplicationEnvironment
2: {
3: public string ApplicationName { get; }
4: public string ApplicationBasePath { get; }
5: public string ApplicationVersion { get; }
6: public FrameworkName RuntimeFramework { get; }
7: }
如果需要獲取一個ApplicationEnvironment對象來描述當前執行環境,我們需要使用到如下這個名為PlatformServices的對象,它的Application屬性返回的就是我們所需的ApplicationEnvironment對象。因為該類型並不存在一個公共的構函數,所以我們不能直接實例化一個PlatformServices對象,不過我們可以利用Default屬性得到這個單例對象。
1: public class PlatformServices
2: {
3: private PlatformServices();
4: public ApplicationEnvironment Application { get; }
5: public static PlatformServices Default { get; }
6: }
對於一個ApplicationEnvironment對象來說,它的ApplicationName、ApplicationVersion和RuntimeFramework屬性決定於定義了程式入口Main方法的程式集,具體來說ApplicationName和ApplicationVersion分別返回這個程式集名稱和版本,而這個編譯這個程式集採用的.NET Framework的版本對應的正是RuntimeFramework屬性。至於ApplicationBasePath屬性,它返回的實際上是AppContext的BaseDirectoryPath屬性對應的路徑,運行時使用這個基礎路徑來解析被載入的目標程式集的真實路徑。針對這四個屬性的取值可以通過下麵這段程式來驗證。
1: public class Program
2: {
3: public static void Main()
4: {
5: Assembly assembly = typeof(Program).GetTypeInfo().Assembly;
6: AssemblyName assemblyName = assembly.GetName();
7: ApplicationEnvironment env = PlatformServices.Default.Application;
8:
9: Debug.Assert(env.ApplicationBasePath == AppContext.BaseDirectory);
10: Debug.Assert(env.ApplicationName == assemblyName.Name);
11: Debug.Assert(env.ApplicationVersion == assemblyName.Version.ToString());
12: Debug.Assert(env.RuntimeFramework.ToString() == assembly.GetCustomAttribute<TargetFrameworkAttribute>().FrameworkName);
13: }
14: }
如果我們沒有對應用的名稱做顯式設置,當前HostingEnvironment的ApplicationName屬性體現的應用名稱來源於這個ApplicationEnvironment對象的同名屬性。HostingEnvironment包括ApplicationName在內的四個屬性(不包括WebRootFileProvider和ContentRootFileProvider屬性,因為它們決定於對應ContentRootPath和WebRootPath屬性)都可以通過WebHostOptions來設置。通過前面一章的介紹我們知道WebHostOptions對象是根據WebHostBuilder的採用的配置來創建的,所以我們可以利用配置的方式來決定執行環境。
二、Configuration和WebHostOptions
對於通過HostingEnvironment的四個屬性(ApplicationName、EnvironmentName、WebRootPath和ContentRootPath) 承載的四個與執行環境相關的設置,在WebHostOptions對象上都具有對應的屬性,後者是前者的數據來源。由於WebHostOptions對象是WebHostBuilder根據它採用的配置來創建的,所以這些設置最初來源於使用的配置。值得一提的是,如果EnvironmentName屬性未作顯式設置,它使用的預設值為“Production”。
由於WebHostBuilder會採用環境變數作為配置來源,並且採用“ASPNETCORE_”作為環境變數過濾採用的首碼,所以我們完全可以按照如下的方式通過設置環境變數的方式來初始化由HostingEnvironment承載的執行環境選項。
1: Environment.SetEnvironmentVariable("ASPNETCORE_APPLICATIONNAME", "MyApp");
2: Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Staging");
3: Environment.SetEnvironmentVariable("ASPNETCORE_WEBROOT", @"c:\myapp\wwwroot\");
4: Environment.SetEnvironmentVariable("ASPNETCORE_CONTENTROOT", @"c:\myapp\contentroot");
5:
6: new WebHostBuilder()
7: .UseConfiguration(new ConfigurationBuilder().AddJsonFile("weboptions.json"))
8: .ConfigureServices(svcs => {
9: IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService<IHostingEnvironment>();
10: Debug.Assert(env.ApplicationName == "MyApp");
11: Debug.Assert(env.EnvironmentName == "Staging");
12: Debug.Assert(env.WebRootPath == @"c:\myapp\wwwroot\");
13: Debug.Assert(env.ContentRootPath == @"c:\myapp\contentroot");
14: })
15: .UseKestrel()
16: .Build();
雖然WebHostBuilder預設使用環境變數作為配置源,但是我們可以顯式地創建一個Configuration對象並通過調用它的擴展方法UseConfiguration進行“導入”。對於上面這段程式,如果我們將配置定義在一個具有如下結構的JSON文件(weboptions.json),我們只需要在創建WebHost之前按照如下的方式調用UseConfiguration方法將對應配置導入進來即可。
weboptions.json:
1: {
2: "applicationName": "MyApp",
3: "environment" : "Staging",
4: "webRoot" : "c:\\myapp\\wwwroot",
5: "contentRoot" : "c:\\myapp\\contentroot"
6: }
Program
1: new WebHostBuilder()
2: .UseConfiguration(new ConfigurationBuilder().AddJsonFile("weboptions.json").Build())
3: .ConfigureServices(svcs => {
4: IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService<IHostingEnvironment>();
5: Debug.Assert(env.ApplicationName == "MyApp");
6: Debug.Assert(env.EnvironmentName == "Staging");
7: Debug.Assert(env.WebRootPath == @"c:\myapp\wwwroot\");
8: Debug.Assert(env.ContentRootPath == @"c:\myapp\contentroot");
9: })
10: .UseKestrel()
11: .Build();
三、特殊的ApplicationName
對於HostingEnvironment的這四個屬性來說,表示應用名稱的ApplicationName比較特殊。雖然它的初始值來源於配置,當我們調用Configure方法或者UseStartup方法是,這個屬性會被覆蓋。如下這段程式與上面不同之處在於創建WebHost之前調用Configure方法,我們採用環境變數設置的應用名(“MyApp”)將失效。
1: Environment.SetEnvironmentVariable("ASPNETCORE_APPLICATIONNAME", "MyApp");
2: new WebHostBuilder()
3: .ConfigureServices(svcs => {
4: IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService<IHostingEnvironment>();
5: Debug.Assert(env.ApplicationName != "MyApp");
6: })
7: .UseKestrel()
8: .Configure(app => {})
9: .Build();
其實這個問題的答案我們在《應用的入口——Startup》中已經給出了。如下所示的是WebHostBuilder用於註冊Startup的兩個擴展方法Configure和UseStartup的定義,我們可以清楚地看到在創建並註冊Startup之前,它們都會設置當前應用的名稱。
1: public static class WebHostBuilderExtensions
2: {
3: public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action<IApplicationBuilder> configureApp)
4: {
5: var startupAssemblyName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name;
6:
7: return hostBuilder
8: .UseSetting("applicationName", startupAssemblyName)
9: …
10: }
11:
12: public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
13: {
14: var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;
15: return hostBuilder
16: .Use