Single File ASP.NET Core 5 web app 不加载静态资源
Single File ASP.NET Core 5 web app does not load static resources
我正在尝试创建单个文件 asp.net core 5 网络应用程序。目标是拥有一个 .exe 文件,运行 Kestrel 服务器通过执行此 exe 文件并在浏览器中加载页面。
我在 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 文件,当我将其复制到其他地方时,运行s 没有问题。
但是当我浏览页面时,加载了 none 个静态文件:
生成单个文件 asp.net 核心应用程序的正确方法是什么,以便它加载静态内容?
编辑
根据要求,将发布后的输出截图放在这里
编辑 2
要获得可复制的项目:
Visual Studio 2019 -> 新解决方案 -> ASP.NET 具有以下配置的核心 Web 应用程序
编辑 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-validation
和 jquery-validation-unobtrusive
文件夹完全丢失。
有什么线索吗?
我创建了一个包含最新更改的 github repo。
我试图在新的 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
与你的场景类似,还有bootstrap。
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>
-- 2021 年 11 月 29 日更新
My expectation was that .exe will be a self-contained bundle and upon
execution it would extract static files and be able to resolve
references to it. That doesn't seem to be the case.
那么你必须将 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\**\*" />
我可能会转载你的问题。
我发布了项目,并在一个不相关的文件夹打开命令行,用完整路径打开exe。那么静态资源就不会加载了。
您可以尝试双击exe,看看是否可以运行。
解决问题:
在Program.cs
中添加Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
我找到了解决方案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 文件添加为嵌入式资源,并使用静态文件提供程序提供它。
根据请求,所有静态文件都在没有 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
});
我正在尝试创建单个文件 asp.net core 5 网络应用程序。目标是拥有一个 .exe 文件,运行 Kestrel 服务器通过执行此 exe 文件并在浏览器中加载页面。
我在 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 文件,当我将其复制到其他地方时,运行s 没有问题。 但是当我浏览页面时,加载了 none 个静态文件:
生成单个文件 asp.net 核心应用程序的正确方法是什么,以便它加载静态内容?
编辑
根据要求,将发布后的输出截图放在这里
编辑 2
要获得可复制的项目:
Visual Studio 2019 -> 新解决方案 -> ASP.NET 具有以下配置的核心 Web 应用程序
编辑 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-validation
和 jquery-validation-unobtrusive
文件夹完全丢失。
有什么线索吗?
我创建了一个包含最新更改的 github repo。
我试图在新的 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
与你的场景类似,还有bootstrap。
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>
-- 2021 年 11 月 29 日更新
My expectation was that .exe will be a self-contained bundle and upon execution it would extract static files and be able to resolve references to it. That doesn't seem to be the case.
那么你必须将 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\**\*" />
我可能会转载你的问题。
我发布了项目,并在一个不相关的文件夹打开命令行,用完整路径打开exe。那么静态资源就不会加载了。
您可以尝试双击exe,看看是否可以运行。
解决问题:
在Program.cs
中添加Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
我找到了解决方案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 文件添加为嵌入式资源,并使用静态文件提供程序提供它。
根据请求,所有静态文件都在没有 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
});