ASP.NET Core Blazor 静态文件

注意

此版本不是本文的最新版本。 有关当前版本,请参阅 本文的 .NET 10 版本

警告

此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅 本文的 .NET 10 版本

本文介绍用于提供静态文件的 Blazor 应用配置。

在阅读本文之前,有关使用 Map Static Assets 路由终结点约定提供静态文件的一般信息,请参阅 ASP.NET Core 应用中的静态文件服务

预加载的 Blazor 框架静态资产

在 Blazor Web App 中,框架静态资源使用 Link 头信息 自动预加载,这允许浏览器在提取和呈现初始页面之前预加载资源。

在独立 Blazor WebAssembly 应用中,在以下情况下,框架资源会被安排为高优先级下载,并在浏览器 index.html 页面处理过程早期进行缓存:

  • 应用 OverrideHtmlAssetPlaceholders 项目文件中的 MSBuild 属性(.csproj) 设置为 true

    <PropertyGroup>
      <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
    </PropertyGroup>
    
  • 以下包含 rel="preload"<link> 元素存在于 wwwroot/index.html<head> 内容中:

    <link rel="preload" id="webassembly" />
    

注意

Blazor Web App增强型导航不会尝试在 .NET 11 或更高版本中预加载资源。 有关详细信息,请参阅 Blazor 增强型导航不再预加载资源

服务器端 Blazor 应用中的静态资源交付

提供静态资产由路由终结点约定或下表中所述的中间件管理。

功能 API .NET 版本 说明
Map Static Assets 路由终结点约定 MapStaticAssets .NET 9 或更高版本 优化静态资产到客户端的传输。
静态文件中间件 UseStaticFiles 所有 .NET 版本 向客户端提供静态资源,但不包含 Map Static Assets 提供的优化;不过,这对于某些 Map Static Assets 无法处理的任务很有用。

Map Static Assets 在大多数情况下可替代 UseStaticFiles。 但是,Map Static Assets 经过了专门优化,用于在构建和发布时从应用中的已知位置提供这些资产。 如果应用服务来自其他位置(如磁盘或嵌入资源)的资产,则应使用 UseStaticFiles

映射静态资产 (MapStaticAssets) 还取代了在提供 Blazor WebAssembly 框架文件的应用中调用 UseBlazorFrameworkFiles 的操作,并且不需要在 Blazor Web App 中显式调用 UseBlazorFrameworkFiles,因为在调用 AddInteractiveWebAssemblyComponents 时会自动调用该 API。

启用交互式 WebAssembly 或交互式自动呈现模式后:

  • Blazor 会创建一个终结点,以将资源集合公开为 JS 模块。
  • 当 WebAssembly 组件被渲染到页面时,URL 会作为持久化的组件状态写入请求正文。
  • WebAssembly 启动期间,Blazor 会检索 URL、导入模块和调用函数来检索资产集合并在内存中重新构造。 URL 特定于内容并永久缓存,因此,每个用户只需为此开销成本付费一次,直到应用更新。
  • 资源集合还以人工可读的 URL (_framework/resource-collection.js) 公开,因此 JS 有权访问资源集合,以便实现增强的导航,或实现其他框架和第三方组件的功能。

静态文件中间件(UseStaticFiles)在“映射静态资源”(MapStaticAssets)无法处理的以下情况下非常有用:

  • 从不属于生成或发布过程的磁盘中提供文件,例如在部署期间或之后添加到应用程序文件夹中的文件。
  • 将路径前缀应用于 Blazor WebAssembly 静态资产文件,详见 Blazor WebAssembly资产的前缀部分。
  • 配置扩展到特定内容类型的文件映射和设置静态文件选项,详见文件映射和静态文件选项部分。

有关详细信息,请参阅 ASP.NET Core应用中的静态文件服务

使用 Map Static Assets 路由端点约定传递资源

本部分适用于服务器端 Blazor 应用。

资产通过 ComponentBase.Assets 属性传递,该属性解析给定资产的指纹 URL。 在以下示例中,Bootstrap、 Blazor 项目模板应用样式表(app.css)和 CSS 隔离样式表 (基于应用的命名空间 BlazorSample)链接在根组件(通常是 App 组件)Components/App.razor中:

