zoukankan      html  css  js  c++  java
  • 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=".logsstdout" />
        </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=".logsstdout" >
              <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

    下载本文示例项目代码

  • 相关阅读:
    RTTI机制
    QT 实现 QTabWidget 的多个Tab宽度之和 等于 TabWidget 的宽度
    安装QT
    给QT 程序的EXE文件添加图标
    OceanBase使用libeasy原理源码分析:服务器端
    libev 源码浅析
    [转载]CAP理论十二年回顾:"规则"变了
    replication 技术摘要
    函数调用和函数返回 栈变化情况
    The Secret To 10 Million Concurrent Connections The Kernel Is The Problem, Not The Solution
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/9827694.html
Copyright © 2011-2022 走看看