【问题标题】:Single File ASP.NET Core 5 web app does not load static resources单文件 ASP.NET Core 5 Web 应用程序不加载静态资源
【发布时间】:2021-12-23 04:42:09
【问题描述】:

我正在尝试创建单个文件 asp.net core 5 web 应用程序。目标是拥有一个 .exe 文件,通过执行此 exe 文件运行 Kestrel 服务器并在浏览器中加载页面。

我在 VS 2019 中创建了一个 ASP.NET Core 5 模板应用程序。然后使用 cli 我运行以下命令:

dotnet publish -c Release -r win-x64 /p:PublishSingleFile=true \n 
/p:IncludeNativeLibrariesForSelfExtract=true --self-contained true

这会生成一个 exe 文件,当我复制到其他地方时,它可以正常运行。 但是当我浏览页面时,没有加载任何静态文件:

生成单个文件 asp.net 核心应用程序的正确方法是什么,以便加载静态内容?

编辑

根据要求,将发布后的输出截图放在这里

编辑 2

要获得可重现的项目:

Visual Studio 2019 -> 新解决方案 -> ASP.NET Core Web App,配置如下

编辑 3

感谢 @JHBonarius 的回答,我更改了 Program.cs 以将 ContentRoot 设置为一个临时文件夹,其中 wwwroot 内容正在被提取。

public class Program
    {
        public static void Main(string[] args)
        {
            var path = Path.Combine(Path.GetTempPath(), ".net", typeof(Program).Assembly.GetName().Name);

            var directory = 
                Directory
                    .GetDirectories(path)
                    .Select(path => new DirectoryInfo(path))
                    .OrderByDescending(di => di.LastWriteTime)
                    .First();

            CreateHostBuilder(args)
                .UseContentRoot(directory.FullName)
                .Build()
                .Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)            
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

虽然我很欣赏这似乎是一种 hack(我找不到任何有关此路径的官方文档),但我想要一些工作代码。

通过这些更改,页面现在加载静态资源,但不是全部。

这是解决方案资源管理器中wwwroot 文件夹的内容

这是在临时路径上提取的wwwroot 文件夹的内容

可以看出js/css 文件夹以及jquery-validationjquery-validation-unobtrusive 文件夹完全丢失。

有什么线索吗?

我创建了一个带有最新更改的github repo

【问题讨论】:

  • 如果您发布的应用程序没有PublishSingleFile 选项,它是否有效?
  • 你能显示关于你的单个 .exe 文件的屏幕截图吗?我的意思是我想在你的网站上看到你的内容。
  • 你也可以尝试设置ItemGroup在部署时包含静态文件。
  • @JasonPan 查看编辑后的帖子
  • @AndrewSilver 不,在那种情况下我没有一个文件。

标签: c# asp.net-core .net-5


【解决方案1】:

我试图在新的 asp net core 空项目上重现你的问题,它工作正常。

也许启动缺少一些配置。

这就是我所做的。

csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>

Startup.cs

public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseRouting();

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

wwwroot

与您的场景类似,还有引导程序。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>TEST</title>
    <link href="css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <script src="js/bootstrap.min.js"></script>
</body>
</html>

-- 29/11/2021 更新

我的期望是 .exe 将是一个独立的包,并且在 执行它将提取静态文件并能够解析 对它的引用。好像不是这样的。

那么,您必须将 wwwroot 中的所有内容都标记为嵌入式资源

<PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
   <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>

<ItemGroup>
    <Compile Remove="wwwroot\**" />
    <Content Remove="wwwroot\**" />
     <EmbeddedResource Include="wwwroot\**\*">
      <Link>wwwroot\%(RecursiveDir)%(Filename)%(Extension)</Link>
      <LogicalName>wwwroot\%(RecursiveDir)%(Filename)%(Extension) 
      </LogicalName>
    </EmbeddedResource>
    <None Remove="wwwroot\**" />
</ItemGroup>

并在Configure(IApplicationBuilder, IWebHostEnvironment)方法中进行文件提取

var basePath = AppDomain.CurrentDomain.BaseDirectory;
var wwwrootPath = Path.Combine(basePath, "wwwroot");

if (!Directory.Exists(wwwrootPath))
{
    var assembly = typeof(Startup).Assembly;
    Directory.CreateDirectory(wwwrootPath);

    var resourcePaths = assembly.GetManifestResourceNames()
        .Where(rnn => rnn.Contains("wwwroot"))
        .ToList();

    foreach (var resourcePath in resourcePaths)
    {
        var fileName = resourcePath;
        var filePath = Path.Combine(basePath, fileName);
        var fileInfo = new System.IO.FileInfo(filePath);
        fileInfo.Directory.Create();
        using var stream = File.Create(filePath);
        using var resourceStream = assembly.GetManifestResourceStream(resourcePath);

        resourceStream.CopyTo(stream);
    }
};

如果你不喜欢这种方法,你可以考虑EmbeddedFileProvider

Startup.cs:配置

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new EmbeddedFileProvider(
        assembly: typeof(Startup).Assembly, 
        baseNamespace: "TestApp.wwwroot"),
});

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/", async http => { http.Response.Redirect("/index.html"); });
});