<link rel="stylesheet" href="@Assets["bootstrap/bootstrap.min.css"]" />
<link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["BlazorSample.styles.css"]" />

ImportMap 组件

本节适用于调用 MapRazorComponents 的 Blazor Web App。

组件 ImportMapImportMap) 表示一个导入映射元素(<script type="importmap"></script>),用于定义模块脚本的导入映射。 Import Map 组件放在根组件的 <head> 内容内,通常是 App 组件(Components/App.razor)。

<ImportMap />

如果未将自定义 ImportMapDefinition 分配给导入映射组件,则会根据应用的资产生成导入映射。

注意

ImportMapDefinition 创建实例的成本很高,因此我们建议在创建其他实例时缓存它们。

以下示例演示自定义导入映射定义及其创建的导入映射。

基本导入映射:

new ImportMapDefinition(
    new Dictionary<string, string>
    {
        { "jquery", "https://cdn.example.com/jquery.js" },
    },
    null,
    null);

上述代码生成以下导入映射:

{
  "imports": {
    "jquery": "https://cdn.example.com/jquery.js"
  }
}

范围内的导入映射:

new ImportMapDefinition(
    null,
    new Dictionary<string, IReadOnlyDictionary<string, string>>
    {
        ["/scoped/"] = new Dictionary<string, string>
        {
            { "jquery", "https://cdn.example.com/jquery.js" },
        }
    },
    null);

上述代码生成以下导入映射:

{
  "scopes": {
    "/scoped/": {
      "jquery": "https://cdn.example.com/jquery.js"
    }
  }
}

带有完整性的导入映射:

new ImportMapDefinition(
    new Dictionary<string, string>
    {
        { "jquery", "https://cdn.example.com/jquery.js" },
    },
    null,
    new Dictionary<string, string>
    {
        { "https://cdn.example.com/jquery.js", "sha384-abc123" },
    });

上述代码生成以下导入映射:

{
  "imports": {
    "jquery": "https://cdn.example.com/jquery.js"
  },
  "integrity": {
    "https://cdn.example.com/jquery.js": "sha384-abc123"
  }
}

将导入映射定义 (ImportMapDefinition) 与 ImportMapDefinition.Combine 组合在一起。

导入从 ResourceAssetCollection 创建的映射,将静态资产映射到其相应的唯一 URL:

ImportMapDefinition.FromResourceCollection(
    new ResourceAssetCollection(
    [
        new ResourceAsset(
            "jquery.fingerprint.js",
            [
                new ResourceAssetProperty("integrity", "sha384-abc123"),
                new ResourceAssetProperty("label", "jquery.js"),
            ])
    ]));

上述代码生成以下导入映射:

{
  "imports": {
    "./jquery.js": "./jquery.fingerprint.js"
  },
  "integrity": {
    "jquery.fingerprint.js": "sha384-abc123"
  }
}

导入映射内容安全策略 (CSP) 冲突

本节适用于调用 MapRazorComponents 的 Blazor Web App。

ImportMap组件呈现为内联<script>标记,这违反了设置default-src指令的严格script-src)。

有关如何通过子资源完整性 (SRI) 或加密 nonce 解决策略冲突的示例,请参阅解决子资源完整性 (SRI) 或 nonce 的 CSP 冲突

通过调用 UseStaticFiles 应用的请求处理管道,将静态文件中间件配置为向客户端提供静态资产。 有关详细信息,请参阅 ASP.NET Core应用中的静态文件服务

在.NET 8 之前的版本中,Blazor框架静态文件(如Blazor脚本)通过静态文件中间件提供。 在 .NET 8 或更高版本中,Blazor框架静态文件使用终结点路由进行映射,并且不再使用静态文件中间件。

独立 Blazor WebAssembly 应用中的指纹客户端静态资产

在独立应用 Blazor WebAssembly 的构建和发布过程中,框架会使用构建时计算出的值覆盖 index.html 中的占位符,以便为客户端渲染生成静态资源的指纹。 将指纹放入 blazor.webassembly.js 脚本文件名中,并为其他 .NET 资产生成导入映射。

要在独立的 Blazor WebAssembly 应用中采用指纹识别功能,其 wwwwoot/index.html 文件中必须包含以下配置:

<head>
    ...
    <script type="importmap"></script>
    ...
</head>

