IIS ASP.NET 6 次启动抛出 System.IO.DirectoryNotFoundException: D:\agent\_work\s\IdentityServer\wwwroot\

IIS ASP.NET 6 startup throws System.IO.DirectoryNotFoundException: D:\agent\_work\38\s\IdentityServer\wwwroot\

我们正在将我们的一个应用程序(在本例中为 IdentityServer)从 .NET 5 更新到 .NET 6。它由 IIS 托管并由 Azure Devops Services 部署。我们看到的问题是,在我们的开发环境中,网站无法加载,但在我们的暂存环境中,它运行得很好。我们在开发中看到的错误是

12:45:37.519|Fatal|1||Host terminated unexpectedly.||
System.IO.DirectoryNotFoundException: D:\agent\_work\s\IdentityServer\wwwroot\
   at Microsoft.Extensions.FileProviders.PhysicalFileProvider..ctor(String root, ExclusionFilters filters)
   at Microsoft.Extensions.FileProviders.PhysicalFileProvider..ctor(String root)
   at Microsoft.AspNetCore.Hosting.StaticWebAssets.StaticWebAssetsLoader.<>c.<UseStaticWebAssetsCore>b__1_0(String contentRoot)
   at Microsoft.AspNetCore.StaticWebAssets.ManifestStaticWebAssetFileProvider..ctor(StaticWebAssetManifest manifest, Func`2 fileProviderFactory)
   at Microsoft.AspNetCore.Hosting.StaticWebAssets.StaticWebAssetsLoader.UseStaticWebAssetsCore(IWebHostEnvironment environment, Stream manifest)
   at Microsoft.AspNetCore.Hosting.StaticWebAssets.StaticWebAssetsLoader.UseStaticWebAssets(IWebHostEnvironment environment, IConfiguration configuration)
   at Microsoft.AspNetCore.WebHost.<>c.<ConfigureWebDefaults>b__9_0(WebHostBuilderContext ctx, IConfigurationBuilder cb)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass9_0.<ConfigureAppConfiguration>b__0(HostBuilderContext context, IConfigurationBuilder builder)
   at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at IdentityServer.Program.Main(String[] args) in D:\agent\_work\s\IdentityServer\Program.cs:line 23

它报告的路径 D:\agent\_work\s\IdentityServer\wwwroot\ 很有趣,因为该路径与 DevOps 构建机器的路径相同。如果我们恢复到 .NET 5,我们就不会看到这个错误,而且我们在暂存计算机上也不会看到这个问题。

Program.csclass定义为

using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using NewRelic.LogEnrichers.Serilog;
using Serilog;
using Serilog.Events;

namespace IdentityServer
{
    public class Program
    {
        public static int Main(string[] args)
        {
            try
            {
                CreateLogger();
                Log.Information("Starting host...");
                CreateHostBuilder(args).Build().Run();
                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly.");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static void CreateLogger()
        {
            var configuration = GetConfiguration();
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .Enrich.FromLogContext() // allows logging middleware to inject output values
                .Enrich.WithThreadId()
                .Enrich.WithNewRelicLogsInContext()
                .CreateLogger();
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            var configuration = GetConfiguration();
            return Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(
                    webBuilder =>
                    {
                        webBuilder.UseConfiguration(configuration);
                        webBuilder.UseSerilog();
                        webBuilder.UseIIS();
                        webBuilder.CaptureStartupErrors(true);
                        webBuilder.UseStartup<Startup>();
                    });
        }

        private static IConfiguration GetConfiguration()
        {
            var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
            var builder = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{environment}.json", true, true);
            var configuration = builder.Build();

            return configuration;
        }
    }
}

我们确实有其他 .NET 6 Web 应用程序 运行 在此 IIS 实例上运行良好。我当时认为问题可能出在我们的发布管道中,但它们在环境之间的任务配置中是相同的。尝试在代码或配置中查找目录路径,但在任何地方都看不到。已尝试通过 Program.cs 中的 .UseWebRoot("path to folder").UseContentRoot("path to folder") 手动设置 WebRoot 和 ContentRoot 路径,但在日志或应用启动。

甚至更新了 web.config 文件以在 aspNetCore 元素中具有执行项目 dll 的确切路径,但仍然没有变化。

2022 年 2 月 10 日更新

向启动添加了调试输出以验证文件和文件夹路径。环境变量和执行文件路径中的所有内容看起来都是正确的。

ASPNETCORE_IIS_PHYSICAL_PATH - C:\inetpub\webapps\IdentityServer\
Executable Path: C:\inetpub\webapps\IdentityServer\IdentityServer.dll

问题最终变成了我们如何将更新从 DevOps 推送到服务器。我们的管道旨在从构建文件夹的 Release 目录中复制文件。这种方法的问题之一是站点 运行 不需要但在构建期间生成的文件也被复制到发布服务器。在这种情况下,.NET 6 中生成的新文件 .staticwebssets.runtime.json 被复制到我们的服务器。

.NET 6 的行为方式似乎是,如果环境设置为 Development,那么它将查找此文件以确定静态 Web 资产的位置。如果该文件不存在,那么它将假定文件位于站点的 wwwroot sub-directory 中。这对于您从本地 Visual Studio 运行 宁项目的情况是有意义的。有关此文件的更多详细信息可在另一个 中找到,其中包含指向 GitHub 中源代码的链接。为了解决我们的问题,我们更改了发布管道以使用 publish.zip 文件,该文件是在您对解决方案执行 运行 publish 命令时生成的。 zip 文件仅包含 运行 站点所需的文件,因此包含 none 的无关文件,例如 .staticwebssets.runtime.json。我们应该一直这样做...吸取教训。

我们现在解压缩 publish.zip 文件,应用任何文件转换,然后将解压缩的文件复制到网络服务器。