csproj

<EmbeddedResource Include="wwwroot\**\*" />

【讨论】:

  • cześć Józef,您的输出中是否有wwwroot 文件夹(如我的编辑中所见)?您可以将您的 exe 文件复制到其他地方(仅 exe 文件)并尝试运行该应用程序吗?如果我从输出文件夹运行 exe 文件,因为wwwroot 文件夹包含所有静态文件,它运行良好(因为它可以解析对静态文件的引用)。我的期望是 .exe 将是一个独立的包,并且在执行时它将提取静态文件并能够解析对它的引用。好像不是这样的。
  • 更新了答案。无论您选择哪个选项,您都需要为wwwroot 文件夹设置嵌入式资源构建操作,并在Startup.cs:Configure 方法中执行一些技巧。
  • EmbeddedFileProvider" didn't work, that's the cleanest option imo. As for the first approach - that didn't work either, wwwroot` 被嵌入(我在输出文件夹中看不到它了),但是将 exe 文件复制到其他地方并运行服务器仍然无法解析对静态文件的引用。跨度>
  • 我很惊讶EmbeddedFileProvider 不起作用。您确定在 csproj 中指定了正确的基本命名空间和配置吗?这真的很重要。最后的手段是创建自定义IFileProvider,以类似于EmbeddedFileProvider 的方式实现它,并在尝试检索资源时添加额外的日志。
  • 我已经看过你的 github repo,你不需要做太多的事情来启用EmbeddedFileProvider。它在 csproj 和正确的基本命名空间 SingleFileApp.wwwroot 中的一个配置
【解决方案2】:

我找到了解决方案here

将以下内容添加到 csproj

<ItemGroup>
    <Content Update="wwwroot\**" ExcludeFromSingleFile="false">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>

因为发生的情况是 .exe 包含一个存档,该存档被提取到一个临时目录。

但是,这仅适用于 .NET Core (3.1)。 They changed the behavior in .NET 5, where most dependencies are now directly loaded from the .exe or memory。所以内容现在被认为是外部依赖,内容路径设置为执行目录,而不是临时目录。

我提到的解决方案仍然会在.exe中包含wwwroot目录并将其解压缩到临时路径,但是由于内容路径不指向那里,因此找不到内容。

您可以使用.UseWebRoot("[path]") 更改内容路径。但是,我还没有找到获取临时目录路径的方法。

编辑:还有另外一个选择:将 wwwroot 文件放入 zip 文件中,将 zip 文件添加为嵌入式资源,并使用静态文件提供程序提供服务。

【讨论】:

  • 谢谢!看看我编辑的帖子,这很有帮助。
  • 赞成。比我提出的更简单的解决方案。如果 .net 版本无关紧要(我知道 .net core 3.1 支持结束,大约在一年后结束)我会使用它。
【解决方案3】:

我可能会转载你的问题。

我发布了项目,在一个不相关的文件夹中打开命令行,打开带有完整路径的 exe。那么静态资源就不会加载了。

您可以尝试双击该exe,检查它是否有效。

解决问题:

在 Program.cs 中添加Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);

【讨论】:

  • 试过了,没用。更加详细一些。注意:exe文件所在的目录下不能有“wwwroot”目录!
【解决方案4】:

根据请求,所有静态文件的请求都没有 wwwroot 路径,因此要么配置您的 html 和 js 并在路径中添加 wwwroot 或显式映射 wwwroot 内容,如下所示:

app.UseStaticFiles(new StaticFileOptions()
{
    FileProvider = new PhysicalFileProvider( Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot")),
    RequestPath = new PathString("/wwwroot")  // OR use "/" to map to base URI
});

【讨论】:

    猜你喜欢
    • 2018-08-04
    • 2016-07-07
    • 2011-07-25
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    • 2022-10-04
    • 2019-08-10
    • 1970-01-01
    相关资源
    最近更新 更多