ASP.NET Core 如何設置發布環境


在ASP.NET Core中自帶了一些內置對象,可以讀取到當前程序處於什么樣的環境當中,比如在ASP.NET Core的Startup類的Configure方法中,我們就會看到這么一段代碼:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }
            
    app.UseStaticFiles();
    app.UseCookiePolicy();
            
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

其中env.IsDevelopment()就可以讀出當前程序是否是處於開發環境當中,如果你在Visual Studio中運行ASP.NET Core項目那么上面的env.IsDevelopment()就會返回true,如果你發布(publish)了ASP.NET Core項目,並在IIS中運行發布后的項目代碼,那么上面的env.IsDevelopment()就會返回false。

 

env.IsDevelopment()其實是IHostingEnvironment接口(IHostingEnvironment接口默認就注冊在了ASP.NET Core的依賴注入容器中,可以在ASP.NET Core中任何需要用依賴注入的地方<例如,Startup類的構造函數(由於IHostingEnvironment接口默認就在ASP.NET Core的依賴注入容器中,所以在Startup類的構造函數中ASP.NET Core就可以注入IHostingEnvironment接口),Startup類的Configure方法,Controller的構造函數,中間件,MVC視圖等地方>使用IHostingEnvironment接口)的一個擴展方法,其定義在HostingEnvironmentExtensions這個擴展類中,可以看到HostingEnvironmentExtensions類定義了一些和ASP.NET Core運行環境相關的方法:

 

注意在ASP.NET Core 3.0中,HostingEnvironmentExtensions擴展類被HostEnvironmentEnvExtensions擴展類所替代,但是其實大同小異:

其中

  • IsDevelopment方法用來檢測ASP.NET Core項目當前是否處於開發環境,比如在Visual Studio中運行ASP.NET Core項目IsDevelopment方法就會返回true
  • IsProduction方法用來檢測ASP.NET Core項目當前是否處於生產環境,比如將ASP.NET Core項目發布(publish)后,IsProduction方法就會返回true
  • IsStaging方法用來檢測ASP.NET Core項目當前是否處於一個中間環境,比如如果項目還有測試環境,就可以將IsStaging方法用來檢測ASP.NET Core項目是否處於測試環境

那么為什么在Visual Studio中運行ASP.NET Core項目,HostingEnvironmentExtensions類的IsDevelopment方法會返回true呢?我們打開ASP.NET Core項目Properties節點下的launchSettings.json文件

其中的Json文本如下:

{
  "iisSettings": {
    "windowsAuthentication": false, 
    "anonymousAuthentication": true, 
    "iisExpress": {
      "applicationUrl": "http://localhost:52028",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "WebCoreEnvironments": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

我們可以看到在"profiles"下有一個"IIS Express"節點,其中有一個"ASPNETCORE_ENVIRONMENT"屬性,其值為Development,這就表示了當我們在Visual Studio中用IIS Express運行ASP.NET Core項目時,處於的是Development環境,所以此時HostingEnvironmentExtensions類的IsDevelopment方法會返回true。ASPNETCORE_ENVIRONMENT屬性后面可以定義任何值,但是一般來說都定義為Development、Production和Staging三個值,對應的HostingEnvironmentExtensions類的三個方法,如果你定義了一個其它的值(比如Staging2),可以用HostingEnvironmentExtensions類的IsEnvironment方法進行檢測,參數environmentName就是要檢測的環境名(比如Staging2)。當發布ASP.NET Core項目后,ASPNETCORE_ENVIRONMENT屬性的默認值會是Production,這就是為什么當ASP.NET Core項目發布后,HostingEnvironmentExtensions類的IsProduction方法會返回true。

 

此外ASPNETCORE_ENVIRONMENT屬性的值還可以影響ASP.NET Core項目appsettings文件的讀取,我們來看下面一個例子:

假設我們有個ASP.NET Core MVC項目叫WebCoreEnvironments,其中我們定義了兩套appsettings文件:appsettings.Development.json和appsettings.Production.json,分別用於存放項目開發環境和生產環境的參數, appsettings.Development.json和appsettings.Production.json中都定義了一個屬性叫TestString,不過兩個文件存儲的TestString屬性值不同。

appsettings.Development.json文件內容如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppSettings": {
    "TestString": "This is development environment"
  }
}

appsettings.Production.json文件內容如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppSettings": {
    "TestString": "This is production environment"
  }
}

