默认情况下,静态文件(如 HTML、CSS、图像和 JavaScript)是 ASP.NET Core 应用直接提供给客户端的资产。
有关添加或取代本文中指导的 Blazor 静态文件指导,请参阅 ASP.NET Core Blazor 静态文件。
映射静态资源路由终结点约定 (MapStaticAssets
)
创建性能良好的 Web 应用需要优化将资源传递到浏览器的过程。 MapStaticAssets 可能的优化包括:
- 提供一次给定资产,直到文件发生更改或浏览器清除其缓存为止。
ETag
设置和上次修改的标头。 - 更新应用后,阻止浏览器使用旧或失效的资源。 设置
Last-Modified
标头。 - 在响应上设置适当的 缓存标头 。
- 使用 缓存中间件。
- 尽可能提供资产的压缩版本。 此优化不包括缩小。
- 使用 CDN 为离用户更近的资产提供服务。
- 指纹资产,以防止重复使用旧版本的文件。
MapStaticAssets
:
- 将生成过程中收集有关静态 Web 资产的信息与运行时库集成,该库处理此信息以优化向浏览器提供的文件。
- 用于优化应用中的静态资产交付的路由终结点约定。 它旨在处理所有 UI 框架,包括 Blazor、Razor、Pages 和 MVC。
MapStaticAssets
与 UseStaticFiles
MapStaticAssets 在 .NET 9 或更高版本的 ASP.NET Core 中提供。 UseStaticFiles 必须在 .NET 9 之前的版本中使用。
UseStaticFiles
提供静态文件,但它没有达到 MapStaticAssets
相同的优化级别。
MapStaticAssets
进行了优化,以提供应用在运行时了解的资产。 如果应用服务来自其他位置(如磁盘或嵌入资源)的资产,则应使用 UseStaticFiles
。
映射静态资产提供在调用 UseStaticFiles
时不可用的以下优点:
- 在应用中,对所有资产进行构建时压缩,包括 JavaScript(JS)和样式表,但不包括已压缩的图像和字体资产。
Gzip (
Content-Encoding: gz
) 压缩在开发期间使用。 发布期间使用附带 Brotli (Content-Encoding: br
) 压缩的 Gzip。 - 使用每个文件的内容的 SHA-256 哈希的 Base64 编码字符串在生成时为所有资产创建指纹。 这可以防止重用旧版本的文件,即使缓存了旧文件。 指纹资产是使用
immutable
指令缓存的,这会导致浏览器在更改之前永远不会再次请求资产。 对于不支持该指令的immutable
浏览器,将添加一个max-age
指令 。 - 在 Visual Studio 热重载开发测试期间:
- 从资产中删除完整性信息,以避免在应用运行时更改文件时出现问题。
- 静态资产不会缓存,以确保浏览器始终检索当前内容。
映射静态资产不提供用于缩小或其他文件转换的功能。 缩小通常由自定义代码或 第三方工具处理。
使用 UseStaticFiles
支持以下功能,但使用 MapStaticAssets
不支持这些功能:
- 从磁盘或嵌入资源或其他位置提供文件
- 在 Web 根目录外提供文件服务
- 设置 HTTP 响应标头
- Directory browsing
- 提供默认文档
FileExtensionContentTypeProvider
- 从多个位置提供文件
在 Web 根目录中提供文件
在应用的 Program
文件中, WebApplication.CreateBuilder 将 内容根 目录设置为当前目录。 调用 MapStaticAssets 方法以启用静态文件服务。 无参数重载导致从应用的 Web 根目录提供文件。 默认的 Web 根目录是 {CONTENT ROOT}/wwwroot
,其中 {CONTENT ROOT}
占位符是内容根目录。
var builder = WebApplication.CreateBuilder(args);
...
app.MapStaticAssets();
可以使用该方法更改 Web 根 UseWebRoot 。 有关详细信息,请参阅 ASP.NET 核心基础知识概述文章的内容根和 Web 根部分。
静态文件可通过相对于 Web 根的路径进行访问。 例如, Blazor Web App 项目模板包含 lib
文件夹中的文件夹 wwwroot
,其中包含 Bootstrap 静态资产。
如果应用将其图像放置在images
文件夹中的 wwwroot
,则以下标记会引用wwwroot/images/favicon.png
:
<link rel="icon" type="image/png" href="images/favicon.png" />
在 Razor Pages 和 MVC 应用中(但不是 Blazor 应用),平铺字符 ~
指向 Web 根。 在以下示例中,~/images/icon.jpg
从应用的icon.jpg
文件夹中加载图标图像(wwwroot/images
):
<img src="~/images/icon.jpg" alt="Icon image" />
前面的示例的 URL 格式为 https://{HOST}/images/{FILE NAME}
。 占位符 {HOST}
是主机,占位符 {FILE NAME}
是文件名。 运行在应用的 localhost 地址和端口 5001 上的上述示例中,绝对 URL 为 https://localhost:5001/images/icon.jpg
。
在网站根目录之外提供文件
请考虑以下目录层次结构,其中静态文件驻留在名为 的文件夹MyStaticFiles
的 Web 根目录之外:
wwwroot
css
images
js
MyStaticFiles
images
red-rose.jpg
按如下方式配置静态文件中间件后,请求可访问 red-rose.jpg
文件:
using Microsoft.Extensions.FileProviders;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles(); //Serve files from wwwroot
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
RequestPath = "/StaticFiles"
});
app.UseAuthorization();
app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();
app.Run();
在前面的代码中, MyStaticFiles
目录层次结构通过 StaticFiles
URL 段公开。 对 https://{HOST}/StaticFiles/images/red-rose.jpg
的请求(其中 {HOST}
占位符是主机)为 red-rose.jpg
文件提供服务。
以下标记引用 MyStaticFiles/images/red-rose.jpg
:
<img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />
若要从多个位置提供文件,请参阅从多个位置提供文件。
设置 HTTP 响应标头
StaticFileOptions 对象可用于设置 HTTP 响应标头。 除了将中间件配置为从 Web 根目录提供静态文件外,以下代码还设置 Cache-Control
标头:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
var cacheMaxAgeOneWeek = (60 * 60 * 24 * 7).ToString();
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.Append(
"Cache-Control", $"public, max-age={cacheMaxAgeOneWeek}");
}
});
app.UseAuthorization();
app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();
app.Run();
上面的代码使静态文件在本地缓存中公开提供一周。
静态文件授权
ASP.NET Core 模板在调用 MapStaticAssets 之前调用 UseAuthorization。 大多数应用都遵循此模式。 在授权中间件之前调用 MapStaticAssets
时:
- 不会对静态文件执行任何授权检查。
- 由静态文件中间件提供的静态文件(例如
wwwroot
下的文件)可公开访问。
若要根据授权提供静态文件,请参阅 静态文件授权。
从多个位置提供文件
请考虑以下显示 Razor 文件的 /MyStaticFiles/image3.png
页面:
@page
<p>Test /MyStaticFiles/image3.png</p>
<img src="~/image3.png" class="img" asp-append-version="true" alt="Test">
UseStaticFiles
和 UseFileServer
默认为指向 wwwroot
的文件提供程序。 可使用其他文件提供程序提供 UseStaticFiles
和 UseFileServer
的其他实例,从多个位置提供文件。 以下示例调用 UseStaticFiles
两次以提供来自 wwwroot
和 MyStaticFiles
的文件:
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"))
});
使用上述代码:
- 将显示
/MyStaticFiles/image3.png
文件。 -
图像标记帮助程序 (AppendVersion)未应用,因为标记帮助程序依赖于 WebRootFileProvider。
WebRootFileProvider
尚未更新以包含MyStaticFiles
该文件夹。
以下代码会更新 WebRootFileProvider
,使图像标记帮助程序能够提供版本:
var webRootProvider = new PhysicalFileProvider(builder.Environment.WebRootPath);
var newPathProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"));
var compositeProvider = new CompositeFileProvider(webRootProvider, newPathProvider);
// Update the default provider.
app.Environment.WebRootFileProvider = compositeProvider;
app.MapStaticAssets();
Note
上述方法适用于 Razor Pages 和 MVC 应用。 有关适用于 Blazor Web App 的指南,请参阅 ASP.NET Core Blazor 静态文件。
通过更新 IWebHostEnvironment.WebRootPath
来提供 wwwroot 外部文件的访问
当 IWebHostEnvironment.WebRootPath 设置为 wwwroot
以外的文件夹时:
- 在开发环境中,在
wwwroot
和更新的IWebHostEnvironment.WebRootPath
中发现的静态资产由wwwroot
提供。 - 在开发环境以外的任何环境中,重复的静态资产会从更新的
IWebHostEnvironment.WebRootPath
文件夹提供。
请考虑使用空 Web 模板创建的 Web 应用:
在
Index.html
和wwwroot
中包含一个wwwroot-custom
文件。使用以下设置了
Program.cs
的已更新WebRootPath = "wwwroot-custom"
文件:var builder = WebApplication.CreateBuilder(new WebApplicationOptions { Args = args, // Look for static files in "wwwroot-custom" WebRootPath = "wwwroot-custom" }); var app = builder.Build(); app.UseDefaultFiles(); app.MapStaticAssets(); app.Run();
在前面的代码中,请求 /
:
- 在开发环境中,返回
wwwroot/Index.html
。 - 在开发以外的任何环境中,返回
wwwroot-custom/Index.html
。
若要确保返回 wwwroot-custom
中的资产,请使用以下方法之一:
在
wwwroot
中删除重复命名的资产。将
"ASPNETCORE_ENVIRONMENT"
中的Properties/launchSettings.json
设置为"Development"
以外的任何值。通过在项目文件中设置
<StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>
,完全禁用静态 Web 资产。 警告,禁用静态 Web 资产会禁用 Razor 类库。将以下 XML 添加到项目文件:
<ItemGroup> <Content Remove="wwwroot\**" /> </ItemGroup>
以下代码更新 IWebHostEnvironment.WebRootPath
为非开发值(暂存),保证从 wwwroot-custom
中返回重复内容,而不是 wwwroot
:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Examine Hosting environment: logging value
EnvironmentName = Environments.Staging,
WebRootPath = "wwwroot-custom"
});
var app = builder.Build();
app.Logger.LogInformation("ASPNETCORE_ENVIRONMENT: {env}",
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
app.Logger.LogInformation("app.Environment.IsDevelopment(): {env}",
app.Environment.IsDevelopment().ToString());
app.UseDefaultFiles();
app.MapStaticAssets();
app.Run();
在本地开发服务器端 Blazor 应用和测试时,请参阅 ASP.NET Core Blazor 静态文件。