如何在 .Net 核心应用程序中发布特定于环境的应用程序设置?

Posted

技术标签:

【中文标题】如何在 .Net 核心应用程序中发布特定于环境的应用程序设置?【英文标题】:How to publish environment specific appsettings in .Net core app? 【发布时间】:2016-08-31 17:52:29 【问题描述】:

我的 .Net 核心应用程序中有 3 个特定于环境的 appsettings 文件

project.json 我有这样的设置publishOptions。 (基于建议here)

"publishOptions": 
    "include": [
      "wwwroot",      
      "appsettings.development.json",
      "appsettings.staging.json",
      "appsettings.production.json",
      "web.config"
    ]
  ,

我有 3 个相应的启动类,它们根据环境使用适当的appsettings

var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile($"appsettings.env.EnvironmentName.json", optional: false, reloadOnChange: true);

但是,当我发布应用程序时,所有 3 个 appsettings 文件最终都会出现在所有环境中。如何发布特定于环境的 appsetting 文件?

【问题讨论】:

有人可以回答这个问题吗?将所有 appsettings.json 文件发布到所有环境中确实没有意义。 只要您在 Windows 上托管,就可以了,但如果您切换到区分大小写的操作系统 (*nix),您就会遇到麻烦。例如。 MS 的 EnvironmentName 字符串是 PascalCased。 您找到解决方案了吗?我正在使用 asp.netcore 3.1 并非如此。目前,我的 powershell 脚本会复制整个发布文件夹,然后删除不需要的 appsettings。请注意,即使您不删除 appsettings,您的应用程序也不会全部使用。您的应用程序将只使用appsettings.jsonappsettings.environmentname.json。启动时加载文件的顺序在这里很重要 【参考方案1】:

如果其他人想知道如何为多个环境使用不同的应用设置,这里是一个可能的解决方案。

如果*.csproj 对这些文件有条件逻辑,dotnet publish --configuration [Debug|Release] 会将相应的 appsettings.json 文件复制到发布文件夹中:

首先在.pubxml发布配置文件(可以在Visual Studio的Properties->PublishProfiles中找到)禁用默认包含所有内容文件
<PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <EnableDefaultContentItems>false</EnableDefaultContentItems>
</PropertyGroup>
然后指定条件调试/发布逻辑
<Choose>
    <When Condition="'$(Configuration)' == 'Debug'">
      <ItemGroup>
        <None Include="appsettings.json" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
        <None Include="appsettings.prod.json" CopyToOutputDirectory="Never" CopyToPublishDirectory="Never" />
      </ItemGroup>
    </When>
    <When Condition="'$(Configuration)' == 'Release'">
      <ItemGroup>
        <None Include="appsettings.json" CopyToOutputDirectory="Never" CopyToPublishDirectory="Never" />
        <None Include="appsettings.prod.json" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
      </ItemGroup>
    </When>
</Choose>
最后在Startup.cs里面尝试加载两个文件
public Startup(IHostingEnvironment env)

    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile($"appsettings.prod.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables();

    Configuration = builder.Build();

我希望这个解决方案对您有所帮助。

【讨论】:

不按要求回答问题。 我认为它确实按要求回答。每个环境在发布到时仅获取相关的 appsettings 文件。 @Ben,这个答案实际上必须标记为答案!它对我很有用! good to know 方法但没有回答问题,因为它清楚地说明了针对不同环境(如登台、生产、开发等)的发布,但此答案试图使用configuration 解决问题。跨度> 【参考方案2】:

我最近也不得不为此找到一个解决方案,我通过在.csproj 文件中添加一些设置并对Program.cs 进行微小更改来完成它。

<Project Sdk="Microsoft.NET.Sdk.Web">
    <!-- ... -->
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <EnvironmentName>Development</EnvironmentName>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <EnvironmentName>Production</EnvironmentName>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Stage|AnyCPU'">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <EnvironmentName>Staging</EnvironmentName>
    </PropertyGroup>
    <ItemGroup>
        <Content Remove="appsettings.json" />
        <Content Remove="appsettings.*.json" />
    </ItemGroup>
    <ItemGroup>
        <Content Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
        <Content Include="appsettings.*.json" Exclude="appsettings.$(EnvironmentName).json" DependentUpon="appsettings.json" CopyToOutputDirectory="Never" />
        <Content Include="appsettings.$(EnvironmentName).json" DependentUpon="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>

    <Target Name="RenameAppsettings" AfterTargets="Publish">
        <Move SourceFiles="$(PublishDir)\appsettings.$(EnvironmentName).json" DestinationFiles="$(PublishDir)\appsettings.overrides.json" />
    </Target>
</Project>

为了稍微解释一下,我为每个配置添加了一个&lt;EnvironmentName&gt; 元素,以便可以在构建过程中使用它。我使用appsettings.EnvironmentName.json(即appsettings.Staging.json)作为“覆盖”文件,所以我只是让它在构建过程中重命名必要的JSON文件。例如,当您运行dotnet publish -c Stage 时,它会将appsettings.Staging.json 文件发布到发布文件夹中,并将其重命名为appsettings.overrides.json。在您的Program.cs 中,您只需要包含appsettings.overrides.json 文件:

    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile($"appsettings.env.EnvironmentName.json", optional: true, reloadOnChange: true)
    .AddJsonFile($"appsettings.overrides.json", optional: true, reloadOnChange: true)

希望对你有帮助!

旁注:我包含appsettings.*.json 并将其设置为CopyToOutputDirectory="Never",这样它们在开发时仍会出现在Visual Studio 中。否则,如果您只想在 VS 中显示当前环境的 appsettings 文件,只需从 csproj 文件中删除该行即可。

【讨论】:

我尝试了您的解决方案,它运行良好,除了我使用“--output c:\temp”发布到特定文件夹并且我的 appsettings.json 正确地以 $OutputPath 结尾(我猜)是发布的暂存区(netcore2.1/win-x64),但不会被复制到最终的发布文件夹。有什么想法可以访问该 --output 目标文件夹吗?发布文件夹同时获取 appsettings.json 和 appsettings.Staging.json 而不是我的 Move 的结果。 嗯,我不确定我是否完全按照。它仍应将正确的结果发布到您指定的 --ouput 路径。您是否期望输出目录中有一个 appsettings.json 文件?使用我发布的解决方案,您将始终拥有 2 个 appsettings 文件。一个是appsettings.json,另一个是appsettings.overrides.json。它应该采用您的“覆盖”设置(即来自appsettings.Staging.json)并将其重命名为输出目录中的appsettings.overrides.json。也许您可以将您正在做的事情作为问题发布,我可以帮助您找到答案? 发布期间似乎有 2 个重要文件夹。一个是 bin/..win-x64 文件夹,第二个是发布文件夹(如果我不使用 --output 会在 win-x64 下创建发布文件夹)。我的 appsettings 在 win-x64 中是正确的,但在发布中不正确。如果我能找出指向发布文件夹的符号(如 $OutputPath),我会直接移动到那里,但我找不到那个。我已经对 Move 进行了硬编码并且它正在工作,但必须有一种方法可以正确地做到这一点。 啊,我明白了。获取发布目录的值为$(PublishDir)。您应该能够在我提供的解决方案中用$(PublishDir) 替换$(OutputPath)\publish,它应该可以满足您的需求。我还更新了使用$(PublishDir) 的答案,因为最好使用它,这样它就可以支持--output。感谢您提出! @Sensei_Shoh 我已经尝试了您的解决方案,并且在本地运行良好。但是,当部署到服务器时,即使我在发布中指定了“测试”环境,也会出现错误“找不到配置文件 'appsettings.Development.json' 并且不是可选的”?【参考方案3】:

您可以使用 MSBuild 条件选择性地在编译输出(或发布的输出)中包含文件。

<ItemGroup Condition="'$(Configuration)'=='Release'">
  <Content Remove="appsettings.Development.json;appsettings.Staging.json" />
  <None Include="appsettings.Development.json;appsettings.Staging.json" />
</ItemGroup>

上面忽略了编译目标配置为Release时的Development和Staging appsettings.json文件变种。

【讨论】:

【参考方案4】:

一种可能的方法是运行 prepublish 或 postpublic 脚本/命令,例如通过运行执行 dotnet publish-iis 的 gulp 任务(或者使用 scriptsprepublish 部分中的任务将文件复制到发布前.

将此添加到您的 project.json:

"scripts": 
  "postpublish": [ "gulp cleanconfig", "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]

您也可以在此处运行 cmd 或 shell 命令。但实际上不应该有任何理由首先要执行此操作,只需发送所有 3 个 appconfig 文件,因为在 Azure App Service 上,您可以根据环境变量切换模式,该环境变量通过Azure Portal 和发布时,暂存槽和生产槽只会交换,但环境变量会保留。

您不应该在 appsettings.json 中存储秘密(我假设您这样做以及您想要删除文件的原因)。相反,使用“用户机密”进行开发和环境变量来设置连接字符串等用于生产。像魅力一样工作,尤其是与 Azure 应用服务和 docker 容器一起使用。

【讨论】:

我在 appsettings 中没有任何秘密。但是,我们在 appsettings 中确实有连接字符串,每个环境都不同。我认为在 appsettings 中有连接字符串是完全可以的。我真的不明白为什么我必须跳过箍来发布特定于环境的应用程序设置。事实上,这应该是发布过程的标准行为。我认为没有人希望所有环境中的所有 appsettings 文件都结束。我认为我们在这里遗漏了一些东西。 好吧,如果连接字符串中包含用户名和密码或 api 密钥,则它是一个秘密。如果没有,那么发布所有 3 个文件也没有什么坏处。到底是怎么回事? 它没有用户名/密码。它也与实际应用设置文件包含哪些数据的问题无关 Tseng 在这方面是正确的,在通用级别上,您应该在工具链中删除一些您不希望在已发布结果中出现的文件,无论是在构建后执行的 gulp、grunt、powershell 文件/预部署 最后我最终使用了 Powershell 脚本来复制文件。在脚本中,我有逻辑根据环境复制 appsetting 文件。【参考方案5】:

你需要实际添加环境变量,according the official tutorial:

var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile($"appsettings.env.EnvironmentName.json", optional: false, reloadOnChange: true)
    // do not forget to add environment variables to your config!
    .AddEnvironmentVariables();

【讨论】:

【参考方案6】:

我有同样的问题,因为多个站点使用相同的代码库,我需要在不同的生产配置文件之间切换。

最后,我为每个客户端/站点创建了一个 powershell 脚本。该脚本将特定于客户端的配置文件复制到生产配置文件上,然后运行发布。我实际上对 appSettings.json 文件和 environment.ts 文件都这样做了。脚本看起来有点像这样:

Remove-Item –path ClientApp\src\environments\environment.prod.ts
Remove-Item –path appsettings.production.json
Write-Output "--> config files removed"
Copy-Item -path ClientApp\src\environments\environment.CLIENT-SITE-NAME.ts ClientApp\src\environments\environment.prod.ts
Copy-Item -path appsettings.CLIENT-SITE-NAME.json appsettings.production.json
Write-Output "--> config files copied"
dotnet build MYPROJ.csproj -c Release /p:DeployOnBuild=true /p:PublishProfile=CLIENT-SITE-NAME 
Write-Output "--> built & published"
Remove-Item –path ClientApp\src\environments\environment.prod.ts
Remove-Item –path appsettings.production.json
Write-Output "Finished"

在我的每个客户端站点的 .pubxml 文件中,我排除了所有非生产应用程序设置的发布,如下所示:

<ItemGroup>
  <Content Update="appsettings.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.site1.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.site2.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.development.json" CopyToPublishDirectory="Never" />
</ItemGroup>

我最终删除了生产文件,以确保不会使用发布向导将它们意外部署到错误的站点。

(我将密码存储在 pubxml 文件中,但您可以将其作为参数包含在脚本中)

【讨论】:

【参考方案7】:

就我而言,我有几个适用于多个实时环境的 appSettings 文件,例如appSettings.Env1Live.json、appSettings.Env2Live.json等

我查看了https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/visual-studio-publish-profiles?view=aspnetcore-3.1 文章,并在每个环境的相应发布 (pubxml) 配置文件中添加了以下语句。

例如为 PublishToEnvironment1.pubxml 添加:

<ItemGroup>
<Content Update="appsettings.*Live.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Development.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Env1Live.json" CopyToPublishDirectory="PreserveNewest" /></ItemGroup>

所以,在发布的文件夹中,我只有两个必要的文件,其他 appSettings.*Live.json 没有发布。

appsettings.json

appsettings.Env1Live.json

【讨论】:

这行得通,而且简单直接——我认为这应该是公认的答案。【参考方案8】:

在VS2017中添加多个环境

步骤 右键项目 --> 添加 --> NewItem - 选择 json 文件 - 将文件名写入 'appsettings.staging.json' 或 'appsettings.production.json'

【讨论】:

但它只有在我运行解决方案 localhost 时才有效。发布时,无论我在propertis(launch.json)中设置什么,环境始终是生产环境:/任何想法? @Muflix 您需要将 ASPNETCORE_ENVIRONMENT 变量设置为例如 Staging。它默认为生产。 @Anders Emil 实际上,当发布到 IIS 时,环境变量是从 web.config 中获取的,而不是从项目的 environment 属性中获取的。【参考方案9】:
        Consider you have multiple appsettings: dev,pre,prod. 
    You can have below configuration in your web project file.

         <!-- This configuration is done for dotnet publish command.
     It will make sure only environment specific files will be copied -->
          <ItemGroup Condition=" '$(EnvironmentName)' == 'Dev'">
            <Content Remove="appsettings.Prod.json" />
            <Content Remove="appsettings.Pre.json" />
          </ItemGroup>
          <ItemGroup Condition=" '$(EnvironmentName)' == 'Pre'">
            <Content Remove="appsettings.Prod.json" />
            <Content Remove="appsettings.Dev.json" />
            <Content Remove="appsettings.Development.json" />
          </ItemGroup>
          <ItemGroup Condition=" '$(**EnvironmentName**)' == 'Prod'">
            <Content Remove="appsettings.Pre.json" />
            <Content Remove="appsettings.Dev.json" />
            <Content Remove="appsettings.Development.json" />
          </ItemGroup>

        This confguration will help during publish. 
    It will not copy the appsettings which is not required for that environment.

        EnvironmentName (part of  <ItemGroup Condition=" '$(**EnvironmentName**)' == 'Prod'">) 
highlighted above should be passed as parameter in below command.

        dotnet publish -o ../../published/20191118A -c release /p:EnvironmentName=Development

【讨论】:

【参考方案10】:

到目前为止,我发现的最简单的方法是部署所有配置文件,然后在部署完成后删除额外的文件。 只需在部署 shell 或批处理脚本的末尾添加几行额外的行。

【讨论】:

【参考方案11】:

我用这个 nuget 包解决了这个问题:https://github.com/Microsoft/slow-cheetah/blob/master/doc/transforming_files.md

活动解决方案配置中安装和使用所有配置非常容易(在我的例子中 - 我为测试部署添加了一个新的“测试”配置)。

之后你可以在VS中安装这个扩展:https://marketplace.visualstudio.com/items?itemName=vscps.SlowCheetah-XMLTransforms。

刚才你可以通过这个工具在 VS 中为 .xml 或 .json 应用程序配置设置创建一个新的配置子文件(如手册中所述)。比如我有Debug、Test、Release文件(appsettings.Debug.json等)

下一步 - 为每个配置设置发布配置文件,发布后您将只有一个包含所有必要转换的文件。

转换的工作方式类似于 .net 核心 Web 应用程序中的经典 .json 环境转换。

【讨论】:

【参考方案12】:

针对使用 .csproj 的版本进行了更新

首先,在.csproj文件中,添加以下内容,确保发布时appsettings.json包含在发布目录中。

  <ItemGroup>
    <None Include="appsettings.json" CopyToPublishDirectory="Always" />
  </ItemGroup>

然后更新 Startup.cs 以包含:

public Startup(IHostEnvironment env)
    
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();
    

【讨论】:

【参考方案13】:

老实说,我认为这不是构建管道的正确任务。 dotnet cli 的发布功能也非常有限。使用 Tseng 向您展示的外部工具。部署是另一个比构建复杂的领域。

除了使用外部工具之外,在 dotnet cli 上没有内置方式!

【讨论】:

以上是关于如何在 .Net 核心应用程序中发布特定于环境的应用程序设置?的主要内容,如果未能解决你的问题,请参考以下文章

使用 GitHub Actions 在 .net 应用程序中填充特定于环境的变量

如何根据新的安全策略在 .Net 中发送电子邮件?

如何根据新的安全策略在 .Net 中发送电子邮件?

AWS Cognito:注册/登录后如何在 .Net 中发出身份验证请求

如何在 ASP.NET C# 中发送电子邮件

如何在 ASP.NET 控制器中发送客户端值?