當然可以在默認的appsettings.json文件中也定義TestString屬性,當項目中appsettings.Development.json和appsettings.Production.json文件不存在的時候,或當在appsettings.Development.json和appsettings.Production.json文件中找不到TestString屬性的時候,就會采用默認的appsettings.json文件的內容。appsettings.json文件內容如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AppSettings": {
    "TestString": "This is default environment"
  },
  "AllowedHosts": "*"
}

 

接着我們在ASP.NET Core MVC項目中定義了一個AppSettings類用於讀取和反序列化appsettings文件的內容:

namespace WebCoreEnvironments.Models
{
    public class AppSettings
    {
        public string TestString { get; set; }
    }
}

其中就一個TestString屬性和appsettings文件中的屬性同名。

 

然后我們在Startup類中設置讀取appsettings文件的代碼,其中相關代碼用黃色高亮標記了出來:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WebCoreEnvironments.Models;

namespace WebCoreEnvironments
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)//這里采用appsettings.{env.EnvironmentName}.json根據當前的運行環境來加載相應的appsettings文件
 .AddEnvironmentVariables();

            Configuration = builder.Build();
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddOptions(); services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

可以看到我們在讀取appsettings文件時,使用了AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)來根據當前ASP.NET Core項目所處的環境讀取相應的appsettings文件,如果是開發環境就讀取appsettings.Development.json,如果是生產環境就讀取appsettings.Production.json。

 

注意上面Startup構造函數中黃色高亮代碼的調用順序,會產生如下效果:

  • 首先,我們調用了AddJsonFile("appsettings.json", optional: true, reloadOnChange: true),來加載默認的appsettings.json文件中的內容。
  • 然后,我們調用了AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true),根據當前的運行環境來加載相應的appsettings文件的內容,因為AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)放在了AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)的后面,所以在"appsettings.json"文件中屬性"TestString"的值,會被"appsettings.{env.EnvironmentName}.json"文件中屬性"TestString"的值覆蓋,這是因為"appsettings.{env.EnvironmentName}.json"文件在"appsettings.json"文件之后加載,所以"appsettings.{env.EnvironmentName}.json"文件會覆蓋"appsettings.json"文件中同名的屬性(但是在"appsettings.json"文件中有,而在"appsettings.{env.EnvironmentName}.json"文件中沒有的屬性,不會被覆蓋)。
  • 此外由於AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)的參數optional為true,所以如果當前運行環境對應的"appsettings.{env.EnvironmentName}.json"文件在ASP.NET Core項目中不存在,也不會導致AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)方法拋出異常,但是如果optional參數為false,AddJsonFile方法找不到文件就會拋出異常。
  • 最后,我們調用了AddEnvironmentVariables(),來加載操作系統中環境變量的值,由於AddEnvironmentVariables方法在最后,所以環境變量會覆蓋與"appsettings.json"和"appsettings.{env.EnvironmentName}.json"文件中同名的屬性。

所以最后ASP.NET Core中包含的是 "appsettings.json"、"appsettings.{env.EnvironmentName}.json"、"操作系統環境變量" 中屬性名去重后的並集。 

 

然后我們在Index.cshtml視圖文件(對應HomeController的Index方法)中定義了如下代碼:

@{
    Layout = null;
}

@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Options;
@using WebCoreEnvironments.Models

@inject IHostingEnvironment env
@inject IOptions<AppSettings> appSettings
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        Current environment is:@env.EnvironmentName
    </div>
    <div>
        Is this a development environment:@env.IsDevelopment().ToString()
    </div>
    <div>
        Is this a production environment:@env.IsProduction().ToString()
    </div>
    <div>
        TestString in AppSettings is :@appSettings.Value.TestString
    </div>
</body>
</html>

在Index.cshtml視圖中我們使用@inject標簽,讓ASP.NET Core MVC依賴注入了IHostingEnvironment接口變量env和IOptions<AppSettings>接口變量appSettings,分別用於讀取當前ASP.NET Core項目所處的環境和appsettings文件的內容。

接着我們用env.EnvironmentName、env.IsDevelopment和env.IsProduction來檢測當前ASP.NET Core項目所處的環境

然后我們用appSettings.Value.TestString輸出當前環境對應的appsettings文件中,TestString屬性的值。

 

當我們在Visual Studio中運行項目時,訪問Index.cshtml視圖,瀏覽器結果如下:

