翻译 - ASP.NET Core 基本知识 - 环境(Environments)

翻译自 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-6.0

ASP.NET Core 基于运行环境使用一个环境变量配置应用程序的行为。

环境

ASP.NET Core 从下列环境变量读取配置决定运行时的环境:

  • DOTNET_ENVIRONMENT
  • ASPNETCORE_ENVIRONMENT 当 ConfigureWebHostDefaults 被调用的时候。默认的 ASP.NET Core web 应用程序模板调用 ConfigureWebHostDefaults。ASPNETCORE_ENVIRONMENT  的值会覆盖 DOTNET_ENVIRONMENT  的值。

IHostEnvironment.EnvironmentName 可以被设置为任意值,但是下面的值是由框架本身提供的:

  • Development(开发):launchSettings.json 文件在本机上设置 ASPNETCORE_ENVIRONMENT  为 Development。
  • Staging(阶段)
  • Production (生产):如果 DOTNET_ENVIRONMENT 和 ASPNETCORE_ENBIRONMENT  都没有设置的话,默认为 Production。

下面的代码:

  • 当 ASPNETCORE_ENVIRONMENT  被设置为 Development 的时候调用了 UseDeveloperExceptionPage
  • 当 ASPNETCORE_ENVIRONMENT 被设置为 Staging,Production,或者 Staging_2 时,调用了 UseExceptionHandler
  • 注入 IWebHostEnvironment 到方法 Startup.Configure 中。当应用程序仅仅要求在 Startup.Configure 中使用最少的代码去区分每一个环境的时候,这种方法很有用。
  • 和通过使用 ASP.NET Core 模板生成的代码是相似的
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    if (env.IsProduction() || env.IsStaging() || env.IsEnvironment("Staging_2"))
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Environment Tag Helper 使用 IHostEnvironment.EnvironmentName 的值包含或者排除元素中的标记:

<environment include="Development">
    <div>The effective tag is: &lt;environment include="Development"&gt;</div>
</environment>
<environment exclude="Development">
    <div>The effective tag is: &lt;environment exclude="Development"&gt;</div>
</environment>
<environment include="Staging,Development,Staging_2">
    <div>
        The effective tag is:
        &lt;environment include="Staging,Development,Staging_2"&gt;
    </div>
</environment>
示例代码(sample code)中的 About page 页面包含了上面的标记并且显示了 IWebHostEnvironment.EnvironmentName 的值。

在 Windows 和 macOS上,环境变量和值是不区分大小写的,在 Linux 上是区分大小写的。

创建环境变量示例

本文档中使用的示例代码(sample code)是基于名称为 EnvironmentsSample 的 Razor Pages 工程。

下面的代码创建并运行名称为 EnvironmentsSample 的 web 应用程序:

dotnet new webapp -o EnvironmentsSample
cd EnvironmentsSample
dotnet run --verbosity normal

当应用程序运行的时候,会有以下输出:

Using launch settings from c:\tmp\EnvironmentsSample\Properties\launchSettings.json
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: c:\tmp\EnvironmentsSample

开发阶段和 launchSettings.json

开发环境可以启用一些不应该在生产环境中暴露的功能特性。例如, ASP.NET Core 的模板在开发环境中启用了 Developer Exception Page

本机开发环境可以在工程中的 Properties\launchSettings.json 中设置。在 launchSettings.json 中设置的环境值会覆盖系统中的环境变量值。

launchSettings.json 文件:

  • 仅仅会用在本地开发及其
  • 未部署的
  • 包含简要配置设置

下面的 JSON 展示了一个使用 Visual Studio 或者 dotnet new 创建的名称为 EnvironmentsSample ASP.NET Core web 工程的 launchSettings.json 文件的内容:

{
  "iisSettings": {
    "windowsAuthentication": false, 
    "anonymousAuthentication": true, 
    "iisExpress": {
      "applicationUrl": "http://localhost:64645",
      "sslPort": 44366
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "EnvironmentsSample": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

上面的标记包含了两个配置文件:

  • IIS Express: 从 Visual Studio 启动应用程序时默认使用的配置。键值 "commandName" 对应值 "IISExpress",所以,IISExpress 是 web 服务器。你可以设置工程的配置文件或者包含其它任意的配置文件。例如,下面的图片展示了选择项目名称加载 Kestrel web 服务器(Kestrel web server)。
  • EnvironmentsSample: 配置文件的名称就是项目名称。当使用 dotnet run 启动加载应用程序的时候,这个配置文件会被默认使用。键 "commandNmae" 对应的值是 "Project",所以 Kestrel web 服务器(Kestrel web server)会被加载。

commandName 的值指定了要加载的 web 服务器。commandName 的值可以是以下值的其中之一:

  • IISExpress: 启动 IIS Express
  • IIS: 没有 web 服务器启动。期望可以使用 IIS。
  • Project: 启动 Kestrel

Visual Studio 项目属性的 Debug 标签提供了编辑 launchSettings.json 文件的。对项目配置文件的修改可能直到 web 服务器重启后才会生效。Kestrel 在它可能检测到它的环境的之前必须重新启动。

 

 下面的 launchSettings.json 文件包含多个配置:

{
  "iisSettings": {
    "windowsAuthentication": false, 
    "anonymousAuthentication": true, 
    "iisExpress": {
      "applicationUrl": "http://localhost:64645",
      "sslPort": 44366
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IISX-Production": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    },
    "IISX-Staging": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Staging",
        "ASPNETCORE_DETAILEDERRORS": "1",
        "ASPNETCORE_SHUTDOWNTIMEOUTSECONDS": "3"
      }
    },
    "EnvironmentsSample": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "KestrelStaging": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Staging"
      }
    }
  }
}

可以通过以下途径选择配置:

  • 从 Visual Studio 界面
  • 在命令行 shell 中使用 dotnet run,使用选项 --launch-profile 设置配置名称。这种方法只支持 Kestrel 配置
dotnet run --launch-profile "SampleApp"

使用  Visual Studio Code 时,环境变量可以在 .vscode/launch.json 文件中设置。下面的示例设置了一些 Host configuration values environment variables

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET Core Launch (web)",
            "type": "coreclr",
            // Configuration ommitted for brevity.
            "env": {
                "ASPNETCORE_ENVIRONMENT": "Development",
                "ASPNETCORE_URLS": "https://localhost:5001",
                "ASPNETCORE_DETAILEDERRORS": "1",
                "ASPNETCORE_SHUTDOWNTIMEOUTSECONDS": "3"
            },
            // Configuration ommitted for brevity.

.vscode/launch.json 文件仅仅被 Visual Studio Code 使用。

生产环境

生产环境应该配置为最大安全性,性能好(performance),应用程序的健壮性。一些公共的不同于开发环境的设置包括:

  • 缓存(Caching
  • 客户端资源是被打包的,最小化的,尽可能的使用 CDN 提供服务
  • 禁用调试错误页面
  • 友好的错误页面
  • 生产环境日志(logging)和监控。例如,Application Insights

设置环境

使用环境变量或者平台设置用于测试目的设置一个指定的环境通常是有用的。如果环境没有设置,默认是 Production,会禁用大部分的调试特性。设置环境的方法依赖于操作系统。

当主机被创建后,应用程序读取的最后的环境设置决定了应用程序的环境。应用程序的环境在应用程序运行期间不能改变。

示例代码(sample code)中的  About page 展示了 IWebHostEnvironment.EnvironmentName 的值。

Azure 应用程序服务

通过以下步骤在  Azure App Service 中设置环境:

  1. App Services 中选择应用程序
  2. Settings 分组中,选则 Configuration
  3. Application settings 标签中,选择 New application setting
  4. Add/Edit application setting 窗口中,为 Name 提供 ASPNETCORE_ENVIRONMENT  值。对于 Value,提供环境(例如,Staging
  5. 选择 Deployment slot setting 选择框,如果你希望对于当前 slot 的环境设置在 slot 切换的时候仍然保留。更多信息,查看 Azure 文档中的 Set up staging environments in Azure App Service
  6. 选择 Ok 关闭 Add/Edit application setting window
  7. 选择 Configuration 顶部的 Save

Azure 应用程序服务会在一个应用程序设置在 Azure portal 中被添加,改变或者删除后重新启动应用程序。

Windows

launchSettings.json 中的环境值会覆盖系统环境中的值。

当应用程序使用 dotnet run 启动时,在当前会话中,为了设置 ASPNETCORE_ENVIRONMENT  的值,可以使用下面的命令:

Command prompt

set ASPNETCORE_ENVIRONMENT=Staging
dotnet run --no-launch-profile

PowerShell

 
 
 
 
 
 
$Env:ASPNETCORE_ENVIRONMENT = "Staging"
dotnet run --no-launch-profile

上面命令中对于 ASPNETCORE_ENVIRONMENT  的设置仅仅对于从命令窗口启动的进程起作用。

在 Windows 中设置全局的值,可以使用以下方法之一:

  • 打开 Control Panel > System > Advanced system settings,添加或者编辑 ASPNETCORE_ENVIRONMENT  的值:

     

     

  • 使用管理员打开 command prompt,使用 setx 命令或者使用管理员打开 PowerShell 命令 prompt 然后使用 [Environment]::SetEnvironmentVariable:
    command prompt:
setx ASPNETCORE_ENVIRONMENT  Staging /M

    /M 开关表明以系统级别设置环境变量。如果没有使用 /M 开关,环境变量只是设置在当前用户账号下。

      PowerShell

[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Staging", "Machine")

    Machine 选项的值表明是以系统级别设置环境。如果选项的值更改为 User,环境变量只设置在当前账号下有效。

当 ASPNETCORE_ENVIRONMENT  环境变量是全局设置的,它将会影响在值设置之后打开的任意命令窗口运行命令 dotnet run。launchSettings.json 中的环境的值会覆盖系统环境中的值。

 web.config

如何使用 web.config 设置 ASPNETCORE_ENVIRONMENT 环境变量,查看 ASP.NET Core Module 中的设置环境变量部分。

项目文件或者发布配置文件

对于在 Windows 上使用 IIS 部署:在发布配置文件(publish profile (.pubxml))或者项目文件中包括有 <EnvironmentName> 属性。这种方法会在项目发布的时候在 web.config 文件中设置环境:

<PropertyGroup>
  <EnvironmentName>Development</EnvironmentName>
</PropertyGroup>

Per IIS Application Pool

对于设置一个在隔离的应用程序池运行(IIS 10.0 或者更新版本支持)的应用程序的 ASPNETCORE_ENVIRONMENT 环境变量,请查看 Environment Variables <environmentVariables> 主题的 AppCmd.exe command 部分。当为一个应用程序池设置 ASPNETCORE_ENVIRONMENT  环境变量时,它会覆盖系统级别的设置。

当在 IIS 中托管一个应用程序时,添加或者更改 ASPNETCORE_ENVIRONMENT 环境变量,可以使用以下的任意一种方法获取新的值:

  • 执行 net stop was /y,然后执行 net start w3svc
  • 重启服务器

macOS

为 macOS 设置当前环境可以在运行应用程序时执行以下命令:

ASPNETCORE_ENVIRONMENT=Staging dotnet run

或者,在运行应用程序之前使用 export 命令:

export ASPNETCORE_ENVIRONMENT=Staging

Machine-level 的环境变量在 .bashrc 或者 .bash_profile 文件中设置。使用任意的文本编辑器都可以编辑。添加下列语句:

export ASPNETCORE_ENVIRONMENT=Staging

Linux

对于 Linux 发行版,在当前会话使用 export 命令设置环境,使用 bash_profile 文件设置 machine-level 的环境设置。

在代码中设置环境

在创建主机的时候调用 UseEnvironment。查看 .NET Generic Host in ASP.NET Core

通过环境配置

通过环境加载配置,请查看 Configuration in ASP.NET Core

基于环境的 Startup 类和方法

注入 IWebHostEnvironment 到 Startup 类

注入 IWebHostEnvironment 到 Startup 的构造方法。这种方法在应用程序在区分只有几个环境,并且代码量很少的情况下,用来配置 Startup 是很有用的。

在下面的例子中:

  • 环境存储在 _env 字段中
  • _env 被用在 ConfigureServices 和 Configure 中基于应用程序的环境应用一些启动时的配置
public class Startup
{
    public Startup(IConfiguration configuration, IWebHostEnvironment env)
    {
        Configuration = configuration;
        _env = env;
    }

    public IConfiguration Configuration { get; }
    private readonly IWebHostEnvironment _env;

    public void ConfigureServices(IServiceCollection services)
    {
        if (_env.IsDevelopment())
        {
            Console.WriteLine(_env.EnvironmentName);
        }
        else if (_env.IsStaging())
        {
            Console.WriteLine(_env.EnvironmentName);
        }
        else
        {
            Console.WriteLine("Not dev or staging");
        }

        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app)
    {
        if (_env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

Startup 类约定

当一个 ASP.NET Core 应用程序启动时,Startup 类(Startup class )引导应用程序。应用程序可以为不同的环境定义多个 Startup 类。合适的 Startup 类会在运行时被选中。类的名称的前缀匹配了当前环境的类会优先使用。如果一个匹配的 Startup{EnvironmentName} 的类没有被发现,Startup 类会被使用。这种方法在应用程序需要区分每一个环境使用大量代码配置不同环境的启动时非常有用。一般的应用程序不会用到这种方法。

为了实现基于环境的 Startup 类,创建一个 Startup{EnvironmentName} 类和一个备用的 Startup 类:

public class StartupDevelopment
{
    public StartupDevelopment(IConfiguration configuration)
    {
        Configuration = configuration;
        Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.Name);
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseDeveloperExceptionPage();

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

public class StartupProduction
{
    public StartupProduction(IConfiguration configuration)
    {
        Configuration = configuration;
        Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.Name);
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app)
    {

        app.UseExceptionHandler("/Error");
        app.UseHsts();

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
        Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.Name);
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

使用重载方法 UseStartup(IWebHostBuilder, String) 接收一个程序集名称:

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

    public static IHostBuilder CreateHostBuilder(string[] args)
    {
        var assemblyName = typeof(Startup).GetTypeInfo().Assembly.FullName;

        return   Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup(assemblyName);
            });
    }
}

Startup 方法约定

Configure 和 ConfigureServices 方法支持特定环境版本的 Configure<EnvironmentName> 和 Configure<Environment>Services。如果匹配的 Configure<Environment>Services 或者 Configure<Environment> 方法没有被发现,ConfigureServices 或者 Configure 方法会被使用。这种方法在应用程序在配置启动时需要使用大量代码来区分每一个环境时是有用的:

public class Startup
{
    private void StartupConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void ConfigureDevelopmentServices(IServiceCollection services)
    {
        MyTrace.TraceMessage();
        StartupConfigureServices(services);
    }

    public void ConfigureStagingServices(IServiceCollection services)
    {
        MyTrace.TraceMessage();
        StartupConfigureServices(services);
    }

    public void ConfigureProductionServices(IServiceCollection services)
    {
        MyTrace.TraceMessage();
        StartupConfigureServices(services);
    }

    public void ConfigureServices(IServiceCollection services)
    {
        MyTrace.TraceMessage();
        StartupConfigureServices(services);
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        MyTrace.TraceMessage();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }

    public void ConfigureStaging(IApplicationBuilder app, IWebHostEnvironment env)
    {
        MyTrace.TraceMessage();

        app.UseExceptionHandler("/Error");
        app.UseHsts();

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

public static class MyTrace
{
    public static void TraceMessage([CallerMemberName] string memberName = "")
    {
        Console.WriteLine($"Method: {memberName}");
    }
}

 

posted @ 2021-04-11 09:52  sims  阅读(593)  评论(0编辑  收藏  举报