<body>
    ...
    <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
    ...
</body>

</html>

在项目文件(.csproj)中,属性 <OverrideHtmlAssetPlaceholders> 设置为 true

<PropertyGroup>
  <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>

在解析 JavaScript 互操作的导入时,浏览器会使用导入映射来解析指纹文件。

在以下示例中,所有开发人员提供的文件都是模块,文件扩展名为 JS。

在应用的scripts.js文件夹中,名为wwwroot/js的模块通过在文件扩展名前添加#[.{fingerprint}]来生成指纹(.js):

<script type="module" src="js/scripts#[.{fingerprint}].js"></script>

在应用程序的项目文件中使用 <StaticWebAssetFingerprintPattern> 属性指定指纹表达式(.csproj)。

<ItemGroup>
  <StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.js" 
    Expression="#[.{fingerprint}]!" />
</ItemGroup>

包含指纹标记的任何 JS 文件(*.js)在 index.html 中都会由框架进行指纹识别,包括发布应用时。

Blazor Web App 中的指纹客户端静态资产

对于在 Blazor Web App 中进行客户端呈现 (CSR) 的场景(交互式自动或交互式 WebAssembly 呈现模式),通过采用映射静态资产的路由终结点约定 (MapStaticAssets)ImportMap 组件ComponentBase.Assets 属性 (@Assets["..."]),可以启用静态资产的服务器端指纹识别。 有关详细信息,请参阅 ASP.NET Core应用中的静态文件服务

要对用于 CSR 的其他 JavaScript 模块进行指纹识别,请在应用的项目文件(<StaticWebAssetFingerprintPattern>)中使用 .csproj 项。 在以下示例中,将为应用中所有开发人员提供 .mjs 的文件添加指纹:

<ItemGroup>
  <StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
    Expression="#[.{fingerprint}]!" />
</ItemGroup>

在解析 JavaScript 互操作的导入时,浏览器会使用导入映射来解析指纹文件。

本部分适用于所有 .NET 版本和 Blazor 应用。

下表汇总了 .NET 版本的静态文件 <link>href 格式。

有关放置静态文件链接的 <head> 内容的位置,请参阅 ASP.NET Core Blazor 项目结构。 也可以在单独的 <HeadContent> 组件中使用 来提供静态资源链接。

有关放置静态文件链接的 <head> 内容的位置,请参阅 ASP.NET Core Blazor 项目结构

.NET 9 或更高版本

应用类型 href 示例
Blazor Web App @Assets["{PATH}"] <link rel="stylesheet" href="@Assets["app.css"]" />
<link href="@Assets["_content/ComponentLib/styles.css"]" rel="stylesheet" />
Blazor Server† @Assets["{PATH}"] <link href="@Assets["css/site.css"]" rel="stylesheet" />
<link href="@Assets["_content/ComponentLib/styles.css"]" rel="stylesheet" />
独立式 Blazor WebAssembly {PATH} <link rel="stylesheet" href="css/app.css" />
<link href="_content/ComponentLib/styles.css" rel="stylesheet" />

.NET 8.x

应用类型 href 示例
Blazor Web App {PATH} <link rel="stylesheet" href="app.css" />
<link href="_content/ComponentLib/styles.css" rel="stylesheet" />
Blazor Server† {PATH} <link href="css/site.css" rel="stylesheet" />
<link href="_content/ComponentLib/styles.css" rel="stylesheet" />
独立式 Blazor WebAssembly {PATH} <link rel="stylesheet" href="css/app.css" />
<link href="_content/ComponentLib/styles.css" rel="stylesheet" />

.NET 7.x 或更早版本

应用类型 href 示例
Blazor Server† {PATH} <link href="css/site.css" rel="stylesheet" />
<link href="_content/ComponentLib/styles.css" rel="stylesheet" />
托管 Blazor WebAssembly‡ {PATH} <link href="css/app.css" rel="stylesheet" />
<link href="_content/ComponentLib/styles.css" rel="stylesheet" />
Blazor WebAssembly {PATH} <link href="css/app.css" rel="stylesheet" />
<link href="_content/ComponentLib/styles.css" rel="stylesheet" />

†.NET 8 或更高版本支持 Blazor Server,但在 .NET 7 之后不再是项目模板。
‡建议在采用 .NET 8 或更高版本时将托管 Blazor WebAssembly 应用更新为 Blazor Web App。

静态 Web 资产项目模式

本部分适用于 .Client 的 Blazor Web App 项目。

<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>.Client 项目中,必需的 Blazor Web App 设置会将 Blazor WebAssembly 静态资源行为恢复为默认设置,使该项目表现得如同托管项目的一部分。 Blazor WebAssembly SDK (Microsoft.NET.Sdk.BlazorWebAssembly) 以特定方式配置静态 Web 资产,以便在“独立”模式下工作,服务器只需使用库的输出。 这不适用于 Blazor Web App,其中,应用的 WebAssembly 部分是主机的逻辑部分,必须表现得更像库。 例如,项目不公开样式捆绑包(例如 BlazorSample.Client.styles.css),而是仅向主机提供项目捆绑包,以便主机可以将其包括在自己的样式捆绑包中。

不支持更改 Default 的值 (<StaticWebAssetProjectMode>) 或从 .Client 项目中删除该属性。

Blazor WebAssembly 资产的前缀

本部分适用于 Blazor Web App。

使用 WebAssemblyComponentsEndpointOptions.PathPrefix 终结点选项设置指示 Blazor WebAssembly 资产的前缀的路径字符串。 该路径必须对应于引用的 Blazor WebAssembly 应用程序项目。

endpoints.MapRazorComponents<App>()
    .AddInteractiveWebAssemblyRenderMode(options => 
        options.PathPrefix = "{PATH PREFIX}");

在上一示例中,{PATH PREFIX} 占位符是路径前缀,必须以正斜杠 (/) 开头。

在以下示例中,路径前缀设置为 /path-prefix

endpoints.MapRazorComponents<App>()
    .AddInteractiveWebAssemblyRenderMode(options => 
        options.PathPrefix = "/path-prefix");

静态 Web 资源基础路径

本部分适用于独立的 Blazor WebAssembly 应用。

发布应用会将应用的静态资产,包括 Blazor 框架文件(_framework 文件夹资产),放置在已发布输出的根路径 (/) 中。 项目文件 (<StaticWebAssetBasePath>) 中指定的 .csproj 属性将基路径设置到非根路径:

<PropertyGroup>
  <StaticWebAssetBasePath>{PATH}</StaticWebAssetBasePath>
</PropertyGroup>

在前面的示例中,{PATH} 占位符是路径。

如果未设置 <StaticWebAssetBasePath> 属性,则独立应用发布在 /BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/

在上面的示例中,{TFM} 占位符是目标框架名字对象 (TFM)

如果独立 <StaticWebAssetBasePath> 应用中的 Blazor WebAssembly 属性将发布的静态资产路径设置为 app1,则发布的输出中应用的根路径为 /app1

在独立 Blazor WebAssembly 应用的项目文件(.csproj)中:

<PropertyGroup>
  <StaticWebAssetBasePath>app1</StaticWebAssetBasePath>
</PropertyGroup>

在发布的输出中,独立 Blazor WebAssembly 应用的路径为 /BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/app1/

在上面的示例中,{TFM} 占位符是目标框架名字对象 (TFM)

本部分适用于独立 Blazor WebAssembly 应用和托管的 Blazor WebAssembly 解决方案。

发布应用会将应用的静态资产,包括 Blazor 框架文件(_framework 文件夹资产),放置在已发布输出的根路径 (/) 中。 项目文件 (<StaticWebAssetBasePath>) 中指定的 .csproj 属性将基路径设置到非根路径:

<PropertyGroup>
  <StaticWebAssetBasePath>{PATH}</StaticWebAssetBasePath>
</PropertyGroup>

在前面的示例中,{PATH} 占位符是路径。

如果不设置 <StaticWebAssetBasePath> 属性,托管解决方案的客户端应用或独立应用将按照以下路径发布:

  • 托管的 Blazor WebAssembly 解决方案的 Server 项目中:/BlazorHostedSample/Server/bin/Release/{TFM}/publish/wwwroot/
  • 在独立 Blazor WebAssembly 应用中:/BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/

如果托管的 <StaticWebAssetBasePath> 应用或独立 Client 应用的 Blazor WebAssembly 项目中的 Blazor WebAssembly 属性将发布的静态资产路径设置为 app1,则发布的输出中应用的根路径为 /app1

Client 应用的项目文件 (.csproj) 或独立 Blazor WebAssembly 应用的项目文件 (.csproj) 中:

<PropertyGroup>
  <StaticWebAssetBasePath>app1</StaticWebAssetBasePath>
</PropertyGroup>

在已发布的输出内容中:

  • 托管的 Blazor WebAssembly 解决方案的 Server 项目中客户端应用的路径:/BlazorHostedSample/Server/bin/Release/{TFM}/publish/wwwroot/app1/
  • 到独立 Blazor WebAssembly 应用程序的路径:/BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/app1/

<StaticWebAssetBasePath> 属性最常用于控制单个托管部署中多个 Blazor WebAssembly 应用的已发布静态资产的路径。 有关详细信息,请参阅多个托管的 ASP.NET Core Blazor WebAssembly 应用。 该属性在独立 Blazor WebAssembly 应用中也有效。

在上面的示例中,{TFM} 占位符是目标框架名字对象 (TFM)

文件映射和静态文件选项

本部分适用于服务器端静态文件。

若要使用 FileExtensionContentTypeProvider 创建其他文件映射,或者要配置其他 StaticFileOptions,请使用以下方法之一。 在以下示例中,{EXTENSION} 占位符为文件扩展名,{CONTENT TYPE} 占位符为内容类型。

  • 使用 通过 ProgramStaticFileOptions 文件中配置选项:

    using Microsoft.AspNetCore.StaticFiles;
    
    ...
    
    var provider = new FileExtensionContentTypeProvider();
    provider.Mappings["{EXTENSION}"] = "{CONTENT TYPE}";
    
    builder.Services.Configure<StaticFileOptions>(options =>
    {
        options.ContentTypeProvider = provider;
    });
    

    此方法配置用于提供 Blazor 脚本的同一文件提供程序。 确保自定义配置不会干扰提供 Blazor 脚本。 例如,不要通过使用 provider.Mappings.Remove(".js") 配置提供程序来删除 JavaScript 文件的映射。

  • UseStaticFiles 文件中使用两次对 Program 的调用:

    • 使用 StaticFileOptions 在第一次调用中配置自定义文件提供程序。
    • 第二个中间件提供 Blazor 脚本,其使用 Blazor 框架提供的默认静态文件配置。
    using Microsoft.AspNetCore.StaticFiles;
    
    ...
    
    var provider = new FileExtensionContentTypeProvider();
    provider.Mappings["{EXTENSION}"] = "{CONTENT TYPE}";
    
    app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
    app.UseStaticFiles();
    
  • 可以使用 MapWhen 执行自定义静态文件中间件来避免在提供 _framework/blazor.server.js 时受到干扰:

    app.MapWhen(ctx => !ctx.Request.Path
        .StartsWithSegments("/_framework/blazor.server.js"),
            subApp => subApp.UseStaticFiles(new StaticFileOptions() { ... }));
    

从多个位置提供文件

本部分中的指南仅适用于 Blazor Web App。

若要使用 CompositeFileProvider 从多个位置提供文件服务:

示例:

在名为 AdditionalStaticAssets 的服务器项目中创建一个新文件夹。 将图像放入文件夹中。

将以下 using 语句添加到服务器项目的 Program 文件的顶部:

using Microsoft.Extensions.FileProviders;

在服务器项目的 Program 文件中,在对 MapStaticAssetsUseStaticFiles 进行任何调用之前,添加以下代码:

var secondaryProvider = new PhysicalFileProvider(
    Path.Combine(builder.Environment.ContentRootPath, "AdditionalStaticAssets"));
app.Environment.WebRootFileProvider = new CompositeFileProvider(
    app.Environment.WebRootFileProvider, secondaryProvider);

在应用的 Home 组件 (Home.razor) 标记中,使用 <img> 标记引用图像:

<img src="{IMAGE FILE NAME}" alt="{ALT TEXT}" />

在上面的示例中:

  • {IMAGE FILE NAME} 占位符是图像文件名称。 如果图像文件位于 AdditionalStaticAssets 文件夹的根目录,则无需提供路径段。
  • {ALT TEXT} 占位符是图像备用文本。

运行应用。

其他资源