Note
此版本不是本文的最新版本。 有关当前版本,请参阅 本文的 .NET 10 版本。
Warning
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅 本文的 .NET 10 版本。
了解如何使用 Map Static Assets 终结点约定或静态文件中间件在 ASP.NET Core应用中提供、保护和优化静态文件。 静态文件(也称为静态资产)不会动态生成,直接提供给客户端,包括 HTML、CSS、图像和 JavaScript。
有关添加或取代本文中指导的 Blazor 静态文件指导,请参阅 ASP.NET Core Blazor 静态文件。
若要在 ASP.NET Core 中启用静态文件处理,请调用 MapStaticAssets。
默认情况下,将静态文件存储在项目的 Web 根 目录中。 默认目录是 {CONTENT ROOT}/wwwroot,其中 {CONTENT ROOT} 占位符是应用的 内容根目录。 只有 wwwroot 文件夹中的文件才可被寻址,因此你无需担心其余代码。
只有具有特定文件扩展名并映射到受支持媒体类型的文件,才能作为静态 Web 资产。
在构建时发现静态 Web 资产,并通过内容指纹进行优化,以防止重复使用旧文件。 资产也会 压缩 ,以减少资产交付时间。
在运行时,所发现的静态 Web 资源将作为终结点进行公开,并应用了 HTTP 标头,例如 缓存头 和内容类型头。 资源仅会被加载一次,直到文件发生更改或浏览器清空其缓存为止。 设置ETag、Last-Modified和Content-Type标头。 更新应用后,将阻止浏览器使用过时资产。
静态资产的交付基于 终结点路由,因此它适用于其他终结点感知功能,例如授权。 它旨在处理所有 UI 框架,包括 Blazor、Razor、Pages 和 MVC。
映射静态资产具有以下优势:
- 在应用中,对所有资产进行构建时压缩,包括 JavaScript(JS)和样式表,但不包括已压缩的图像和字体资产。
Gzip (
Content-Encoding: gz) 压缩在开发期间使用。 Gzip 和 Brotli (Content-Encoding: br) 压缩在发布期间都使用。 - 使用每个文件的内容的 SHA-256 哈希的 Base64 编码字符串在生成时为所有资产创建指纹。 这可以防止重用旧版本的文件,即使缓存了旧文件。 指纹资产是使用
immutable指令缓存的,这会导致浏览器在更改之前永远不会再次请求资产。 对于不支持该指令的immutable浏览器,将添加一个max-age指令 。
映射静态资产不提供用于缩小或其他文件转换的功能。 缩小通常由自定义代码或 第三方工具处理。
Note
MapStaticAssets 本身不提供 默认文档。 若要提供默认文档,请先调用 UseDefaultFiles,然后调用 UseStaticFiles。 有关详细信息,请参阅 “服务默认文档 ”部分。
若要在 ASP.NET Core 中启用静态文件处理,请调用 UseStaticFiles。
默认情况下,将静态文件存储在项目的 Web 根 目录中。 默认目录是 {CONTENT ROOT}/wwwroot,其中 {CONTENT ROOT} 占位符是应用的 内容根目录。 只有 wwwroot 文件夹中的文件才可被寻址,因此你无需担心其余代码。
在运行时,当请求中包含资产修改和内容类型头时,静态文件中间件会返回静态 Web 资源。 设置ETag、Last-Modified和Content-Type标头。
静态文件中间件可提供静态文件服务,当在应用的请求处理管道中调用 UseStaticFiles 时,应用会使用该中间件。 文件从指定IWebHostEnvironment.WebRootPathWebRootFileProvider路径提供,通常默认为 Web 根文件夹wwwroot。
还可以从 引用的项目和包中提供静态网站资源。
更改 Web 根目录
若要更改 Web 根目录,请使用 UseWebRoot 该方法。 有关详细信息,请参阅 ASP.NET 核心基础知识概述。
通过在项目文件中使用wwwroot来阻止发布<Content>中的文件。 以下示例阻止在 wwwroot/local 及其子目录中发布内容:
<ItemGroup>
<Content Update="wwwroot\local\**\*.*" CopyToPublishDirectory="Never" />
</ItemGroup>
采用 CreateBuilder 方法可将内容根目录设置为当前目录:
var builder = WebApplication.CreateBuilder(args);
采用 CreateDefaultBuilder 方法可将内容根目录设置为当前目录:
Host.CreateDefaultBuilder(args)
在请求处理管道中,在调用 UseHttpsRedirection后,调用 MapStaticAssets 以启用从应用的 Web 根提供静态文件:
app.MapStaticAssets();
在请求处理管道中,在调用 UseHttpsRedirection后,调用 UseStaticFiles 以启用从应用的 Web 根提供静态文件:
app.UseStaticFiles();
可通过 Web 根目录的相关路径访问静态文件。
若要访问指定位置的图像,请执行以下步骤 wwwroot/images/favicon.png:
- URL 格式:
https://{HOST}/images/{FILE NAME}- 占位符号
{HOST}是主机。 - 占位符
{FILE NAME}是文件名。
- 占位符号
- 示例
- 绝对 URL:
https://localhost:5001/images/favicon.png - 根目录相对 URL:
images/favicon.png
- 绝对 URL:
在Blazor应用中,images/favicon.png从应用的favicon.png文件夹加载图标图像(wwwroot/images)。
<link rel="icon" type="image/png" href="images/favicon.png" />
在 Razor Pages 和 MVC 应用中,平铺字符 ~ 指向 Web 根。 在以下示例中,~/images/favicon.png从应用的favicon.png文件夹中加载图标图像(wwwroot/images):
<link rel="icon" type="image/png" href="~/images/favicon.png" />
对中间件管道进行短路
为避免在匹配到静态资源后执行整个中间件管道(这是 UseStaticFiles 的行为),请在 MapStaticAssets 上调用 ShortCircuit。 调用 ShortCircuit 会立即执行终结点并返回响应,从而阻止其他中间件执行静态资产请求:
app.MapStaticAssets().ShortCircuit();
在开发过程中控制静态文件缓存
在 Development 环境中运行时(例如在 Visual Studio 热重载 开发测试期间),框架会重写缓存标头,以防止浏览器缓存静态文件。 此行为有助于确保在文件更改时使用最新版本的文件,避免过时内容出现问题。 在生产环境中,框架设置正确的缓存标头,以便浏览器可以按预期缓存静态资产。
若要禁用此行为,请在EnableStaticAssetsDevelopmentCaching环境的应用设置文件(true)中将Development设置为appsettings.Development.json。
非Development 环境中的静态文件
在本地运行应用时,环境 Development 是唯一启用静态 Web 资产的环境。 要在本地开发和测试期间为 Development 以外的环境(例如在 Staging 环境中)启用静态文件,请在 WebApplicationBuilder 上调用 UseStaticWebAssets。
Warning
调用UseStaticWebAssets以获取特定的环境,避免在生产环境中激活此功能,因为它会从磁盘上的其他位置提供文件,而不是从项目目录中的位置。 本节中的示例使用 IsStaging 来检查是否处于 Staging 环境中。
if (builder.Environment.IsStaging())
{
builder.WebHost.UseStaticWebAssets();
}
通过 IWebHostEnvironment.WebRootPath 在 Web 根目录之外提供文件
当您将 IWebHostEnvironment.WebRootPath 设置为不同于 wwwroot 的文件夹时,应用会具有以下默认行为:
- 在
Development环境中,如果wwwroot和分配给 WebRootPath 的另一个文件夹中都存在同名的静态资源,则这些资源将从wwwroot提供。 - 在除
Development以外的任何环境中,重复的静态资源均从 WebRootPath 文件夹提供。
请考虑从空 Web 模板创建的 Web 应用:
- 在
Index.html和wwwroot中包含一个wwwroot-custom文件。 -
Program文件已更新,以设置WebRootPath = "wwwroot-custom"。
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
WebRootPath = "wwwroot-custom"
});
默认情况下,对于请求 /:
- 在
Development环境中,wwwroot/Index.html返回。 - 在任何非
Development的环境中,wwwroot-custom/Index.html被返回。
使用以下方法 wwwroot-custom ,来确保始终返回来自的资产:
删除
wwwroot中名称重复的资源。将
ASPNETCORE_ENVIRONMENT中的Properties/launchSettings.json设置为Development以外的任何值。在应用的项目文件中,通过将
<StaticWebAssetsEnabled>设置为false来禁用静态 Web 资源。 警告: 禁用静态 Web 资产会 Razor 禁用类库。将以下 XML 添加到项目文件:
<ItemGroup> <Content Remove="wwwroot\**" /> </ItemGroup>
以下代码更新 WebRootPath 为非开发值(Staging),保证从 wwwroot-custom 中返回重复内容,而不是 wwwroot:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
EnvironmentName = Environments.Staging,
WebRootPath = "wwwroot-custom"
});
静态文件中间件
静态文件中间件支持在特定静态文件场景中提供静态文件,通常作为 Map Static Assets 终结点路由约定(MapStaticAssets)的补充。
当你在应用的请求处理管道中调用 UseStaticFiles 时,将静态文件中间件纳入请求处理流程,通常是在添加 Map Static Assets 终结点约定(MapStaticAssets)之后。
在面向 .NET 9 或更高版本的应用中,使用 Map Static Assets 端点约定。 在面向 .NET 9 之前的.NET版本的应用中使用静态文件中间件。
静态文件中间件用于提供静态文件,但它无法提供与 Map Static Assets 终结点约定相同级别的优化。 如果仅依赖静态文件中间件,则无法使用 Map Static Assets 终结点约定提供的构建时压缩和指纹功能。
终结点约定已针对为应用在运行时已知的资源提供服务进行了优化。 如果应用提供来自其他位置(如磁盘或嵌入式资源)的资产,请使用静态文件中间件。
本文所述的以下功能受静态文件中间件支持,但不受 Map Static Assets 终结点约定支持:
通过 UseStaticFiles 在 Web 根目录之外提供文件
请考虑以下目录结构,其中静态文件位于一个名为的文件夹中,而不是应用程序的ExtraStaticFiles下:
wwwrootcssimagesjs
ExtraStaticFilesimagesred-rose.jpg
通过配置一个新的静态文件中间件实例,请求可访问 red-rose.jpg:
以下 API 的命名空间:
using Microsoft.Extensions.FileProviders;
在请求处理管道中,在调用 MapStaticAssets(.NET 9 或更高版本)或 UseStaticFiles(.NET 8 或更早版本)之后:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles")),
RequestPath = "/static-files"
});
在前面的代码中, ExtraStaticFiles 目录层次结构可通过 static-files URL 段公开访问。 对 https://{HOST}/StaticFiles/images/red-rose.jpg 的请求(其中 {HOST} 占位符是主机)为 red-rose.jpg 文件提供服务。
以下标记引用 ExtraStaticFiles/images/red-rose.jpg:
<img src="static-files/images/red-rose.jpg" alt="A red rose" />
对于前面的示例, Razor Pages 和 MVC 视图支持平铺斜杠表示法(src="~/StaticFiles/images/red-rose.jpg"),但 Razor 应用中的 Blazor 组件不支持此表示法。
从多个位置提供文件
本部分中的指南适用于 Razor Pages 和 MVC 应用。 有关适用于 Blazor Web App 的指南,请参阅 ASP.NET Core Blazor 静态文件。
请考虑显示公司徽标的以下标记:
<img src="~/logo.png" asp-append-version="true" alt="Company logo">
开发人员打算使用 映像标记帮助程序 来追加版本,并从自定义位置(名为 ExtraStaticFiles文件夹)提供文件。
以下示例调用 MapStaticAssets 来提供来自 wwwroot 的文件,并调用 UseStaticFiles 来提供来自 ExtraStaticFiles 的文件:
在请求处理管道中,在调用 MapStaticAssets(.NET 9 或更高版本)或 UseStaticFiles(.NET 8 或更早版本)之后:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles"))
});
以下示例调用UseStaticFiles两次,以分别从wwwroot 和 ExtraStaticFiles 提供文件。
在请求处理管道中,在现有对 UseStaticFiles 的调用之后:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles"))
});
使用前面的代码来显示文件ExtraStaticFiles/logo.png。 但是,图像标记助手(AppendVersion)未应用,因为标记帮助程序依赖于WebRootFileProvider,而WebRootFileProvider尚未更新以包含文件夹。
以下代码使用 WebRootFileProvider 将 ExtraStaticFiles 更新以包含 CompositeFileProvider 文件夹。 这使图像标签帮助程序能够将版本应用于ExtraStaticFiles文件夹中的图像。
以下 API 的命名空间:
using Microsoft.Extensions.FileProviders;
在请求处理管道中,在调用 MapStaticAssets(.NET 9 或更高版本)或 UseStaticFiles(.NET 8 或更早版本)之前:
var webRootProvider = new PhysicalFileProvider(builder.Environment.WebRootPath);
var newPathProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles"));
var compositeProvider = new CompositeFileProvider(webRootProvider, newPathProvider);
app.Environment.WebRootFileProvider = compositeProvider;
UseStaticFiles 和 UseFileServer 默认为指向 wwwroot 的文件提供程序。 你可以结合其他文件提供程序提供 UseStaticFiles 和 UseFileServer 的其他实例,以从其他位置提供文件。 有关详细信息,请参阅在使用 UseFileServer 配置 wwwroot 时仍需使用 UseStaticFiles (dotnet/AspNetCore.Docs #15578)。
设置 HTTP 响应标头
使用 StaticFileOptions 设置 HTTP 响应标头。 除了将静态文件中间件配置为提供静态文件外,以下代码将标头设置为 Cache-Control 604,800 秒(一周)。
以下 API 的命名空间:
using Microsoft.AspNetCore.Http;
在请求处理管道中,在调用 MapStaticAssets(.NET 9 或更高版本)或 UseStaticFiles(.NET 8 或更早版本)之后:
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.Append(
"Cache-Control", "public, max-age=604800");
}
});
大量资产集合
处理大量资产(大约 1,000 个或更多)时,请使用打包工具来减少应用最终提供的资产数量,或将 MapStaticAssets 与 UseStaticFiles 合并。
MapStaticAssets 及时地加载在资源生成过程中捕获的预先计算的元数据,以支持压缩、缓存和指纹。 这些功能的成本是应用占用更大的内存。 对于频繁访问的资产,通常值得花费。 对于不经常访问的资产,权衡后可能不值得去承担这些成本。
如果不使用捆绑功能,请将 MapStaticAssets 与 UseStaticFiles 结合使用。 以下示例演示了该方法。
在项目文件 (.csproj) 中,使用 StaticWebAssetEndpointExclusionPattern MSBuild 属性来筛选最终清单中针对 MapStaticAssets 的终结点。 被排除的文件由 UseStaticFiles 提供,不受益于压缩、缓存和指纹识别。
若要保留框架的默认排除模式,在设置值StaticWebAssetEndpointExclusionPattern时保留 $(StaticWebAssetEndpointExclusionPattern) 。 在分号分隔的列表中添加更多模式。
在以下示例中,排除模式将添加文件夹中的静态文件 lib/icons ,这表示一批假设的图标:
<StaticWebAssetEndpointExclusionPattern>
$(StaticWebAssetEndpointExclusionPattern);lib/icons/**
</StaticWebAssetEndpointExclusionPattern>
在 Program 文件中完成 HTTPS 重定向中间件 (app.UseHttpsRedirection();) 的处理后:
- 调用 UseStaticFiles 以处理排除的文件(
lib/icons/**)和未涵盖 MapStaticAssets的任何其他文件。 - 在 UseStaticFiles 处理关键应用程序文件(CSS、JS、图片)后,调用 MapStaticAssets。
app.UseStaticFiles();
app.UseAuthorization();
app.MapStaticAssets();
静态资产清单
MapStaticAssets 根据 静态资源清单 提供资源,而不是在运行时扫描 Web 根目录。 清单在构建和发布时生成,用于记录为应用发现的静态 Web 资源,以及内容指纹、Content-Type 标头、缓存标头和预先计算的压缩版本(Gzip 和 Brotli)等元数据。 在运行时, MapStaticAssets 读取清单,为每个资产注册终结点,并提供优化的响应。
构建过程会在构建输出目录中生成清单。 其文件名基于项目的程序集名称(例如,{ASSEMBLY NAME}.staticwebassets.endpoints.json,其中占位符 {ASSEMBLY NAME} 为应用的 MSBuild AssemblyName 值)。 若要从其他位置提供清单,请参阅“ 提供自定义静态文件清单 ”部分。
由于 MapStaticAssets 仅提供清单中列出的资产,因此它不为不属于清单的文件提供服务。 文件不是清单的一部分,因为它们是:
- 位于构建时的 Web 根目录之外,例如从磁盘提供的文件、嵌入式资源,或在运行时设置的自定义 WebRootPath。
- 从带有
StaticWebAssetEndpointExclusionPatternMSBuild 属性的清单中排除(请参阅 “资产的大型集合 ”部分)。
若要提供不在清单中的文件,请调用 UseStaticFiles,它直接在运行时从 Web 根目录提供文件。 这也是为什么使用MapStaticAssets提供默认文档时需要调用UseStaticFiles。
将构建生成的文件集成到静态 Web 资源中
生成工具(如 TypeScript 编译器和 JavaScript 捆绑程序)通常会在生成过程中生成文件。 若要为这些生成的文件提供指纹、压缩和缓存, MapStaticAssets 必须在生成过程中将文件发现为静态 Web 资产。 生成的文件通常保存在 Web 根 (wwwroot)之外,并被从源代码管理中排除,因此默认情况下不会将其发现为静态 Web 资产。 只有在构建期间解析静态 Web 资源时被解析为位于 wwwroot 下的文件,才会被添加到 静态资源清单中,并由 MapStaticAssets 提供服务。 链接到wwwroot中的文件(例如,通过wwwroot项和<Content>)也会包含在内,即使源文件存储在Link之外。
若要将构建生成的文件作为静态 Web 资产包含在内,请使用以下任一方法。
将生成的文件链接到 Web 根目录
添加一个带有Link的<Content>项,它会将每个生成的文件放在wwwroot下。 链接到 wwwroot 的文件会被静态 Web 资产管道发现,并由 MapStaticAssets 提供服务,同时支持指纹处理、压缩和缓存,即使源文件存储在 wwwroot 外部。
在以下示例中,某个构建步骤会在 main.js 文件夹中生成 generated,而 <Content> 项会将该文件链接到 wwwroot:
<ItemGroup>
<Content Include="generated\main.js" Link="wwwroot\main.js"
CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
生成过程解析静态 Web 资产时,生成的文件必须存在。
使用 JavaScript 项目来处理复杂的构建管道
对于复杂的 JavaScript 或 TypeScript 客户端生成,请使用单独的 JavaScript 项目,该项目使用 JavaScript 项目系统 ( Microsoft.VisualStudio.JavaScript.Sdk MSBuild SDK 和 .esproj 项目文件)生成客户端资产。 在 ASP.NET Core 应用中引用 JavaScript 项目,以便将其输出作为静态 Web 资产使用。 有关示例,请参阅Microsoft.FluentUI.AspNetCore.Components.Assets.esproj项目文件(microsoft/fluentui-blazorGitHub存储库)。
静态文件授权
当应用采用 回退授权策略时,它需要对所有未显式指定授权策略的请求进行授权。 此要求包括授权中间件处理请求后对静态文件的请求。 若要允许对静态文件进行匿名访问,请将 AllowAnonymousAttribute 应用于静态文件的终结点生成器:
app.MapStaticAssets().Add(endpointBuilder =>
endpointBuilder.Metadata.Add(new AllowAnonymousAttribute()));
当应用采用 回退授权策略时,所有未显式指定授权策略的请求(包括授权中间件处理请求后对静态文件的请求)都需要授权。 ASP.NET Core 模板允许通过在调用UseStaticFiles之前调用UseAuthorization来匿名访问静态文件。 大多数应用都遵循此模式。 在授权中间件之前调用静态文件中间件时:
- 不会对静态文件执行任何授权检查。
- 由静态文件中间件提供的静态文件(例如 Web 根目录中通常位于
wwwroot的文件)可被公开访问。
根据授权提供静态文件:
- 确认应用设置 回退授权策略 以要求经过身份验证的用户。
- 将静态文件存储在应用的 Web 根目录之外。
- 调用 UseAuthorization后,调用 UseStaticFiles,指定 Web 根外部静态文件文件夹的路径。
以下 API 的命名空间:
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.FileProviders;
服务注册:
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
在调用 UseAuthorization后的请求处理管道中:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "SecureStaticFiles")),
RequestPath = "/static-files"
});
以下 API 的命名空间:
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.FileProviders;
在 Startup.ConfigureServices中:
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
在 Startup.Configure 中,在调用 UseAuthorization 之后:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "SecureStaticFiles")),
RequestPath = "/static-files"
});
在前面的代码中,回退授权策略需要经过身份验证的用户。 指定其自己的授权要求的终结点(如控制器和 Razor 页面)不使用回退授权策略。 例如,具有 Razor 或 [AllowAnonymous] 的 [Authorize(PolicyName="MyPolicy")] Pages、控制器或操作方法使用应用的授权属性,而不是回退授权策略。
RequireAuthenticatedUser 将 DenyAnonymousAuthorizationRequirement 添加到当前实例,这将强制对当前用户进行身份验证。
存储在应用的 Web 根目录中的静态资产是可公开访问的,因为之前UseStaticFiles调用默认静态文件中间件(UseAuthorization)。 文件夹中的 SecureStaticFiles 静态资产需要身份验证。
还有一种根据授权提供文件的方法是:
- 将文件存储在 Web 根目录以及任何可由静态文件中间件访问的目录之外。
- 通过一个已应用授权的动作方法提供文件,并返回一个 FileResult 对象。
从 Razor 页 (Pages/BannerImage.cshtml.cs):
public class BannerImageModel : PageModel
{
private readonly IWebHostEnvironment _env;
public BannerImageModel(IWebHostEnvironment env) => _env = env;
public PhysicalFileResult OnGet()
{
var filePath = Path.Combine(
_env.ContentRootPath, "SecureStaticFiles", "images", "red-rose.jpg");
return PhysicalFile(filePath, "image/jpeg");
}
}
来自控制器(Controllers/HomeController.cs):
[Authorize]
public IActionResult BannerImage()
{
var filePath = Path.Combine(
_env.ContentRootPath, "SecureStaticFiles", "images", "red-rose.jpg");
return PhysicalFile(filePath, "image/jpeg");
}
上述方法要求对每个文件使用一个页面或终结点。
以下路由终结点示例返回经过身份验证的用户的文件。
在 Program 文件中:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AuthenticatedUsers", b => b.RequireAuthenticatedUser());
});
...
app.MapGet("/files/{fileName}", IResult (string fileName) =>
{
var filePath = GetOrCreateFilePath(fileName);
if (File.Exists(filePath))
{
return TypedResults.PhysicalFile(filePath, fileName);
}
return TypedResults.NotFound("No file found with the supplied file name");
})
.WithName("GetFileByName")
.RequireAuthorization("AuthenticatedUsers");
以下路由终结点示例用于为具有管理员角色的已认证用户 (admin) 上传文件。
在 Program 文件中:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminsOnly", b => b.RequireRole("admin"));
});
...
// IFormFile uses memory buffer for uploading. For handling large
// files, use streaming instead. See the *File uploads* article
// in the ASP.NET Core documentation:
// https://learn.microsoft.com/aspnet/core/mvc/models/file-uploads
app.MapPost("/files", async (IFormFile file, LinkGenerator linker,
HttpContext context) =>
{
// Don't rely on the value in 'file.FileName', as it's only metadata that can
// be manipulated by the end-user. Consider the 'Utilities.IsFileValid' method
// that takes an 'IFormFile' and validates its signature within the
// 'AllowedFileSignatures'.
var fileSaveName = Guid.NewGuid().ToString("N") +
Path.GetExtension(file.FileName);
await SaveFileWithCustomFileName(file, fileSaveName);
context.Response.Headers.Append("Location", linker.GetPathByName(context,
"GetFileByName", new { fileName = fileSaveName}));
return TypedResults.Ok("File Uploaded Successfully!");
})
.RequireAuthorization("AdminsOnly");
在 Startup.ConfigureServices中:
services.AddAuthorization(options =>
{
options.AddPolicy("AuthenticatedUsers", b => b.RequireAuthenticatedUser());
});
在 Startup.Configure中:
app.MapGet("/files/{fileName}", IResult (string fileName) =>
{
var filePath = GetOrCreateFilePath(fileName);
if (File.Exists(filePath))
{
return TypedResults.PhysicalFile(filePath, fileName);
}
return TypedResults.NotFound("No file found with the supplied file name");
})
.WithName("GetFileByName")
.RequireAuthorization("AuthenticatedUsers");
以下代码为管理员角色admin()中经过身份验证的用户上传文件。
在 Startup.ConfigureServices中:
services.AddAuthorization(options =>
{
options.AddPolicy("AdminsOnly", b => b.RequireRole("admin"));
});
在 Startup.Configure中:
// IFormFile uses memory buffer for uploading. For handling large
// files, use streaming instead. See the *File uploads* article
// in the ASP.NET Core documentation:
// https://learn.microsoft.com/aspnet/core/mvc/models/file-uploads
app.MapPost("/files", async (IFormFile file, LinkGenerator linker,
HttpContext context) =>
{
// Don't rely on the value in 'file.FileName', as it's only metadata that can
// be manipulated by the end-user. Consider the 'Utilities.IsFileValid' method
// that takes an 'IFormFile' and validates its signature within the
// 'AllowedFileSignatures'.
var fileSaveName = Guid.NewGuid().ToString("N") +
Path.GetExtension(file.FileName);
await SaveFileWithCustomFileName(file, fileSaveName);
context.Response.Headers.Append("Location", linker.GetPathByName(context,
"GetFileByName", new { fileName = fileSaveName}));
return TypedResults.Ok("File Uploaded Successfully!");
})
.RequireAuthorization("AdminsOnly");
目录浏览
目录浏览允许在指定目录中列出目录。
出于安全原因,默认情况下禁用目录浏览。 有关详细信息,请参阅静态文件的安全注意事项。
使用以下 API 启用目录浏览:
在下面的示例中:
-
images应用根目录中的文件夹保存用于目录浏览的图像。 - 浏览图像的请求路径为
/DirectoryImages。 - 调用UseStaticFiles和设置FileProviderStaticFileOptions可显示指向各个文件的浏览器链接。
以下 API 的命名空间:
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
服务注册:
builder.Services.AddDirectoryBrowser();
在请求处理管道中,在调用 MapStaticAssets(.NET 9 或更高版本)或 UseStaticFiles(.NET 8 或更早版本)之后:
var fileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.WebRootPath, "images"));
var requestPath = "/DirectoryImages";
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = requestPath
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = fileProvider,
RequestPath = requestPath
});
以下 API 的命名空间:
using Microsoft.Extensions.FileProviders;
using System.IO;
在 Startup.ConfigureServices中:
services.AddDirectoryBrowser();
在 Startup.Configure 中,在现有的对 UseStaticFiles 的调用之后:
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.WebRootPath, "images")),
RequestPath = "/DirectoryImages"
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.WebRootPath, "images")),
RequestPath = "/DirectoryImages"
});
前面的代码允许通过 URL wwwroot/images 浏览 https://{HOST}/DirectoryImages 文件夹的目录,并提供每个文件和文件夹的链接,其中 {HOST} 占位符表示主机。
AddDirectoryBrowser 添加目录浏览中间件所需的服务,包括 HtmlEncoder。 这些服务可能会由其他调用添加,例如 AddRazorPages,但请调用 AddDirectoryBrowser 以确保这些服务已被添加。
提供默认文档
设置默认页面为访问者提供网站的起点。 若要从 wwwroot 中提供默认文件而不要求请求 URL 包含文件名,请调用该方法 UseDefaultFiles 。
UseDefaultFiles 是一个不提供文件服务的 URL 重写工具。 它将请求 URL 重写到默认文档(例如 / ,指向 /index.html),另一个组件为该文件提供服务。
由于 MapStaticAssets 通过终结点路由提供在生成时发现的资产,因此它本身不会提供默认文档。 调用 UseDefaultFiles 以重写请求,然后 UseStaticFiles 为默认文档提供重写请求:
app.UseDefaultFiles();
app.UseStaticFiles();
app.MapStaticAssets();
Important
仅配置了 UseDefaultFiles 和 MapStaticAssets(未配置 UseStaticFiles)时,对 / 的请求会返回 404 - 未找到 响应。 之所以会出现这种行为,是因为最小托管模型会在请求处理管道的开头添加路由中间件,因此终结点路由会在 UseDefaultFiles 将请求重写为默认文档之前先匹配该请求。 将 Web 根更改为自定义路径WebRootPath时,问题尤其明显,因为自定义 Web 根中的文件不是提供生成时静态资产清单MapStaticAssets的一部分。 如前面的示例所示,在 UseDefaultFiles 之后添加对 UseStaticFiles 的调用,以提供默认文档。
在请求处理管道中,位于现有的 UseStaticFiles 调用之前:
app.UseDefaultFiles();
使用 UseDefaultFiles 请求对 wwwroot 中的文件夹搜索:
default.htmdefault.htmlindex.htmindex.html
如同请求包含了文件名一样,提供从列表中找到的第一个文件。 浏览器 URL 继续反映请求的 URI。
以下代码将默认文件名更改为 default-document.html:
var options = new DefaultFilesOptions();
options.DefaultFileNames.Clear();
options.DefaultFileNames.Add("default-document.html");
app.UseDefaultFiles(options);
合并静态文件、默认文档和目录浏览
UseFileServer 结合了 UseStaticFiles、UseDefaultFiles 和 UseDirectoryBrowser(可选)的功能。
在请求处理管道中,在对现有调用MapStaticAssets(.NET 9 或更高版本)或UseStaticFiles(.NET 8 或更早版本)后,调用UseFileServer以启用静态文件和默认文件的服务:
app.UseFileServer();
前面的示例未启用目录浏览。
以下代码支持提供静态文件、默认文件和目录浏览。
服务注册:
builder.Services.AddDirectoryBrowser();
在请求处理管道中,在现有对 UseStaticFiles 的调用之后:
app.UseFileServer(enableDirectoryBrowsing: true);
在 Startup.ConfigureServices中:
services.AddDirectoryBrowser();
在 Startup.Configure 中,在现有的对 UseStaticFiles 的调用之后:
app.UseFileServer(enableDirectoryBrowsing: true);
对于主机地址(/),在默认UseFileServer页面(Razor)或默认 MVC 视图(Pages/Index.cshtml)之前,返回默认 HTML 文档。
考虑以下目录层次结构:
wwwrootcssimagesjs
ExtraStaticFilesimageslogo.png
default.html
以下代码允许提供静态文件、默认文件和目录浏览 ExtraStaticFiles。
以下 API 的命名空间:
using Microsoft.Extensions.FileProviders;
服务注册:
builder.Services.AddDirectoryBrowser();
在请求处理管道中,在现有对 UseStaticFiles 的调用之后:
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles")),
RequestPath = "/static-files",
EnableDirectoryBrowsing = true
});
以下 API 的命名空间:
using Microsoft.Extensions.FileProviders;
using System.IO;
在 Startup.ConfigureServices中:
services.AddDirectoryBrowser();
在 Startup.Configure 中,在现有的对 UseStaticFiles 的调用之后:
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "ExtraStaticFiles")),
RequestPath = "/static-files",
EnableDirectoryBrowsing = true
});
必须在 AddDirectoryBrowser 属性值为 EnableDirectoryBrowsing 时调用 true。
使用前面的文件层次结构和代码,URL 解析,如下表所示( {HOST} 占位符是主机)。
| URI | 响应文件 |
|---|---|
https://{HOST}/static-files/images/logo.png |
ExtraStaticFiles/images/logo.png |
https://{HOST}/static-files |
ExtraStaticFiles/default.html |
如果目录中不存在默认命名的文件 ExtraStaticFiles , https://{HOST}/static-files 则返回包含可单击链接的目录列表,其中 {HOST} 占位符是主机。
UseDefaultFiles 和 UseDirectoryBrowser 执行从不带尾部 / 的目标 URI 到带尾部 / 的目标 URI 的客户端重定向。 例如,从 https://{HOST}/static-files(末尾没有 /)到 https://{HOST}/static-files/(末尾包含 /)。 除非使用了 DefaultFilesOptions 的 RedirectToAppendTrailingSlash 选项,否则 ExtraStaticFiles 目录内的相对 URL 若未以斜杠结尾 (/) 则无效。
将文件扩展名映射到 MIME 类型
Note
有关适用于 Blazor 应用的指南,请参阅 ASP.NET Core Blazor 静态文件。
使用 FileExtensionContentTypeProvider.Mappings 来添加或修改文件扩展名至 MIME 内容类型的映射。
Note
FileExtensionContentTypeProvider 在并发写入时 不是线程安全的。 其内部映射字典是一个不带同步功能的标准 Dictionary<string, string>。 该提供程序的映射设置旨在在启动时进行一次配置。 如果后续仅执行读取操作(查询),则可以安全地将该提供程序注册为单例。 当提供程序已被并发请求使用后,请勿添加、删除或修改映射关系。
在以下示例中,多个文件扩展名映射到已知的 MIME 类型。 该 .rtf 扩展将被替换,并 .mp4 被删除:
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
...
// Set up custom content types - associating file extension to MIME type
var provider = new FileExtensionContentTypeProvider();
// Add new mappings
provider.Mappings[".myapp"] = "application/x-msdownload";
provider.Mappings[".htm3"] = "text/html";
provider.Mappings[".image"] = "image/png";
// Replace an existing mapping
provider.Mappings[".rtf"] = "application/x-msdownload";
// Remove MP4 videos
provider.Mappings.Remove(".mp4");
app.UseStaticFiles(new StaticFileOptions
{
ContentTypeProvider = provider
});
如果有多个要配置的静态文件选项,也可以使用以下命令设置提供程序 StaticFileOptions:
var provider = new FileExtensionContentTypeProvider();
...
builder.Services.Configure<StaticFileOptions>(options =>
{
options.ContentTypeProvider = provider;
});
app.UseStaticFiles();
在 Startup.Configure中:
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
using System.IO;
...
// Set up custom content types - associating file extension to MIME type
var provider = new FileExtensionContentTypeProvider();
// Add new mappings
provider.Mappings[".myapp"] = "application/x-msdownload";
provider.Mappings[".htm3"] = "text/html";
provider.Mappings[".image"] = "image/png";
// Replace an existing mapping
provider.Mappings[".rtf"] = "application/x-msdownload";
// Remove MP4 videos
provider.Mappings.Remove(".mp4");
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.WebRootPath, "images")),
RequestPath = "/images",
ContentTypeProvider = provider
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.WebRootPath, "images")),
RequestPath = "/images"
});
有关详细信息,请参阅 MIME 内容类型。
非标准内容类型
静态文件中间件可识别近 400 种已知文件内容类型。 如果用户请求具有未知文件类型的文件,静态文件中间件会将请求传递给管道中的下一个中间件。 如果没有中间件处理请求,服务器将返回 404 未找到 的响应。 如果启用了目录浏览,服务器会在目录列表中显示指向文件的链接。
以下代码支持提供未知内容类型,并将未知文件呈现为图像:
app.UseStaticFiles(new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "image/png"
});
使用前面的代码,请求的文件含未知内容类型时,以图像形式返回请求。
Warning
启用 ServeUnknownFileTypes 会形成安全隐患。 它默认处于禁用状态,不建议使用。 将文件扩展名映射到 MIME 类型 可提供更安全的替代方法,用于提供具有非标准扩展名的文件。
提供自定义静态文件清单
staticAssetsManifestPath如果是null,IHostEnvironment.ApplicationName则用于查找清单。 或者,指定清单文件的完整路径。 如果使用相对路径,框架会在AppContext.BaseDirectory中查找该文件。
静态文件的安全注意事项
Warning
UseDirectoryBrowser 和 UseStaticFiles 可能会泄漏机密。 强烈建议在生产中禁用目录浏览。 请仔细查看哪些目录是通过 UseStaticFiles 或 UseDirectoryBrowser 启用的。 整个目录及其子目录均可公开访问。 将适合公开的文件存储在专用目录中,如 <content_root>/wwwroot。 将这些文件与 MVC 视图、Razor Pages 和配置文件等分开。
通过 UseDirectoryBrowser 和 UseStaticFiles 公开的内容的 URL 遵循底层文件系统的大小写敏感性和字符限制。 例如,Windows 不区分大小写,但 macOS 和 Linux 区分大小写。
托管于 IIS 中的 ASP.NET Core 应用使用 ASP.NET Core 模块将所有请求转发到应用,包括静态文件请求。 未使用 IIS 静态文件处理程序,并且不处理请求。
在 IIS Manager 中完成以下步骤,删除服务器或网站级别的 IIS 静态文件处理程序:
- 转到“模块”功能。
- 在列表中选择 StaticFileModule。
- 单击“操作”侧栏中的“删除”。
Warning
如果启用了 IIS 静态文件处理程序且 ASP.NET Core 模块配置不正确,则会提供静态文件。 例如,如果
web.config文件未部署,就会出现这种情况。将代码文件(包括
.cs和.cshtml)放在应用项目的 Web 根目录之外。 此配置在应用的客户端内容与基于服务器的代码之间创建逻辑分隔。 这种分离可防止服务器端代码泄露。
MSBuild 属性
下表显示了静态文件 MSBuild 属性和元数据说明。
| 资产 | Description |
|---|---|
EnableDefaultCompressedItems |
启用默认压缩的包含和排除模式。 |
CompressionIncludePatterns |
用分号分隔的、要纳入压缩的文件模式列表。 |
CompressionExcludePatterns |
要从压缩中排除的文件模式的分号分隔列表。 |
EnableDefaultCompressionFormats |
启用默认压缩格式(Gzip 和 Brotli)。 |
BuildCompressionFormats |
在构建期间使用的压缩格式。 |
PublishCompressionFormats |
发布期间要使用的压缩格式。 |
DisableBuildCompression |
在生成过程中禁用压缩。 |
CompressDiscoveredAssetsDuringBuild |
在构建过程中压缩已发现的资源。 |
BrotliCompressionLevel |
Brotli 算法的压缩级别。 |
StaticWebAssetBuildCompressAllAssets |
压缩构建过程中所有的资产,而不是仅限于构建过程中发现或计算的那些资产。 |
StaticWebAssetPublishCompressAllAssets |
在发布期间压缩所有资产,而不仅仅是在构建过程中发现或计算的资产。 |
| 资产 | Description |
|---|---|
StaticWebAssetBasePath |
库中所有资产的基 URL 路径。 |
StaticWebAssetsFingerprintContent |
启用内容指纹识别功能以实现缓存绕过。 |
StaticWebAssetFingerprintingEnabled |
为静态 Web 资产启用指纹功能。 |
StaticWebAssetsCacheDefineStaticWebAssetsEnabled |
启用静态 Web 资产定义的缓存。 |
StaticWebAssetEndpointExclusionPattern |
排除终结点的模式。 |
| 物料组 | Description | Metadata |
|---|---|---|
StaticWebAssetContentTypeMapping |
将文件模式映射到终结点的内容类型和缓存标头。 |
Pattern、Cache、Priority |
StaticWebAssetFingerprintPattern |
定义用于将指纹应用于静态 Web 资产以用于缓存破坏的模式。 |
Pattern、Expression |
元数据说明:
Pattern:用于匹配文件的 glob 模式。 对于StaticWebAssetContentTypeMapping,它匹配文件以确定其内容类型(例如,*.jsJavaScript 文件)。StaticWebAssetFingerprintPattern用于标识需要特殊指纹处理的多扩展文件(例如*.lib.module.js)。Cache:指定Cache-Control匹配内容类型的标头值。 此值控制浏览器缓存行为(例如,媒体文件使用max-age=3600, must-revalidate)。Priority:当多个StaticWebAssetContentTypeMapping项与同一文件匹配时,控制优先级。 较高的数值优先于较低的数值。Priority必需。Expression:定义指纹标识插入文件名的方式。 默认值为#[.{FINGERPRINT}],在扩展之前插入指纹({FINGERPRINT}占位符)。
以下示例将位图文件模式 (.bmp) 映射到 image/bmp 内容类型,其中 {CACHE HEADER} 占位符表示 Cache-Control 标头,用于没有指纹的端点:
<ItemGroup>
<StaticWebAssetContentTypeMapping Include="image/bmp" Cache="{CACHE HEADER}"
Pattern="*.bmp" Priority="1" />
</ItemGroup>
运行时配置选项
下表介绍了运行时配置选项。
| 配置密钥 | Description |
|---|---|
ReloadStaticAssetsAtRuntime |
支持开发阶段静态资源的热重载:提供修改后的 Web 根目录 (wwwroot) 文件(重新计算 ETag,必要时重新压缩),而非构建阶段的清单版本。 默认情况下,仅在服务构建清单时启用,除非有明确设置。 |
DisableStaticAssetNotFoundRuntimeFallback |
当 true 时,将屏蔽用于处理构建清单中未包含的新添加文件的备用终结点。 当 false 时或不存在时,文件存在性检查 {**path} 备用方案 (GET/HEAD) 会记录一条警告,并返回一个通过计算得出的 ETag 值。 |
EnableStaticAssetsDevelopmentCaching |
当 true 时,保留资产描述符上的原始 Cache-Control 头。 当false为空或缺失时,将Cache-Control标头重写为no-cache以避免在开发过程中出现过于激进的客户端缓存。 |
EnableStaticAssetsDevelopmentIntegrity |
当true运行时,会在资产描述符上保持完整性属性。 如果 false 不存在,则删除任何完整性属性,以防止在开发过程中文件发生更改时不匹配。 |