可以看到當前所處的環境是Development,所以env.IsDevelopment返回true,env.IsProduction返回false,並且appSettings.Value.TestString的值為我們在appsettings.Development.json文件中定義的內容。

 

現在我們發布ASP.NET Core項目到一個叫publish的windows文件夾,然后將其部署到IIS中的一個站點上:

我們現在訪問IIS站點中的Index.cshtml視圖,瀏覽器結果如下:

可以看到當前所處的環境是Production,所以env.IsDevelopment返回false,env.IsProduction返回true,並且appSettings.Value.TestString的值為我們在appsettings.Production.json文件中定義的內容。

 

現在我們發現當我們發布ASP.NET Core項目后,其ASPNETCORE_ENVIRONMENT屬性的值始終是Production,所以發布ASP.NET Core項目后其處於Production環境。那么有沒有辦法將發布后的ASP.NET Core項目改為Staging環境(比如需要部署到測試環境服務器)呢?

這時我們可以從ASP.NET Core項目發布后的文件夾中找到web.config文件:

 

將其打開后可以看到其中有一個aspNetCore的XML節點如下:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\WebCoreEnvironments.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
    </system.webServer>
  </location>
</configuration>
<!--ProjectGuid: b9c7e11d-171e-41c4-b7aa-3c0225f76647-->

將其改為如下,然后再放到IIS的站點目錄下,現在ASP.NET Core項目就處於Staging環境了:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\WebCoreEnvironments.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" >
          <environmentVariables>
              <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Staging" />
          </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>
<!--ProjectGuid: b9c7e11d-171e-41c4-b7aa-3c0225f76647-->

注意,上面arguments=".\WebCoreEnvironments.dll"屬性為你的ASP.NET Core項目程序集dll文件名。 

 

 

如何不修改ASP.NET Core項目Startup類的構造函數

我們看到上面的代碼中,我們有修改ASP.NET Core項目中Startup類的構造函數,從ASP.NET Core 2.0開始,我們可以不修改項目的Startup類構造函數,例如我們也可以采用新建ASP.NET Core 2.0項目時Startup類的默認構造函數:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WebCoreEnvironments.Models;

namespace WebCoreEnvironments
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddOptions(); services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

但是我們要修改ASP.NET Core項目中Program類(在ASP.NET Core項目中的Program.cs文件中)的代碼如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace WebCoreEnvironments
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((webHostBuilderContext, configurationbuilder) => { var env = webHostBuilderContext.HostingEnvironment;//可以通過WebHostBuilderContext類的HostingEnvironment屬性得到IHostingEnvironment接口對象
 configurationbuilder.SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)//這里采用appsettings.{env.EnvironmentName}.json根據當前的運行環境來加載相應的appsettings文件
 .AddEnvironmentVariables(); })
           .UseStartup<Startup>();
    }
}

可以看到其實我們就是將ASP.NET Core項目Startup類構造函數中配置讀取appsettings文件的邏輯,移植到了ASP.NET Core項目中的Program類里面,其中可以通過WebHostBuilderContext類的HostingEnvironment屬性得到IHostingEnvironment接口對象,來讀取當前ASP.NET Core項目所處的運行環境是什么。

這里在ASP.NET Core 3.0中稍有不同,ASP.NET Core 3.0是通過HostBuilderContext類的HostingEnvironment屬性得到IHostEnvironment接口對象,來讀取當前ASP.NET Core項目所處的運行環境是什么,ASP.NET Core 3.0的Program類代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace WebCoreEnvironments
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostBuilderContext, configurationbuilder) => { var env = hostBuilderContext.HostingEnvironment;//可以通過HostBuilderContext類的HostingEnvironment屬性得到IHostEnvironment接口對象
 configurationbuilder.SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)//這里采用appsettings.{env.EnvironmentName}.json根據當前的運行環境來加載相應的appsettings文件
 .AddEnvironmentVariables(); })                 .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

 

ASP.NET Core 3.0

在ASP.NET Core 3.0中,IHostingEnvironment已經過時,請用IWebHostEnvironment接口對象(繼承IHostEnvironment接口對象)替換本文所述的IHostingEnvironment接口對象。

詳情可以查看:Migrate from ASP.NET Core 2.2 to 3.0

 

關於本文所述的內容,可以參考微軟官方關於ASP.NET Core多環境配置的文檔,鏈接如下:

Use multiple environments in ASP.NET Core

 

下載本文示例項目代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM