托管和部署 ASP.NET Core Blazor WebAssembly

利用 Blazor WebAssembly 托管模型

  • 将 Blazor 应用、其依赖项及 .NET 运行时并行下载到浏览器。
  • 应用将在浏览器线程中直接执行。

支持以下部署策略:

  • Blazor 应用由 ASP.NET Core 应用提供服务。 使用 ASP.NET Core 进行托管部署部分中介绍了此策略。
  • Blazor 应用位于静态托管 Web 服务器或服务中,其中未使用 .NET 对 Blazor 应用提供服务。 独立部署部分介绍了此策略,包括有关将 Blazor WebAssembly 应用作为 IIS 子应用托管的信息。

预先 (AOT) 编译

Blazor WebAssembly 支持预先 (AOT) 编译,其中你可直接将 .NET 代码编译到 WebAssembly 中。 AOT 编译会提高运行时性能,代价是应用大小增加。

如果没有启用 AOT 编译,则 Blazor WebAssembly 应用使用在 WebAssembly 中实现的 .NET 中间语言 (IL) 解释器在浏览器上运行。 由于 .NET 代码已经过解释,因此应用的运行速度通常比在服务器端 .NET 即时 (JIT) 运行时上要慢。 AOT 编译将应用的 .NET 代码直接编译到 WebAssembly 中来供浏览器进行本机 WebAssembly 执行,从而处理这一性能问题。 AOT 性能改进可使执行 CPU 密集型任务的应用得到极大改进。 使用 AOT 编译的缺点是,AOT 编译的应用通常比其 IL 解释的对应项要长,因此通常在被首次请求时,下载到客户端的速度通常更慢。

若要启用 WebAssembly AOT 编译,请将设置为 true<RunAOTCompilation> 属性添加到 Blazor WebAssembly 应用的项目文件中:

<PropertyGroup>
  <RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>

若要将应用编译到 WebAssembly,请发布应用。 发布 Release 配置可确保 .NET 中间语言 (IL) 链接也运行来减少已发布的应用的大小:

dotnet publish -c Release

仅当项目发布时,才执行 WebAssembly AOT 编译。 在开发期间(Development 环境)运行项目时,不会使用 AOT 编译,理由是 AOT 编译处理小项目时通常用时几分钟,而处理更大项目时,耗时可能长得多。 对于未来版本的 ASP.NET Core,正在开发减少 AOT 编译生成时间的功能。

AOT 编译的 Blazor WebAssembly 应用的大小通常比编译到 .NET IL 中的应用大小要大。 虽然大小差异取决于应用,则大多数 AOT 编译的应用大约是其 IL 编译的版本的两倍大。 这意味着使用 AOT 编译是用加载时间性能换取运行时性能。 使用 AOT 编译是否值得进行这种权衡取决于你的应用。 CPU 密集型的 Blazor WebAssembly 应用通常从 AOT 编译中受益最大。

运行时重新链接

Blazor WebAssembly 应用最大的组成部分之一是基于 WebAssembly 的 .NET 运行时 (dotnet.wasm),当用户的浏览器首次访问该应用时,浏览器必须下载它。 重新链接 .NET WebAssembly 运行时会剪裁未使用的运行时代码,从而提高下载速度。

发布应用时,会自动执行运行时重新链接。 禁用全局化时,减少的大小尤其显著。 有关详细信息,请参阅 ASP.NET Core Blazor 全球化和本地化

本机依赖项支持

Blazor WebAssembly 应用可以使用生成的本机依赖项在 WebAssembly 上运行。 可以使用 .NET WebAssembly 生成工具将本机依赖项静态链接到 .NET WebAssembly 运行时,这些工具还可用于将 Blazor 应用提前 (AOT) 编译到 WebAssembly 或重新链接运行时以删除未使用的功能

.NET WebAssembly 生成工具基于 Emscripten,这是一个用于 Web 平台的编译器工具链。 若要安装 .NET WebAssembly 生成工具,请使用以下方法之一:

  • 在 Visual Studio 安装程序中选择可选组件。
  • 从管理命令提示符运行 dotnet workload install wasm-tools

通过在应用程序的项目文件中添加 NativeFileReference 项,将本机依赖项添加到 Blazor WebAssembly 应用。 生成项目时,每个 NativeFileReference 都由 .NET WebAssembly 生成工具传递给 Emscripten,以便对其进行编译并链接到运行时。 接下来,从应用程序的 .NET 代码使用 p/invoke 进入本机代码。

通常,任何可移植的本机代码都可以用作带有 Blazor WebAssembly 的本机依赖项。 可以将本机依赖项添加到 C/C++ 代码或以前使用 Emscripten 编译的代码:

  • 对象文件 (.o)
  • 存档文件 (.a)
  • Bitcode (.bc)
  • 独立 WebAssembly 模块 (.wasm)

通常必须使用用于生成 .NET WebAssembly 运行时的同一版本的 Emscripten 生成预生成依赖项。

使用本机代码

将简单的本机 C 函数添加到 Blazor WebAssembly 应用:

  1. 创建一个新的 Blazor WebAssembly 项目。

  2. Test.c 文件添加到该项目。

  3. Test.c 中添加用于计算阶乘的 C 函数:

    int fact(int n)
    {
        if (n == 0) return 1;
        return n * fact(n - 1);
    }
    
  4. 在应用程序的项目文件中为 Test.c 添加 NativeFileReference

    <ItemGroup>
      <NativeFileReference Include="Test.c" />
    </ItemGroup>
    
  5. 在 Razor 组件 (.razor) 中,为生成的 Test 库中的 fact 函数添加 DllImportAttribute,并从组件中的 .NET 代码调用 fact 方法:

    @using System.Runtime.InteropServices
    
    <p>@fact(3)</p>
    
    @code {
        [DllImport("Test")]
        static extern int fact(int n);
    }
    

在安装 .NET WebAssembly 生成工具后生成应用时,本机 C 代码将编译并链接到 .NET WebAssembly 运行时 (dotnet.wasm)。 编译和链接可能需要几分钟时间。 生成应用后,运行应用以查看呈现的阶乘值。

备注

在 6.0 预览版发布期间,你可能会在后续生成上收到生成错误,指明输出程序集正由另一个进程使用。 这是一个已知问题,将在 .NET 6 正式版本中得到解决。 若要解决此问题,请再次重新生成项目。

使用库

NuGet 包可以包含在 WebAssembly 使用的本机依赖项。 这些库及其本机功能随后可供任何 Blazor WebAssembly 应用使用。 本机依赖项的文件应为 WebAssembly 生成,并打包到特定于 browser-wasm 体系结构的文件夹中。 不自动引用特定于 WebAssembly 的依赖项,必须作为 NativeFileReference 手动引用。 包作者可以通过在带有引用的包中包含 .props 文件来选择添加本机引用。

SkiaSharp 是基于本机 Skia 图形库的适用于 .NET 的跨平台 2D 图形库,现在对 Blazor WebAssembly 提供预览支持。

警告

SkiaSharpSkia 图形库不归 Microsoft 所有或维护。 本部分中的演示示例不受 Microsoft 支持,不应在生产环境中使用。

若要在 Blazor WebAssembly 应用中使用 SkiaSharp,请执行以下操作:

  1. 在 Blazor WebAssembly 项目中添加对 SkiaSharp.Views.Blazor 包的包引用。 使用 Visual Studio 的进程将包添加到应用(管理 NuGet 包)或在命令外壳中执行 dotnet add package 命令:

    dotnet add package –-prerelease SkiaSharp.Views.Blazor
    

    备注

    在撰写本文时,SkiaSharp.Views.Blazor 包是预发行 NuGet 包,不用于生产用途。

  2. 使用以下内容将 SKCanvasView 组件添加到应用:

    • SkiaSharpSkiaSharp.Views.Blazor 命名空间。
    • 在 SkiaSharp 画布视图组件中绘制的逻辑 (SKCanvasView)。

    Pages/NativeDependencyExample.razor:

    @page "/native-dependency-example"
    @using SkiaSharp
    @using SkiaSharp.Views.Blazor
    
    <PageTitle>Native dependency</PageTitle>
    
    <h1>Native dependency example with SkiaSharp</h1>
    
    <SKCanvasView OnPaintSurface="OnPaintSurface" />
    
    @code {
        private void OnPaintSurface(SKPaintSurfaceEventArgs e)
        {
            var canvas = e.Surface.Canvas;
            canvas.Clear(SKColors.White);
            using var paint = new SKPaint
            {
                Color = SKColors.Black,
                IsAntialias = true,
                TextSize = 24
            };
            canvas.DrawText("SkiaSharp", 0, 24, paint);
        }
    }
    
  3. 生成应用,这可能需要几分钟时间。 运行应用并导航到 /native-dependency-example 处的 NativeDependencyExample 组件。

自定义加载启动资源的方式

自定义如何使用 loadBootResource API 加载启动资源。 有关详细信息,请参阅 ASP.NET Core Blazor 启动

压缩

发布 Blazor WebAssembly 应用时,将在发布过程中对输出内容进行静态压缩,从而减小应用的大小,并免去运行时压缩的开销。 使用以下压缩算法:

Blazor 依赖于主机提供适当的压缩文件。 使用 ASP.NET Core 托管项目时,主机项目能够执行内容协商并提供静态压缩文件。 托管 Blazor WebAssembly 独立应用时,可能需要额外的工作来确保提供静态压缩文件:

  • 有关 IIS web.config 压缩配置,请参阅 IIS:Brotli 和 Gzip 压缩 部分。

  • 如果在不支持静态压缩文件内容协商的静态托管解决方案(例如 GitHub 页面)上进行托管,请考虑配置应用以提取和解码 Brotli 压缩文件:

    • google/brotli GitHub repository 中获取 JavaScript Brotli 解码器。 缩小的解码器文件被命名为 decode.min.js,并且位于存储库的 js 文件夹中。

      备注

      如果 decode.js 脚本的缩小版本 (decode.min.js) 失败,请尝试使用缩小版本 (decode.js)。

    • 更新应用以使用解码器。

      wwwroot/index.html 文件中,在 Blazor 的 <script> 标记上将 autostart 设置为 false

      <script src="_framework/blazor.webassembly.js" autostart="false"></script>
      

      在 Blazor 的 <script> 标记之后和结束 </body> 标记之前,添加以下 JavaScript 代码 <script> 块:

      <script type="module">
        import { BrotliDecode } from './decode.min.js';
        Blazor.start({
          loadBootResource: function (type, name, defaultUri, integrity) {
            if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
              return (async function () {
                const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
                if (!response.ok) {
                  throw new Error(response.statusText);
                }
                const originalResponseBuffer = await response.arrayBuffer();
                const originalResponseArray = new Int8Array(originalResponseBuffer);
                const decompressedResponseArray = BrotliDecode(originalResponseArray);
                const contentType = type === 
                  'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
                return new Response(decompressedResponseArray, 
                  { headers: { 'content-type': contentType } });
              })();
            }
          }
        });
      </script>
      

      有关加载启动资源的详细信息,请参阅 ASP.NET Core Blazor 启动

若要禁用压缩,请将 BlazorEnableCompression MSBuild 属性添加到应用的项目文件,并将值设置为 false

<PropertyGroup>
  <BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>

可在命令行界面中使用以下语法将 BlazorEnableCompression 属性传递给 dotnet publish 命令:

dotnet publish -p:BlazorEnableCompression=false

重写 URL,以实现正确路由

在 Blazor WebAssembly 应用中路由对页组件的请求不如在 Blazor Server 托管应用中路由请求直接。 假设有一个具有两个组件的 Blazor WebAssembly 应用:

  • Main.razor:在应用的根目录处加载,并包含指向 About 组件 (href="About") 的链接。
  • About.razorAbout 组件。

使用浏览器的地址栏(例如,https://www.contoso.com/)请求应用的默认文档:

  1. 浏览器发出请求。
  2. 返回默认页,通常为 index.html
  3. index.html 启动应用。
  4. Blazor 的路由器进行加载,然后呈现 Razor Main 组件。

在 Main 页中,选择指向 About 组件的链接适用于客户端,因为 Blazor 路由器阻止浏览器在 Internet 上发出请求,针对 About 转到 www.contoso.com,并为呈现的 About 组件本身提供服务。 针对 Blazor WebAssembly 应用中的内部终结点的所有请求,工作原理都相同:这些请求不会触发对 Internet 上的服务器托管资源的基于浏览器的请求。 路由器将在内部处理请求。

如果针对 www.contoso.com/About 使用浏览器的地址栏发出请求,则请求会失败。 应用的 Internet 主机上不存在此类资源,所以返回的是“404 - 找不到”响应。

由于浏览器针对客户端页面请求基于 Internet 的主机,因此 Web 服务器和托管服务必须将对服务器上非物理方式资源的所有请求重写为 index.html 页。 如果返回 index.html,应用的 Blazor 路由器将接管工作并使用正确的资源响应。

部署到 IIS 服务器时,可以将 URL 重写模块与应用的已发布 web.config 文件一起使用。 有关详细信息,请参阅 IIS 部分。

使用 ASP.NET Core 进行托管部署

托管部署通过在 Web 服务器上运行的 ASP.NET Core 应用为浏览器提供 Blazor WebAssembly 应用。

客户端 Blazor WebAssembly 应用与服务器应用的其他任何静态 Web 资产一起发布到服务器应用的 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 文件夹。 这两个应用一起部署。 需要能够托管 ASP.NET Core 应用的 Web 服务器。 对于托管部署,Visual Studio 会在选择 Hosted 选项(使用 dotnet new 命令时为 -ho|--hosted)的情况下,包含 Blazor WebAssembly 应用项目模板(使用 dotnet new 命令时为 blazorwasm 模板) 。

有关详细信息,请参阅以下文章:

具有多个 Blazor WebAssembly 应用的托管部署

应用配置

托管的 Blazor 解决方案可为多个 Blazor WebAssembly 应用提供服务。

备注

本节中的示例引用 Visual Studio 解决方案的用法,但多个客户端应用在托管的 Blazor WebAssembly 程序方案中运行时,不需要使用 Visual Studio 和 Visual Studio 解决方案。 如果你未在使用 Visual Studio,则忽略 {SOLUTION NAME}.sln 文件以及为 Visual Studio 创建的任何其他文件。

如下示例中:

  • 初始(第一个)客户端应用是通过 Blazor WebAssembly 项目模板创建的解决方案的默认客户端项目。 第一个客户端应用可在浏览器中通过端口 5001 上或主机为 firstapp.com 的 URL /FirstApp 访问。
  • 向解决方案添加第二个客户端应用:SecondBlazorApp.Client。 第二个客户端应用可在浏览器中通过端口 5002 上或主机为 secondapp.com 的 URL /SecondApp 访问。

使用现有的托管 Blazor 解决方案,或者从 Blazor 托管的项目模板创建一个新解决方案:

  • 在客户端应用的项目文件中,在 <PropertyGroup> 中添加一个值为 FirstApp<StaticWebAssetBasePath> 属性,以设置项目静态资产的基路径:

    <PropertyGroup>
      ...
      <StaticWebAssetBasePath>FirstApp</StaticWebAssetBasePath>
    </PropertyGroup>
    
  • 向解决方案添加第二个客户端应用:

    • 向解决方案的文件夹添加一个名为 SecondClient 的文件夹。 在添加 SecondClient 文件夹后,通过项目模板创建的解决方案文件夹包含以下解决方案文件和文件夹:

      • Client(文件夹)
      • SecondClient(文件夹)
      • Server(文件夹)
      • Shared(文件夹)
      • {SOLUTION NAME}.sln(文件)

      占位符 {SOLUTION NAME} 是解决方案的名称。

    • 在 Blazor WebAssembly 项目模板的 SecondClient 文件夹中创建一个名为 SecondBlazorApp.Client 的 Blazor WebAssembly 应用。

    • SecondBlazorApp.Client 应用的项目文件中,执行以下操作:

      • <PropertyGroup> 添加一个值为 SecondApp<StaticWebAssetBasePath> 属性:

        <PropertyGroup>
          ...
          <StaticWebAssetBasePath>SecondApp</StaticWebAssetBasePath>
        </PropertyGroup>
        
      • Shared 项目添加一个项目引用:

        <ItemGroup>
          <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
        </ItemGroup>
        

        占位符 {SOLUTION NAME} 是解决方案的名称。

  • 在服务器应用的项目文件中,为新增的 SecondBlazorApp.Client 客户端应用创建一个项目引用:

    <ItemGroup>
      <ProjectReference Include="..\Client\{SOLUTION NAME}.Client.csproj" />
      <ProjectReference Include="..\SecondClient\SecondBlazorApp.Client.csproj" />
      <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
    </ItemGroup>
    

    占位符 {SOLUTION NAME} 是解决方案的名称。

  • 在服务器应用的 Properties/launchSettings.json 文件中,配置 Kestrel 配置文件 ({SOLUTION NAME}.Server) 的 applicationUrl,以访问位于端口 5001 和 5002 的客户端应用:

    "applicationUrl": "https://localhost:5001;https://localhost:5002",
    
  • 在服务器应用的 Program.cs 文件中,删除在调用 UseHttpsRedirection 后显示的以下行:

    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.MapRazorPages();
    app.MapControllers();
    app.MapFallbackToFile("index.html");
    

    添加将请求映射到客户端应用的中间件。 下面的示例将中间件配置为在以下情况下运行:

    • 原始客户端应用的请求端口为 5001,或新增的客户端应用的请求端口为 5002。

    • 原始客户端应用的请求主机为 firstapp.com,或新增的客户端应用的请求主机为 secondapp.com

      备注

      本部分中显示的示例需要其他配置以实现以下目的:

      • 访问示例主机域 firstapp.comsecondapp.com 上的应用。
      • 获取客户端应用的证书以启用 TLS 安全性 (HTTPS)。

      所需的配置不在本文内容范围内,并且取决于解决方案的托管方式。 有关详细信息,请参阅托管和部署文章

    将以下代码放在先前已删除行的位置:

    app.MapWhen(ctx => ctx.Request.Host.Port == 5001 || 
        ctx.Request.Host.Equals("firstapp.com"), first =>
    {
        first.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/FirstApp" + ctx.Request.Path;
            return nxt();
        });
    
        first.UseBlazorFrameworkFiles("/FirstApp");
        first.UseStaticFiles();
        first.UseStaticFiles("/FirstApp");
        first.UseRouting();
    
        first.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/FirstApp/{*path:nonfile}", 
                "FirstApp/index.html");
        });
    });
    
    app.MapWhen(ctx => ctx.Request.Host.Port == 5002 || 
        ctx.Request.Host.Equals("secondapp.com"), second =>
    {
        second.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/SecondApp" + ctx.Request.Path;
            return nxt();
        });
    
        second.UseBlazorFrameworkFiles("/SecondApp");
        second.UseStaticFiles();
        second.UseStaticFiles("/SecondApp");
        second.UseRouting();
    
        second.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/SecondApp/{*path:nonfile}", 
                "SecondApp/index.html");
        });
    });
    
  • 在服务器应用的天气预报控制器 (Controllers/WeatherForecastController.cs) 中,将到 WeatherForecastController 的现有路由 ([Route("[controller]")]) 替换为以下路由:

    [Route("FirstApp/[controller]")]
    [Route("SecondApp/[controller]")]
    

    先前添加到服务器应用的请求处理管道的中间件会将向 /WeatherForecast 发出的传入请求修改为 /FirstApp/WeatherForecast/SecondApp/WeatherForecast,具体取决于端口 (5001/5002) 或域 (firstapp.com/secondapp.com)。 为了将天气数据从服务器应用返回到客户端应用,需要前面的控制器路由。

多个 Blazor WebAssembly 应用的静态资产和类库

使用以下方法来引用静态资产:

  • 如果资产位于客户端应用的 wwwroot 文件夹中,请正常提供路径:

    <img alt="..." src="/{PATH AND FILE NAME}" />
    

    {PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。

  • 如果资产位于 Razor 类库 (RCL)wwwroot 文件夹中,请按照 ASP.NET Core 的类库中的可重用 Razor UI 中的指导,在客户端应用中引用静态资产:

    <img alt="..." src="_content/{PACKAGE ID}/{PATH AND FILE NAME}" />
    

    {PACKAGE ID} 占位符是库的包 ID。 如果项目文件中没有指定 <PackageId>,则包 ID 默认为项目的程序集名称。 {PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。

有关 RCL 的详细信息,请参阅:

独立部署

独立部署将 Blazor WebAssembly 应用作为客户端直接请求的一组静态文件提供。 任何静态文件服务器均可提供 Blazor 应用。

独立部署资产将发布到 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 文件夹。

Azure 应用服务

可以将 Blazor WebAssembly 应用部署到 Windows 上的 Azure 应用服务,该服务在 IIS 上托管应用。

目前不支持将独立的 Blazor WebAssembly 应用部署到适用于 Linux 的 Azure 应用服务。 目前无法提供用于托管应用的 Linux 服务器映像。 正在进行此工作以支持此场景。

Azure 静态 Web 应用

有关详细信息,请参阅教程:在 Azure Static Web Apps 中使用 Blazor 生成静态 Web 应用

IIS

IIS 是适用于 Blazor 应用的强大静态文件服务器。 要配置 IIS 以托管 Blazor,请参阅在 IIS 上生成静态网站

/bin/Release/{TARGET FRAMEWORK}/publish 文件夹中已创建发布的资产。 在 Web 服务器或托管服务上托管 publish 文件夹的内容。

web.config

发布 Blazor 项目时,将使用以下 IIS 配置创建 web.config 文件:

  • 对以下文件扩展名设置 MIME 类型:
    • .dll: application/octet-stream
    • .json: application/json
    • .wasm: application/wasm
    • .woff: application/font-woff
    • .woff2: application/font-woff
  • 对以下 MIME 类型启用 HTTP 压缩:
    • application/octet-stream
    • application/wasm
  • 建立 URL 重写模块规则:
    • 提供应用的静态资产所驻留的子目录 (wwwroot/{PATH REQUESTED})。
    • 创建 SPA 回退路由,以便非文件资产请求能够重定向到应用的静态资产文件夹中的默认文档 (wwwroot/index.html)。

使用自定义 web.config

要使用自定义 web.config 文件,请将自定义 web.config 文件置于项目文件夹的根目录下。 将项目配置为使用应用项目文件中的 PublishIISAssets 发布特定于 IIS 的资源,并发布项目:

<PropertyGroup>
  <PublishIISAssets>true</PublishIISAssets>
</PropertyGroup>

安装 URL 重写模块

重写 URL 必须使用 URL 重写模块。 此模块默认不安装,且不适用于安装为 Web 服务器 (IIS) 角色服务功能。 必须从 IIS 网站下载该模块。 使用 Web 平台安装程序安装模块:

  1. 以本地方式导航到 URL 重写模块下载页。 对于英语版本,请选择“WebPI”以下载 WebPI 安装程序。 对于其他语言,请选择适当的服务器体系结构 (x86/x64) 下载安装程序。
  2. 将安装程序复制到服务器。 运行安装程序。 选择“安装”按钮,并接受许可条款。 安装完成后无需重启服务器。

配置网站

将网站的物理路径设置为应用的文件夹。 该文件夹包含:

  • web.config 文件,IIS 使用该文件配置网站,包括所需的重定向规则和文件内容类型。
  • 应用的静态资产文件夹。

作为 IIS 子应用托管

如果独立应用作为 IIS 子应用托管,请执行下列任一操作:

  • 禁用继承的 ASP.NET Core 模块处理程序。

    通过向文件添加 <handlers> 部分,删除 Blazor 应用的已发布 web.config 文件中的处理程序:

    <handlers>
      <remove name="aspNetCore" />
    </handlers>
    
  • 通过使用 <location> 元素并且将 inheritInChildApplications 设置为 false,禁止继承根(父级)应用的 <system.webServer> 部分:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <location path="." inheritInChildApplications="false">
        <system.webServer>
          <handlers>
            <add name="aspNetCore" ... />
          </handlers>
          <aspNetCore ... />
        </system.webServer>
      </location>
    </configuration>
    

配置应用的基路径外,还需删除处理程序或禁用继承。 在 IIS 中配置子应用时,在应用的 index.html 文件中将应用基路径设置为 IIS 别名。

Brotli 和 Gzip 压缩

本部分仅适用于独立的 Blazor WebAssembly 应用。托管的 Blazor 应用使用默认的 ASP.NET Core 应用 web.config 文件,而不使用本部分中所链接的文件。

通过 web.config 可将 IIS 配置为提供独立 Blazor WebAssembly 应用的 Brotli 或 Gzip 压缩的 Blazor 资产。 若要查看示例配置文件,请参阅 web.config

在以下情况下,可能需要进一步配置示例 web.config 文件:

  • 应用的规范具有以下任意一个要求:
    • 提供不是由示例 web.config 文件配置的压缩文件。
    • 采用非压缩格式提供由示例 web.config 文件配置的压缩文件。
  • 服务器的 IIS 配置(例如 applicationHost.config)提供了服务器级 IIS 默认值。 根据服务器级别配置,应用可能要求 IIS 配置不同于示例 web.config 文件所包含的配置。

疑难解答

如果你看到“500 - 内部服务器错误”,且 IIS 管理器在尝试访问网站配置时抛出错误,请确认是否已安装 URL 重写模块。 如果未安装该模块,则 IIS 无法分析 web.config 文件。 这可以防止 IIS 管理器加载网站配置,并防止网站对 Blazor 的静态文件提供服务。

有关排查部署到 IIS 的问题的详细信息,请参阅 对 Azure 应用服务和 IIS 上的 ASP.NET Core 进行故障排除

Azure 存储

Azure 存储静态文件承载允许无服务器的 Blazor 应用承载。 支持自定义域名、Azure 内容分发网络 (CDN) 以及 HTTPS。

为存储帐户上的静态网站承载启用 blob 服务时:

  • 设置“索引文档名称”到 index.html
  • 设置“错误文档路径”到 index.html。 Razor 组件和其他非文件终结点不会驻留在由 blob 服务存储的静态内容中的物理路径中。 当收到 Blazor 路由器应处理的对这些资源之一的请求时,由 blob 服务生成的“404 - 未找到”错误会将此请求路由到“错误文档路径”。 返回 index.html blob,Blazor 路由器会加载并处理此路径。

如果由于文件的 Content-Type 标头中的 MIME 类型不正确,导致在运行时未加载文件,请执行以下任一操作:

  • 配置工具,用于在部署文件时设置正确的 MIME 类型(Content-Type 标头)。

  • 在部署应用后更改文件的 MIME 类型(Content-Type 标头)。

    在每个文件的存储资源管理器(Azure 门户)中,执行以下操作:

    1. 右键单击该文件并选择“属性”。
    2. 设置“ContentType”并选择“保存”按钮 。

有关更多信息,请参阅 Azure 存储中的静态网站承载

Nginx

以下 nginx.conf 文件已简化,以展示如何配置 Nginx,以便每当在磁盘上找不到相应文件时,就发送 index.html 文件。

events { }
http {
    server {
        listen 80;

        location / {
            root      /usr/share/nginx/html;
            try_files $uri $uri/ /index.html =404;
        }
    }
}

使用 limit_req 设置 NGINX 突发速率限制时,Blazor WebAssembly 应用可能需要一个较大的 burst 参数值来容纳应用发出的请求数。 首先,将值设置为不低于 60:

http {
    server {
        ...

        location / {
            ...

            limit_req zone=one burst=60 nodelay;
        }
    }
}

如果浏览器开发人员工具或网络流量工具指示请求收到“503 - 服务不可用”状态代码,则将该值调高。

有关生产 Nginx Web 服务器配置的详细信息,请参阅 Creating NGINX Plus and NGINX Configuration Files(创建 NGINX 增强版和 NGINX 配置文件)。

Apache

若要将 Blazor WebAssembly 应用部署到 CentOS 7 或更高版本,请执行以下操作:

  1. 创建 Apache 配置文件。 下面的示例展示了一个简化的配置文件 (blazorapp.config):

    <VirtualHost *:80>
        ServerName www.example.com
        ServerAlias *.example.com
    
        DocumentRoot "/var/www/blazorapp"
        ErrorDocument 404 /index.html
    
        AddType application/wasm .wasm
        AddType application/octet-stream .dll
    
        <Directory "/var/www/blazorapp">
            Options -Indexes
            AllowOverride None
        </Directory>
    
        <IfModule mod_deflate.c>
            AddOutputFilterByType DEFLATE text/css
            AddOutputFilterByType DEFLATE application/javascript
            AddOutputFilterByType DEFLATE text/html
            AddOutputFilterByType DEFLATE application/octet-stream
            AddOutputFilterByType DEFLATE application/wasm
            <IfModule mod_setenvif.c>
          BrowserMatch ^Mozilla/4 gzip-only-text/html
          BrowserMatch ^Mozilla/4.0[678] no-gzip
          BrowserMatch bMSIE !no-gzip !gzip-only-text/html
      </IfModule>
        </IfModule>
    
        ErrorLog /var/log/httpd/blazorapp-error.log
        CustomLog /var/log/httpd/blazorapp-access.log common
    </VirtualHost>
    
  2. 将 Apache 配置文件放入 /etc/httpd/conf.d/ 目录(这是 CentOS 7 中的默认 Apache 配置目录)。

  3. 将应用的文件放入 /var/www/blazorapp 目录(配置文件中特定于 DocumentRoot 的位置)。

  4. 重启 Apache 服务。

有关详细信息,请参阅 mod_mimemod_deflate

GitHub 页

要处理 URL 重写,请使用脚本添加 wwwroot/404.html 文件,该脚本可处理到 index.html 页的重定向请求。 有关示例,请参阅 SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库

如果使用项目站点而非组织站点,请在 wwwroot/index.html 中更新 <base> 标记。 将 href 属性值设置为,包含尾部斜杠的 GitHub 存储库名称(例如,/my-repository/)。 在 SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库中,将在发布时通过 .github/workflows/main.yml 配置文件更新基本 href

备注

SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库 不归 .NET Foundation 或 Microsoft 所有,也不由它们提供维护和支持。

主机配置值

在开发环境中,Blazor WebAssembly 应用可以在运行时接受以下主机配置值作为命令行参数。

内容根

--contentroot 参数设置包含应用内容文件的目录的绝对路径(内容根目录)。 在下面的示例中,/content-root-path 是应用的内容根路径。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --contentroot=/content-root-path
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--contentroot=/content-root-path"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --contentroot=/content-root-path
    

基路径

--pathbase 参数可设置使用非根相对 URL 路径本地运行的应用的应用基路径(将 <base> 标记 href 针对暂存和生产设置为 / 之外的路径)。 在下面的示例中,/relative-URL-path 是应用的基路径。 有关详细信息,请参阅应用基路径

重要

不同于向 href 标记的 <base> 提供的路径,传递 --pathbase 参数值时不包括尾部反斜杠 (/)。 如果在 <base> 标记中以 <base href="/CoolApp/"> 形式(包括尾部反斜杠)提供应用基路径,则以 --pathbase=/CoolApp 形式(无尾部反斜杠)传递命令行参数值。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --pathbase=/relative-URL-path
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--pathbase=/relative-URL-path"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --pathbase=/relative-URL-path
    

URL

--urls 参数设置 IP 地址或主机地址,其中包含侦听请求的端口和协议。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --urls=http://127.0.0.1:0
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--urls=http://127.0.0.1:0"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --urls=http://127.0.0.1:0
    

配置裁边器

Blazor 对每个发布版本执行中间语言 (IL) 剪裁,以从输出程序集中删除不必要的 IL。 有关详细信息,请参阅 配置适用于 ASP.NET Core Blazor 的裁边器

更改 DLL 文件的文件扩展名

如果需要更改应用的已发布 .dll 文件的文件扩展名,请按照本部分中的指导进行操作。

发布应用后,使用 shell 脚本或 DevOps 生成管道将 .dll 文件重命名,以使用其他文件扩展名。 将 .dll 文件的目标位置设为应用的已发布输出的 wwwroot 目录中(例如 {CONTENT ROOT}/bin/Release/netstandard2.1/publish/wwwroot)。

在下面的示例中,重命名 .dll 文件,以使用 .bin 文件扩展名。

在 Windows 上:

dir .\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content .\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content .\_framework\blazor.boot.json

如果服务工作进程资产也在使用中,请添加以下命令:

((Get-Content .\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content .\service-worker-assets.js

在 Linux 或 macOS 上:

for f in _framework/_bin/*; do mv "$f" "`echo $f | sed -e 's/\.dll/.bin/g'`"; done
sed -i 's/\.dll"/.bin"/g' _framework/blazor.boot.json

如果服务工作进程资产也在使用中,请添加以下命令:

sed -i 's/\.dll"/.bin"/g' service-worker-assets.js

要使用不同于 .bin 的其他文件扩展名,请在前面的命令中替换 .bin

若要处理压缩的 blazor.boot.json.gzblazor.boot.json.br 文件,请采用以下方法之一:

  • 删除压缩的 blazor.boot.json.gzblazor.boot.json.br 文件。 此方法禁用压缩。
  • 重新压缩更新后的 blazor.boot.json 文件。

上述指导也适用于正在使用服务工作进程资产的情况。 删除或重新压缩 wwwroot/service-worker-assets.js.brwwwroot/service-worker-assets.js.gz。 否则,浏览器中的文件完整性检查将失败。

以下 Windows 示例使用项目根目录中的 PowerShell 脚本。

ChangeDLLExtensions.ps1::

param([string]$filepath,[string]$tfm)
dir $filepath\bin\Release\$tfm\wwwroot\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json
Remove-Item $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json.gz

如果服务工作进程资产也在使用中,请添加以下命令:

((Get-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js

在项目文件中,在发布应用后运行脚本:

<Target Name="ChangeDLLFileExtensions" AfterTargets="Publish" Condition="'$(Configuration)'=='Release'">
  <Exec Command="powershell.exe -command &quot;&amp; { .\ChangeDLLExtensions.ps1 '$(SolutionDir)' '$(TargetFramework)'}&quot;" />
</Target>

备注

重命名和延迟加载相同的程序集时,请参阅 在 ASP.NET Core Blazor WebAssembly 中延迟加载程序集 中的指南。

解决完整性检查失败

当 Blazor WebAssembly 下载应用的启动文件时,它会指示浏览器对响应执行完整性检查。 它使用 blazor.boot.json 文件中的信息为 .dll.wasm 和其他文件指定预期的 SHA-256 哈希值。 这对以下原因有所帮助:

  • 它可确保不会出现加载不一致文件集的风险,例如,在用户正在下载应用程序文件时将新部署应用到 Web 服务器的情况。 不一致的文件可能导致未定义的行为。
  • 它可确保用户的浏览器从不缓存不一致或无效的响应,这些响应可能会阻止他们启动应用(即使他们手动刷新了页面也是如此)。
  • 它可以安全地缓存响应,甚至无需检查服务器端更改,直到预期的 SHA-256 哈希本身发生更改,因此,后续页面加载需要较少的请求即可快速完成。

如果你的 Web 服务器返回的响应与预期的 SHA-256 哈希不匹配,你将看到类似于以下内容的错误显示在浏览器的开发人员控制台中:

未能使用计算出的 SHA-256 完整性“IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY=”在资源“https://myapp.example.com/_framework/MyBlazor App.dll”的“integrity”属性中找到有效摘要。 资源已被阻止。

在大多数情况下,这不是完整性检查本身的问题。 相反,它表示存在其他问题,并且完整性检查会警告你其他问题。

诊断完整性问题

生成应用时,生成的 blazor.boot.json 清单将描述生成输出生成时启动资源(例如,.dll.wasm 和其他文件)的 SHA-256 哈希。 只要 blazor.boot.json 中的 SHA-256 哈希与传递到浏览器的文件相匹配,完整性检查就会通过。

此失败的常见原因包括:

  • Web 服务器的响应是一个错误(例如,“404 - 找不到”或“500 - 内部服务器错误”),而不是浏览器所请求的文件。 浏览器会将其报告为完整性检查失败,而不是响应失败。
  • 在文件生成和传递到浏览器之间已更改文件的内容。 下面可能会发生这种情况:
    • 你或生成工具手动修改生成输出的情况。
    • 部署过程的某个方面修改了文件的情况。 例如,在使用基于 Git 的部署机制时,请记住,如果你在 Windows 上提交文件并在 Linux 上检查它们,则 Git 会以透明方式将 Windows 样式的行尾转换为 Unix 样式的行尾。 更改文件行尾将更改 SHA-256 哈希。 若要避免此问题,请考虑使用 .gitattributes 将生成项目视为 binary 文件
    • Web 服务器在提供文件内容的过程中对其进行修改。 例如,某些内容分发网络 (CDN) 会自动尝试缩小 HTML,从而对其进行修改。 可能需要禁用此类功能。

若要诊断哪些功能适用于你的情况,请执行执行操作:

  1. 通过读取错误消息来记下哪个文件触发错误。
  2. 打开浏览器的开发人员工具,然后在“网络”选项卡中查找。如有必要,请重新加载页面以查看请求和响应的列表。 在该列表中查找触发错误的文件。
  3. 检查响应中的 HTTP 状态代码。 如果服务器返回除“200 - 正常”(或其他 2xx 状态代码)以外的任何内容,则需要诊断服务器端问题。 例如,状态代码 403 表示存在授权问题,而状态代码 500 表示服务器以未指定的方式失败。 请参阅服务器端日志以诊断和修复应用。
  4. 如果资源的状态代码为“200 - 正常”,请在浏览器的开发人员工具中查看响应内容,并检查内容是否与预期的数据匹配。 例如,常见问题是错误配置了路由,因此请求甚至返回其他文件的 index.html 数据。 请确保对 .wasm 请求的响应是 WebAssembly 二进制文件,对 .dll 请求的响应是 .NET 程序集二进制文件。 如果不是,则需要诊断服务器端路由问题。
  5. 搜索以验证应用的已发布和已部署输出,并提供完整性 PowerShell 脚本故障排除

如果确认服务器返回看似正确的数据,则必须在生成文件和传递文件之间修改内容。 若要对此进行调查,请执行以下操作:

  • 如果在生成文件后修改文件,请检查生成工具链和部署机制。 例如,在 Git 转换文件行尾时,如前所述。
  • 如设置为动态修改响应(例如,尝试缩小 HTML),请检查 Web 服务器或 CDN 配置。 Web 服务器可以实现 HTTP 压缩(例如,返回 content-encoding: brcontent-encoding: gzip),因为这不会影响解压缩后的结果。 但是,Web 服务器不可以修改未压缩的数据。

完整性 PowerShell 脚本故障排除

使用 integrity.ps1 PowerShell 脚本来验证已发布和已部署的 Blazor 应用。 当应用出现 Blazor 框架无法识别的完整性问题时,该脚本将作为起点提供给 PowerShell Core 6。 应用可能需要自定义脚本,包括在 6.2.7 版之后的 PowerShell 版本上运行时。

此脚本将检查 publish 文件夹中的文件,并从部署的应用中下载这些文件,以检测包含完整性哈希的不同清单中的问题。 这些检查应检测最常见的问题:

  • 你修改了已发布的输出中的文件,但未实现它。
  • 应用未正确部署到部署目标,或者在部署目标的环境中发生了更改。
  • 部署的应用与发布应用的输出之间存在差异。

在 PowerShell 命令行中使用以下命令调用脚本:

.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER}

占位符:

  • {BASE URL}:已部署的应用的 URL。
  • {PUBLISH OUTPUT FOLDER}:应用的 publish 文件夹的路径,或已发布的用于部署应用的位置。

备注

克隆 dotnet/AspNetCore.Docs GitHub 存储库时,integrity.ps1 脚本可能会被 Bitdefender 或系统上存在的另一个病毒扫描程序隔离。 通常,该文件被病毒扫描程序的启发式扫描技术捕获,该技术只查找文件中可能指示存在恶意软件的模式。 若要防止病毒扫描程序隔离该文件,请在克隆存储库之前向病毒扫描程序添加一个例外。 以下示例是 Windows 系统上脚本的一个典型路径。 可以根据需要为其他系统调整路径。 占位符 {USER} 是用户的路径段。

C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1

警告:创建病毒扫描程序例外是危险操作,只有在你确定文件是安全的情况下才可以进行。

将文件的校验和与有效校验和值进行比较并不能保证文件的安全,但以维护校验和值的方式修改文件对于恶意用户来说并不简单。 因此,校验和作为一种常规安全方法很有用。 将本地 integrity.ps1 文件的校验和与以下值之一进行比较:

  • SHA256:6b0dc7aba5d8489136bb2969036432597615b11b4e432535e173ca077a0449c4
  • MD5:f0c800a4c72604bd47f3c19f5f0bb4f4

通过以下命令在 Windows OS 上获取文件的校验和。 为 {PATH AND FILE NAME} 占位符提供路径和文件名,并指明为 {SHA512|MD5} 占位符生成的校验和类型,可以是 SHA256,也可以是 MD5

CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5}

如果你有任何理由担心校验和验证在你的环境中不够安全,请咨询组织的安全领导以获得指导。

有关详细信息,请参阅了解恶意软件和其他威胁

禁用非 PWA 应用的完整性检查

在大多数情况下,不要禁用完整性检查。 禁用完整性检查并不能解决导致意外响应的根本问题,并且会导致丢失前面列出的权益。

在某些情况下,Web 服务器无法用于返回一致的响应,但别无选择,只能禁用完整性检查。 若要禁用完整性检查,请将以下内容添加到 Blazor WebAssembly 项目的 .csproj 文件中的属性组:

<BlazorCacheBootResources>false</BlazorCacheBootResources>

BlazorCacheBootResources 还会根据 SHA-256 哈希禁用 Blazor 缓存 .dll.wasm 和其他文件的默认行为,因为属性指示无法依靠 SHA-256 哈希来确保正确性。 即使有此设置,浏览器的普通 HTTP 缓存仍可能会缓存这些文件,但是否发生这种情况取决于你的 Web 服务器配置和它所提供的 cache-control 标头。

备注

BlazorCacheBootResources 属性不会禁用渐进式 Web 应用程序 (PWA) 的完整性检查。 有关 PWA 的相关指南,请参阅禁用 PWA 的完整性检查部分。

禁用 PWA 的完整性检查

Blazor 的渐进式 Web 应用程序 (PWA) 模板包含建议的 service-worker.published.js 文件,该文件负责获取和存储应用程序文件以供脱机使用。 这是普通应用启动机制的独立进程,具有其自己单独的完整性检查逻辑。

service-worker.published.js 文件中,出现以下行:

.map(asset => new Request(asset.url, { integrity: asset.hash }));

若要禁用完整性检查,请通过将该行更改为以下行来删除 integrity 参数:

.map(asset => new Request(asset.url));

同样,禁用完整性检查意味着会丢失完整性检查提供的安全保证。 例如,如果用户的浏览器在你部署新版本的那一刻缓存应用,则会存在风险,它可能会缓存旧部署中的某些文件和新部署中的某些文件。 如果发生这种情况,则在部署进一步更新之前,应用程序会在中断状态下停滞。

利用 Blazor WebAssembly 托管模型

  • 将 Blazor 应用、其依赖项及 .NET 运行时并行下载到浏览器。
  • 应用将在浏览器线程中直接执行。

支持以下部署策略:

  • Blazor 应用由 ASP.NET Core 应用提供服务。 使用 ASP.NET Core 进行托管部署部分中介绍了此策略。
  • Blazor 应用位于静态托管 Web 服务器或服务中,其中未使用 .NET 对 Blazor 应用提供服务。 独立部署部分介绍了此策略,包括有关将 Blazor WebAssembly 应用作为 IIS 子应用托管的信息。

自定义加载启动资源的方式

自定义如何使用 loadBootResource API 加载启动资源。 有关详细信息,请参阅 ASP.NET Core Blazor 启动

压缩

发布 Blazor WebAssembly 应用时,将在发布过程中对输出内容进行静态压缩,从而减小应用的大小,并免去运行时压缩的开销。 使用以下压缩算法:

Blazor 依赖于主机提供适当的压缩文件。 使用 ASP.NET Core 托管项目时,主机项目能够执行内容协商并提供静态压缩文件。 托管 Blazor WebAssembly 独立应用时,可能需要额外的工作来确保提供静态压缩文件:

  • 有关 IIS web.config 压缩配置,请参阅 IIS:Brotli 和 Gzip 压缩 部分。

  • 如果在不支持静态压缩文件内容协商的静态托管解决方案(例如 GitHub 页面)上进行托管,请考虑配置应用以提取和解码 Brotli 压缩文件:

    • google/brotli GitHub repository 中获取 JavaScript Brotli 解码器。 缩小的解码器文件被命名为 decode.min.js,并且位于存储库的 js 文件夹中。

      备注

      如果 decode.js 脚本的缩小版本 (decode.min.js) 失败,请尝试使用缩小版本 (decode.js)。

    • 更新应用以使用解码器。

      wwwroot/index.html 文件中,在 Blazor 的 <script> 标记上将 autostart 设置为 false

      <script src="_framework/blazor.webassembly.js" autostart="false"></script>
      

      在 Blazor 的 <script> 标记之后和结束 </body> 标记之前,添加以下 JavaScript 代码 <script> 块:

      <script type="module">
        import { BrotliDecode } from './decode.min.js';
        Blazor.start({
          loadBootResource: function (type, name, defaultUri, integrity) {
            if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
              return (async function () {
                const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
                if (!response.ok) {
                  throw new Error(response.statusText);
                }
                const originalResponseBuffer = await response.arrayBuffer();
                const originalResponseArray = new Int8Array(originalResponseBuffer);
                const decompressedResponseArray = BrotliDecode(originalResponseArray);
                const contentType = type === 
                  'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
                return new Response(decompressedResponseArray, 
                  { headers: { 'content-type': contentType } });
              })();
            }
          }
        });
      </script>
      

      有关加载启动资源的详细信息,请参阅 ASP.NET Core Blazor 启动

若要禁用压缩,请将 BlazorEnableCompression MSBuild 属性添加到应用的项目文件,并将值设置为 false

<PropertyGroup>
  <BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>

可在命令行界面中使用以下语法将 BlazorEnableCompression 属性传递给 dotnet publish 命令:

dotnet publish -p:BlazorEnableCompression=false

重写 URL,以实现正确路由

在 Blazor WebAssembly 应用中路由对页组件的请求不如在 Blazor Server 托管应用中路由请求直接。 假设有一个具有两个组件的 Blazor WebAssembly 应用:

  • Main.razor:在应用的根目录处加载,并包含指向 About 组件 (href="About") 的链接。
  • About.razorAbout 组件。

使用浏览器的地址栏(例如,https://www.contoso.com/)请求应用的默认文档:

  1. 浏览器发出请求。
  2. 返回默认页,通常为 index.html
  3. index.html 启动应用。
  4. Blazor 的路由器进行加载,然后呈现 Razor Main 组件。

在 Main 页中,选择指向 About 组件的链接适用于客户端,因为 Blazor 路由器阻止浏览器在 Internet 上发出请求,针对 About 转到 www.contoso.com,并为呈现的 About 组件本身提供服务。 针对 Blazor WebAssembly 应用中的内部终结点的所有请求,工作原理都相同:这些请求不会触发对 Internet 上的服务器托管资源的基于浏览器的请求。 路由器将在内部处理请求。

如果针对 www.contoso.com/About 使用浏览器的地址栏发出请求,则请求会失败。 应用的 Internet 主机上不存在此类资源,所以返回的是“404 - 找不到”响应。

由于浏览器针对客户端页面请求基于 Internet 的主机,因此 Web 服务器和托管服务必须将对服务器上非物理方式资源的所有请求重写为 index.html 页。 如果返回 index.html,应用的 Blazor 路由器将接管工作并使用正确的资源响应。

部署到 IIS 服务器时,可以将 URL 重写模块与应用的已发布 web.config 文件一起使用。 有关详细信息,请参阅 IIS 部分。

使用 ASP.NET Core 进行托管部署

托管部署通过在 Web 服务器上运行的 ASP.NET Core 应用为浏览器提供 Blazor WebAssembly 应用。

客户端 Blazor WebAssembly 应用与服务器应用的其他任何静态 Web 资产一起发布到服务器应用的 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 文件夹。 这两个应用一起部署。 需要能够托管 ASP.NET Core 应用的 Web 服务器。 对于托管部署,Visual Studio 会在选择 Hosted 选项(使用 dotnet new 命令时为 -ho|--hosted)的情况下,包含 Blazor WebAssembly 应用项目模板(使用 dotnet new 命令时为 blazorwasm 模板) 。

有关详细信息,请参阅以下文章:

具有多个 Blazor WebAssembly 应用的托管部署

应用配置

托管的 Blazor 解决方案可为多个 Blazor WebAssembly 应用提供服务。

备注

本节中的示例引用 Visual Studio 解决方案的用法,但多个客户端应用在托管的 Blazor WebAssembly 程序方案中运行时,不需要使用 Visual Studio 和 Visual Studio 解决方案。 如果你未在使用 Visual Studio,则忽略 {SOLUTION NAME}.sln 文件以及为 Visual Studio 创建的任何其他文件。

如下示例中:

  • 初始(第一个)客户端应用是通过 Blazor WebAssembly 项目模板创建的解决方案的默认客户端项目。 第一个客户端应用可在浏览器中通过端口 5001 上或主机为 firstapp.com 的 URL /FirstApp 访问。
  • 向解决方案添加第二个客户端应用:SecondBlazorApp.Client。 第二个客户端应用可在浏览器中通过端口 5002 上或主机为 secondapp.com 的 URL /SecondApp 访问。

使用现有的托管 Blazor 解决方案,或者从 Blazor 托管的项目模板创建一个新解决方案:

  • 在客户端应用的项目文件中,在 <PropertyGroup> 中添加一个值为 FirstApp<StaticWebAssetBasePath> 属性,以设置项目静态资产的基路径:

    <PropertyGroup>
      ...
      <StaticWebAssetBasePath>FirstApp</StaticWebAssetBasePath>
    </PropertyGroup>
    
  • 向解决方案添加第二个客户端应用:

    • 向解决方案的文件夹添加一个名为 SecondClient 的文件夹。 在添加 SecondClient 文件夹后,通过项目模板创建的解决方案文件夹包含以下解决方案文件和文件夹:

      • Client(文件夹)
      • SecondClient(文件夹)
      • Server(文件夹)
      • Shared(文件夹)
      • {SOLUTION NAME}.sln(文件)

      占位符 {SOLUTION NAME} 是解决方案的名称。

    • 在 Blazor WebAssembly 项目模板的 SecondClient 文件夹中创建一个名为 SecondBlazorApp.Client 的 Blazor WebAssembly 应用。

    • SecondBlazorApp.Client 应用的项目文件中,执行以下操作:

      • <PropertyGroup> 添加一个值为 SecondApp<StaticWebAssetBasePath> 属性:

        <PropertyGroup>
          ...
          <StaticWebAssetBasePath>SecondApp</StaticWebAssetBasePath>
        </PropertyGroup>
        
      • Shared 项目添加一个项目引用:

        <ItemGroup>
          <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
        </ItemGroup>
        

        占位符 {SOLUTION NAME} 是解决方案的名称。

  • 在服务器应用的项目文件中,为新增的 SecondBlazorApp.Client 客户端应用创建一个项目引用:

    <ItemGroup>
      <ProjectReference Include="..\Client\{SOLUTION NAME}.Client.csproj" />
      <ProjectReference Include="..\SecondClient\SecondBlazorApp.Client.csproj" />
      <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
    </ItemGroup>
    

    占位符 {SOLUTION NAME} 是解决方案的名称。

  • 在服务器应用的 Properties/launchSettings.json 文件中,配置 Kestrel 配置文件 ({SOLUTION NAME}.Server) 的 applicationUrl,以访问位于端口 5001 和 5002 的客户端应用:

    "applicationUrl": "https://localhost:5001;https://localhost:5002",
    
  • 在服务器应用的 Startup.Configure 方法 (Startup.cs) 中,删除在调用 UseHttpsRedirection 后显示的以下行:

    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllers();
        endpoints.MapFallbackToFile("index.html");
    });
    

    添加将请求映射到客户端应用的中间件。 下面的示例将中间件配置为在以下情况下运行:

    • 原始客户端应用的请求端口为 5001,或新增的客户端应用的请求端口为 5002。

    • 原始客户端应用的请求主机为 firstapp.com,或新增的客户端应用的请求主机为 secondapp.com

      备注

      本部分中显示的示例需要其他配置以实现以下目的:

      • 访问示例主机域 firstapp.comsecondapp.com 上的应用。
      • 获取客户端应用的证书以启用 TLS 安全性 (HTTPS)。

      所需的配置不在本文内容范围内,并且取决于解决方案的托管方式。 有关详细信息,请参阅托管和部署文章

    将以下代码放在先前已删除行的位置:

    app.MapWhen(ctx => ctx.Request.Host.Port == 5001 || 
        ctx.Request.Host.Equals("firstapp.com"), first =>
    {
        first.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/FirstApp" + ctx.Request.Path;
            return nxt();
        });
    
        first.UseBlazorFrameworkFiles("/FirstApp");
        first.UseStaticFiles();
        first.UseStaticFiles("/FirstApp");
        first.UseRouting();
    
        first.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/FirstApp/{*path:nonfile}", 
                "FirstApp/index.html");
        });
    });
    
    app.MapWhen(ctx => ctx.Request.Host.Port == 5002 || 
        ctx.Request.Host.Equals("secondapp.com"), second =>
    {
        second.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/SecondApp" + ctx.Request.Path;
            return nxt();
        });
    
        second.UseBlazorFrameworkFiles("/SecondApp");
        second.UseStaticFiles();
        second.UseStaticFiles("/SecondApp");
        second.UseRouting();
    
        second.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/SecondApp/{*path:nonfile}", 
                "SecondApp/index.html");
        });
    });
    
  • 在服务器应用的天气预报控制器 (Controllers/WeatherForecastController.cs) 中,将到 WeatherForecastController 的现有路由 ([Route("[controller]")]) 替换为以下路由:

    [Route("FirstApp/[controller]")]
    [Route("SecondApp/[controller]")]
    

    先前添加到服务器应用的 Startup.Configure 方法的中间件会将向 /WeatherForecast 发出的传入请求修改为 /FirstApp/WeatherForecast/SecondApp/WeatherForecast,具体取决于端口 (5001/5002) 或域 (firstapp.com/secondapp.com)。 为了将天气数据从服务器应用返回到客户端应用,需要前面的控制器路由。

多个 Blazor WebAssembly 应用的静态资产和类库

使用以下方法来引用静态资产:

  • 如果资产位于客户端应用的 wwwroot 文件夹中,请正常提供路径:

    <img alt="..." src="/{PATH AND FILE NAME}" />
    

    {PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。

  • 如果资产位于 Razor 类库 (RCL)wwwroot 文件夹中,请按照 ASP.NET Core 的类库中的可重用 Razor UI 中的指导,在客户端应用中引用静态资产:

    <img alt="..." src="_content/{PACKAGE ID}/{PATH AND FILE NAME}" />
    

    {PACKAGE ID} 占位符是库的包 ID。 如果项目文件中没有指定 <PackageId>,则包 ID 默认为项目的程序集名称。 {PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。

有关 RCL 的详细信息,请参阅:

独立部署

独立部署将 Blazor WebAssembly 应用作为客户端直接请求的一组静态文件提供。 任何静态文件服务器均可提供 Blazor 应用。

独立部署资产将发布到 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 文件夹。

Azure 应用服务

可以将 Blazor WebAssembly 应用部署到 Windows 上的 Azure 应用服务,该服务在 IIS 上托管应用。

目前不支持将独立的 Blazor WebAssembly 应用部署到适用于 Linux 的 Azure 应用服务。 目前无法提供用于托管应用的 Linux 服务器映像。 正在进行此工作以支持此场景。

Azure 静态 Web 应用

有关详细信息,请参阅教程:在 Azure Static Web Apps 中使用 Blazor 生成静态 Web 应用

IIS

IIS 是适用于 Blazor 应用的强大静态文件服务器。 要配置 IIS 以托管 Blazor,请参阅在 IIS 上生成静态网站

/bin/Release/{TARGET FRAMEWORK}/publish 文件夹中已创建发布的资产。 在 Web 服务器或托管服务上托管 publish 文件夹的内容。

web.config

发布 Blazor 项目时,将使用以下 IIS 配置创建 web.config 文件:

  • 对以下文件扩展名设置 MIME 类型:
    • .dll: application/octet-stream
    • .json: application/json
    • .wasm: application/wasm
    • .woff: application/font-woff
    • .woff2: application/font-woff
  • 对以下 MIME 类型启用 HTTP 压缩:
    • application/octet-stream
    • application/wasm
  • 建立 URL 重写模块规则:
    • 提供应用的静态资产所驻留的子目录 (wwwroot/{PATH REQUESTED})。
    • 创建 SPA 回退路由,以便非文件资产请求能够重定向到应用的静态资产文件夹中的默认文档 (wwwroot/index.html)。

使用自定义 web.config

要使用自定义 web.config 文件,请将自定义 web.config 文件置于项目文件夹的根目录下。 将项目配置为使用应用项目文件中的 PublishIISAssets 发布特定于 IIS 的资源,并发布项目:

<PropertyGroup>
  <PublishIISAssets>true</PublishIISAssets>
</PropertyGroup>

安装 URL 重写模块

重写 URL 必须使用 URL 重写模块。 此模块默认不安装,且不适用于安装为 Web 服务器 (IIS) 角色服务功能。 必须从 IIS 网站下载该模块。 使用 Web 平台安装程序安装模块:

  1. 以本地方式导航到 URL 重写模块下载页。 对于英语版本,请选择“WebPI”以下载 WebPI 安装程序。 对于其他语言,请选择适当的服务器体系结构 (x86/x64) 下载安装程序。
  2. 将安装程序复制到服务器。 运行安装程序。 选择“安装”按钮,并接受许可条款。 安装完成后无需重启服务器。

配置网站

将网站的物理路径设置为应用的文件夹。 该文件夹包含:

  • web.config 文件,IIS 使用该文件配置网站,包括所需的重定向规则和文件内容类型。
  • 应用的静态资产文件夹。

作为 IIS 子应用托管

如果独立应用作为 IIS 子应用托管,请执行下列任一操作:

  • 禁用继承的 ASP.NET Core 模块处理程序。

    通过向文件添加 <handlers> 部分,删除 Blazor 应用的已发布 web.config 文件中的处理程序:

    <handlers>
      <remove name="aspNetCore" />
    </handlers>
    
  • 通过使用 <location> 元素并且将 inheritInChildApplications 设置为 false,禁止继承根(父级)应用的 <system.webServer> 部分:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <location path="." inheritInChildApplications="false">
        <system.webServer>
          <handlers>
            <add name="aspNetCore" ... />
          </handlers>
          <aspNetCore ... />
        </system.webServer>
      </location>
    </configuration>
    

配置应用的基路径外,还需删除处理程序或禁用继承。 在 IIS 中配置子应用时,在应用的 index.html 文件中将应用基路径设置为 IIS 别名。

Brotli 和 Gzip 压缩

本部分仅适用于独立的 Blazor WebAssembly 应用。托管的 Blazor 应用使用默认的 ASP.NET Core 应用 web.config 文件,而不使用本部分中所链接的文件。

通过 web.config 可将 IIS 配置为提供独立 Blazor WebAssembly 应用的 Brotli 或 Gzip 压缩的 Blazor 资产。 若要查看示例配置文件,请参阅 web.config

在以下情况下,可能需要进一步配置示例 web.config 文件:

  • 应用的规范具有以下任意一个要求:
    • 提供不是由示例 web.config 文件配置的压缩文件。
    • 采用非压缩格式提供由示例 web.config 文件配置的压缩文件。
  • 服务器的 IIS 配置(例如 applicationHost.config)提供了服务器级 IIS 默认值。 根据服务器级别配置,应用可能要求 IIS 配置不同于示例 web.config 文件所包含的配置。

疑难解答

如果你看到“500 - 内部服务器错误”,且 IIS 管理器在尝试访问网站配置时抛出错误,请确认是否已安装 URL 重写模块。 如果未安装该模块,则 IIS 无法分析 web.config 文件。 这可以防止 IIS 管理器加载网站配置,并防止网站对 Blazor 的静态文件提供服务。

有关排查部署到 IIS 的问题的详细信息,请参阅 对 Azure 应用服务和 IIS 上的 ASP.NET Core 进行故障排除

Azure 存储

Azure 存储静态文件承载允许无服务器的 Blazor 应用承载。 支持自定义域名、Azure 内容分发网络 (CDN) 以及 HTTPS。

为存储帐户上的静态网站承载启用 blob 服务时:

  • 设置“索引文档名称”到 index.html
  • 设置“错误文档路径”到 index.html。 Razor 组件和其他非文件终结点不会驻留在由 blob 服务存储的静态内容中的物理路径中。 当收到 Blazor 路由器应处理的对这些资源之一的请求时,由 blob 服务生成的“404 - 未找到”错误会将此请求路由到“错误文档路径”。 返回 index.html blob,Blazor 路由器会加载并处理此路径。

如果由于文件的 Content-Type 标头中的 MIME 类型不正确,导致在运行时未加载文件,请执行以下任一操作:

  • 配置工具,用于在部署文件时设置正确的 MIME 类型(Content-Type 标头)。

  • 在部署应用后更改文件的 MIME 类型(Content-Type 标头)。

    在每个文件的存储资源管理器(Azure 门户)中,执行以下操作:

    1. 右键单击该文件并选择“属性”。
    2. 设置“ContentType”并选择“保存”按钮 。

有关更多信息,请参阅 Azure 存储中的静态网站承载

Nginx

以下 nginx.conf 文件已简化,以展示如何配置 Nginx,以便每当在磁盘上找不到相应文件时,就发送 index.html 文件。

events { }
http {
    server {
        listen 80;

        location / {
            root      /usr/share/nginx/html;
            try_files $uri $uri/ /index.html =404;
        }
    }
}

使用 limit_req 设置 NGINX 突发速率限制时,Blazor WebAssembly 应用可能需要一个较大的 burst 参数值来容纳应用发出的请求数。 首先,将值设置为不低于 60:

http {
    server {
        ...

        location / {
            ...

            limit_req zone=one burst=60 nodelay;
        }
    }
}

如果浏览器开发人员工具或网络流量工具指示请求收到“503 - 服务不可用”状态代码,则将该值调高。

有关生产 Nginx Web 服务器配置的详细信息,请参阅 Creating NGINX Plus and NGINX Configuration Files(创建 NGINX 增强版和 NGINX 配置文件)。

Apache

若要将 Blazor WebAssembly 应用部署到 CentOS 7 或更高版本,请执行以下操作:

  1. 创建 Apache 配置文件。 下面的示例展示了一个简化的配置文件 (blazorapp.config):

    <VirtualHost *:80>
        ServerName www.example.com
        ServerAlias *.example.com
    
        DocumentRoot "/var/www/blazorapp"
        ErrorDocument 404 /index.html
    
        AddType application/wasm .wasm
        AddType application/octet-stream .dll
    
        <Directory "/var/www/blazorapp">
            Options -Indexes
            AllowOverride None
        </Directory>
    
        <IfModule mod_deflate.c>
            AddOutputFilterByType DEFLATE text/css
            AddOutputFilterByType DEFLATE application/javascript
            AddOutputFilterByType DEFLATE text/html
            AddOutputFilterByType DEFLATE application/octet-stream
            AddOutputFilterByType DEFLATE application/wasm
            <IfModule mod_setenvif.c>
          BrowserMatch ^Mozilla/4 gzip-only-text/html
          BrowserMatch ^Mozilla/4.0[678] no-gzip
          BrowserMatch bMSIE !no-gzip !gzip-only-text/html
      </IfModule>
        </IfModule>
    
        ErrorLog /var/log/httpd/blazorapp-error.log
        CustomLog /var/log/httpd/blazorapp-access.log common
    </VirtualHost>
    
  2. 将 Apache 配置文件放入 /etc/httpd/conf.d/ 目录(这是 CentOS 7 中的默认 Apache 配置目录)。

  3. 将应用的文件放入 /var/www/blazorapp 目录(配置文件中特定于 DocumentRoot 的位置)。

  4. 重启 Apache 服务。

有关详细信息,请参阅 mod_mimemod_deflate

GitHub 页

要处理 URL 重写,请使用脚本添加 wwwroot/404.html 文件,该脚本可处理到 index.html 页的重定向请求。 有关示例,请参阅 SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库

如果使用项目站点而非组织站点,请在 wwwroot/index.html 中更新 <base> 标记。 将 href 属性值设置为,包含尾部斜杠的 GitHub 存储库名称(例如,/my-repository/)。 在 SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库中,将在发布时通过 .github/workflows/main.yml 配置文件更新基本 href

备注

SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库 不归 .NET Foundation 或 Microsoft 所有,也不由它们提供维护和支持。

主机配置值

在开发环境中,Blazor WebAssembly 应用可以在运行时接受以下主机配置值作为命令行参数。

内容根

--contentroot 参数设置包含应用内容文件的目录的绝对路径(内容根目录)。 在下面的示例中,/content-root-path 是应用的内容根路径。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --contentroot=/content-root-path
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--contentroot=/content-root-path"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --contentroot=/content-root-path
    

基路径

--pathbase 参数可设置使用非根相对 URL 路径本地运行的应用的应用基路径(将 <base> 标记 href 针对暂存和生产设置为 / 之外的路径)。 在下面的示例中,/relative-URL-path 是应用的基路径。 有关详细信息,请参阅应用基路径

重要

不同于向 href 标记的 <base> 提供的路径,传递 --pathbase 参数值时不包括尾部反斜杠 (/)。 如果在 <base> 标记中以 <base href="/CoolApp/"> 形式(包括尾部反斜杠)提供应用基路径,则以 --pathbase=/CoolApp 形式(无尾部反斜杠)传递命令行参数值。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --pathbase=/relative-URL-path
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--pathbase=/relative-URL-path"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --pathbase=/relative-URL-path
    

URL

--urls 参数设置 IP 地址或主机地址,其中包含侦听请求的端口和协议。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --urls=http://127.0.0.1:0
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--urls=http://127.0.0.1:0"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --urls=http://127.0.0.1:0
    

配置裁边器

Blazor 对每个发布版本执行中间语言 (IL) 剪裁,以从输出程序集中删除不必要的 IL。 有关详细信息,请参阅 配置适用于 ASP.NET Core Blazor 的裁边器

更改 DLL 文件的文件扩展名

如果需要更改应用的已发布 .dll 文件的文件扩展名,请按照本部分中的指导进行操作。

发布应用后,使用 shell 脚本或 DevOps 生成管道将 .dll 文件重命名,以使用其他文件扩展名。 将 .dll 文件的目标位置设为应用的已发布输出的 wwwroot 目录中(例如 {CONTENT ROOT}/bin/Release/netstandard2.1/publish/wwwroot)。

在下面的示例中,重命名 .dll 文件,以使用 .bin 文件扩展名。

在 Windows 上:

dir .\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content .\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content .\_framework\blazor.boot.json

如果服务工作进程资产也在使用中,请添加以下命令:

((Get-Content .\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content .\service-worker-assets.js

在 Linux 或 macOS 上:

for f in _framework/_bin/*; do mv "$f" "`echo $f | sed -e 's/\.dll/.bin/g'`"; done
sed -i 's/\.dll"/.bin"/g' _framework/blazor.boot.json

如果服务工作进程资产也在使用中,请添加以下命令:

sed -i 's/\.dll"/.bin"/g' service-worker-assets.js

要使用不同于 .bin 的其他文件扩展名,请在前面的命令中替换 .bin

若要处理压缩的 blazor.boot.json.gzblazor.boot.json.br 文件,请采用以下方法之一:

  • 删除压缩的 blazor.boot.json.gzblazor.boot.json.br 文件。 此方法禁用压缩。
  • 重新压缩更新后的 blazor.boot.json 文件。

上述指导也适用于正在使用服务工作进程资产的情况。 删除或重新压缩 wwwroot/service-worker-assets.js.brwwwroot/service-worker-assets.js.gz。 否则,浏览器中的文件完整性检查将失败。

以下 Windows 示例使用项目根目录中的 PowerShell 脚本。

ChangeDLLExtensions.ps1::

param([string]$filepath,[string]$tfm)
dir $filepath\bin\Release\$tfm\wwwroot\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json
Remove-Item $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json.gz

如果服务工作进程资产也在使用中,请添加以下命令:

((Get-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js

在项目文件中,在发布应用后运行脚本:

<Target Name="ChangeDLLFileExtensions" AfterTargets="Publish" Condition="'$(Configuration)'=='Release'">
  <Exec Command="powershell.exe -command &quot;&amp; { .\ChangeDLLExtensions.ps1 '$(SolutionDir)' '$(TargetFramework)'}&quot;" />
</Target>

备注

重命名和延迟加载相同的程序集时,请参阅 在 ASP.NET Core Blazor WebAssembly 中延迟加载程序集 中的指南。

解决完整性检查失败

当 Blazor WebAssembly 下载应用的启动文件时,它会指示浏览器对响应执行完整性检查。 它使用 blazor.boot.json 文件中的信息为 .dll.wasm 和其他文件指定预期的 SHA-256 哈希值。 这对以下原因有所帮助:

  • 它可确保不会出现加载不一致文件集的风险,例如,在用户正在下载应用程序文件时将新部署应用到 Web 服务器的情况。 不一致的文件可能导致未定义的行为。
  • 它可确保用户的浏览器从不缓存不一致或无效的响应,这些响应可能会阻止他们启动应用(即使他们手动刷新了页面也是如此)。
  • 它可以安全地缓存响应,甚至无需检查服务器端更改,直到预期的 SHA-256 哈希本身发生更改,因此,后续页面加载需要较少的请求即可快速完成。

如果你的 Web 服务器返回的响应与预期的 SHA-256 哈希不匹配,你将看到类似于以下内容的错误显示在浏览器的开发人员控制台中:

未能使用计算出的 SHA-256 完整性“IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY=”在资源“https://myapp.example.com/_framework/MyBlazor App.dll”的“integrity”属性中找到有效摘要。 资源已被阻止。

在大多数情况下,这不是完整性检查本身的问题。 相反,它表示存在其他问题,并且完整性检查会警告你其他问题。

诊断完整性问题

生成应用时,生成的 blazor.boot.json 清单将描述生成输出生成时启动资源(例如,.dll.wasm 和其他文件)的 SHA-256 哈希。 只要 blazor.boot.json 中的 SHA-256 哈希与传递到浏览器的文件相匹配,完整性检查就会通过。

此失败的常见原因包括:

  • Web 服务器的响应是一个错误(例如,“404 - 找不到”或“500 - 内部服务器错误”),而不是浏览器所请求的文件。 浏览器会将其报告为完整性检查失败,而不是响应失败。
  • 在文件生成和传递到浏览器之间已更改文件的内容。 下面可能会发生这种情况:
    • 你或生成工具手动修改生成输出的情况。
    • 部署过程的某个方面修改了文件的情况。 例如,在使用基于 Git 的部署机制时,请记住,如果你在 Windows 上提交文件并在 Linux 上检查它们,则 Git 会以透明方式将 Windows 样式的行尾转换为 Unix 样式的行尾。 更改文件行尾将更改 SHA-256 哈希。 若要避免此问题,请考虑使用 .gitattributes 将生成项目视为 binary 文件
    • Web 服务器在提供文件内容的过程中对其进行修改。 例如,某些内容分发网络 (CDN) 会自动尝试缩小 HTML,从而对其进行修改。 可能需要禁用此类功能。

若要诊断哪些功能适用于你的情况,请执行执行操作:

  1. 通过读取错误消息来记下哪个文件触发错误。
  2. 打开浏览器的开发人员工具,然后在“网络”选项卡中查找。如有必要,请重新加载页面以查看请求和响应的列表。 在该列表中查找触发错误的文件。
  3. 检查响应中的 HTTP 状态代码。 如果服务器返回除“200 - 正常”(或其他 2xx 状态代码)以外的任何内容,则需要诊断服务器端问题。 例如,状态代码 403 表示存在授权问题,而状态代码 500 表示服务器以未指定的方式失败。 请参阅服务器端日志以诊断和修复应用。
  4. 如果资源的状态代码为“200 - 正常”,请在浏览器的开发人员工具中查看响应内容,并检查内容是否与预期的数据匹配。 例如,常见问题是错误配置了路由,因此请求甚至返回其他文件的 index.html 数据。 请确保对 .wasm 请求的响应是 WebAssembly 二进制文件,对 .dll 请求的响应是 .NET 程序集二进制文件。 如果不是,则需要诊断服务器端路由问题。
  5. 搜索以验证应用的已发布和已部署输出,并提供完整性 PowerShell 脚本故障排除

如果确认服务器返回看似正确的数据,则必须在生成文件和传递文件之间修改内容。 若要对此进行调查,请执行以下操作:

  • 如果在生成文件后修改文件,请检查生成工具链和部署机制。 例如,在 Git 转换文件行尾时,如前所述。
  • 如设置为动态修改响应(例如,尝试缩小 HTML),请检查 Web 服务器或 CDN 配置。 Web 服务器可以实现 HTTP 压缩(例如,返回 content-encoding: brcontent-encoding: gzip),因为这不会影响解压缩后的结果。 但是,Web 服务器不可以修改未压缩的数据。

完整性 PowerShell 脚本故障排除

使用 integrity.ps1 PowerShell 脚本来验证已发布和已部署的 Blazor 应用。 当应用出现 Blazor 框架无法识别的完整性问题时,该脚本将作为起点提供给 PowerShell Core 6。 应用可能需要自定义脚本,包括在 6.2.7 版之后的 PowerShell 版本上运行时。

此脚本将检查 publish 文件夹中的文件,并从部署的应用中下载这些文件,以检测包含完整性哈希的不同清单中的问题。 这些检查应检测最常见的问题:

  • 你修改了已发布的输出中的文件,但未实现它。
  • 应用未正确部署到部署目标,或者在部署目标的环境中发生了更改。
  • 部署的应用与发布应用的输出之间存在差异。

在 PowerShell 命令行中使用以下命令调用脚本:

.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER}

占位符:

  • {BASE URL}:已部署的应用的 URL。
  • {PUBLISH OUTPUT FOLDER}:应用的 publish 文件夹的路径,或已发布的用于部署应用的位置。

备注

克隆 dotnet/AspNetCore.Docs GitHub 存储库时,integrity.ps1 脚本可能会被 Bitdefender 或系统上存在的另一个病毒扫描程序隔离。 通常,该文件被病毒扫描程序的启发式扫描技术捕获,该技术只查找文件中可能指示存在恶意软件的模式。 若要防止病毒扫描程序隔离该文件,请在克隆存储库之前向病毒扫描程序添加一个例外。 以下示例是 Windows 系统上脚本的一个典型路径。 可以根据需要为其他系统调整路径。 占位符 {USER} 是用户的路径段。

C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1

警告:创建病毒扫描程序例外是危险操作,只有在你确定文件是安全的情况下才可以进行。

将文件的校验和与有效校验和值进行比较并不能保证文件的安全,但以维护校验和值的方式修改文件对于恶意用户来说并不简单。 因此,校验和作为一种常规安全方法很有用。 将本地 integrity.ps1 文件的校验和与以下值之一进行比较:

  • SHA256:6b0dc7aba5d8489136bb2969036432597615b11b4e432535e173ca077a0449c4
  • MD5:f0c800a4c72604bd47f3c19f5f0bb4f4

通过以下命令在 Windows OS 上获取文件的校验和。 为 {PATH AND FILE NAME} 占位符提供路径和文件名,并指明为 {SHA512|MD5} 占位符生成的校验和类型,可以是 SHA256,也可以是 MD5

CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5}

如果你有任何理由担心校验和验证在你的环境中不够安全,请咨询组织的安全领导以获得指导。

有关详细信息,请参阅了解恶意软件和其他威胁

禁用非 PWA 应用的完整性检查

在大多数情况下,不要禁用完整性检查。 禁用完整性检查并不能解决导致意外响应的根本问题,并且会导致丢失前面列出的权益。

在某些情况下,Web 服务器无法用于返回一致的响应,但别无选择,只能禁用完整性检查。 若要禁用完整性检查,请将以下内容添加到 Blazor WebAssembly 项目的 .csproj 文件中的属性组:

<BlazorCacheBootResources>false</BlazorCacheBootResources>

BlazorCacheBootResources 还会根据 SHA-256 哈希禁用 Blazor 缓存 .dll.wasm 和其他文件的默认行为,因为属性指示无法依靠 SHA-256 哈希来确保正确性。 即使有此设置,浏览器的普通 HTTP 缓存仍可能会缓存这些文件,但是否发生这种情况取决于你的 Web 服务器配置和它所提供的 cache-control 标头。

备注

BlazorCacheBootResources 属性不会禁用渐进式 Web 应用程序 (PWA) 的完整性检查。 有关 PWA 的相关指南,请参阅禁用 PWA 的完整性检查部分。

禁用 PWA 的完整性检查

Blazor 的渐进式 Web 应用程序 (PWA) 模板包含建议的 service-worker.published.js 文件,该文件负责获取和存储应用程序文件以供脱机使用。 这是普通应用启动机制的独立进程,具有其自己单独的完整性检查逻辑。

service-worker.published.js 文件中,出现以下行:

.map(asset => new Request(asset.url, { integrity: asset.hash }));

若要禁用完整性检查,请通过将该行更改为以下行来删除 integrity 参数:

.map(asset => new Request(asset.url));

同样,禁用完整性检查意味着会丢失完整性检查提供的安全保证。 例如,如果用户的浏览器在你部署新版本的那一刻缓存应用,则会存在风险,它可能会缓存旧部署中的某些文件和新部署中的某些文件。 如果发生这种情况,则在部署进一步更新之前,应用程序会在中断状态下停滞。

利用 Blazor WebAssembly 托管模型

  • 将 Blazor 应用、其依赖项及 .NET 运行时并行下载到浏览器。
  • 应用将在浏览器线程中直接执行。

支持以下部署策略:

  • Blazor 应用由 ASP.NET Core 应用提供服务。 使用 ASP.NET Core 进行托管部署部分中介绍了此策略。
  • Blazor 应用位于静态托管 Web 服务器或服务中,其中未使用 .NET 对 Blazor 应用提供服务。 独立部署部分介绍了此策略,包括有关将 Blazor WebAssembly 应用作为 IIS 子应用托管的信息。

自定义加载启动资源的方式

自定义如何使用 loadBootResource API 加载启动资源。 有关详细信息,请参阅 ASP.NET Core Blazor 启动

压缩

发布 Blazor WebAssembly 应用时,将在发布过程中对输出内容进行静态压缩,从而减小应用的大小,并免去运行时压缩的开销。 使用以下压缩算法:

Blazor 依赖于主机提供适当的压缩文件。 使用 ASP.NET Core 托管项目时,主机项目能够执行内容协商并提供静态压缩文件。 托管 Blazor WebAssembly 独立应用时,可能需要额外的工作来确保提供静态压缩文件:

  • 有关 IIS web.config 压缩配置,请参阅 IIS:Brotli 和 Gzip 压缩 部分。

  • 如果在不支持静态压缩文件内容协商的静态托管解决方案(例如 GitHub 页面)上进行托管,请考虑配置应用以提取和解码 Brotli 压缩文件:

    • google/brotli GitHub repository 中获取 JavaScript Brotli 解码器。 缩小的解码器文件被命名为 decode.min.js,并且位于存储库的 js 文件夹中。

      备注

      如果 decode.js 脚本的缩小版本 (decode.min.js) 失败,请尝试使用缩小版本 (decode.js)。

    • 更新应用以使用解码器。

      wwwroot/index.html 文件中,在 Blazor 的 <script> 标记上将 autostart 设置为 false

      <script src="_framework/blazor.webassembly.js" autostart="false"></script>
      

      在 Blazor 的 <script> 标记之后和结束 </body> 标记之前,添加以下 JavaScript 代码 <script> 块:

      <script type="module">
        import { BrotliDecode } from './decode.min.js';
        Blazor.start({
          loadBootResource: function (type, name, defaultUri, integrity) {
            if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
              return (async function () {
                const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
                if (!response.ok) {
                  throw new Error(response.statusText);
                }
                const originalResponseBuffer = await response.arrayBuffer();
                const originalResponseArray = new Int8Array(originalResponseBuffer);
                const decompressedResponseArray = BrotliDecode(originalResponseArray);
                const contentType = type === 
                  'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
                return new Response(decompressedResponseArray, 
                  { headers: { 'content-type': contentType } });
              })();
            }
          }
        });
      </script>
      

      有关加载启动资源的详细信息,请参阅 ASP.NET Core Blazor 启动

若要禁用压缩,请将 BlazorEnableCompression MSBuild 属性添加到应用的项目文件,并将值设置为 false

<PropertyGroup>
  <BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>

可在命令行界面中使用以下语法将 BlazorEnableCompression 属性传递给 dotnet publish 命令:

dotnet publish -p:BlazorEnableCompression=false

重写 URL,以实现正确路由

在 Blazor WebAssembly 应用中路由对页组件的请求不如在 Blazor Server 托管应用中路由请求直接。 假设有一个具有两个组件的 Blazor WebAssembly 应用:

  • Main.razor:在应用的根目录处加载,并包含指向 About 组件 (href="About") 的链接。
  • About.razorAbout 组件。

使用浏览器的地址栏(例如,https://www.contoso.com/)请求应用的默认文档:

  1. 浏览器发出请求。
  2. 返回默认页,通常为 index.html
  3. index.html 启动应用。
  4. Blazor 的路由器进行加载,然后呈现 Razor Main 组件。

在 Main 页中,选择指向 About 组件的链接适用于客户端,因为 Blazor 路由器阻止浏览器在 Internet 上发出请求,针对 About 转到 www.contoso.com,并为呈现的 About 组件本身提供服务。 针对 Blazor WebAssembly 应用中的内部终结点的所有请求,工作原理都相同:这些请求不会触发对 Internet 上的服务器托管资源的基于浏览器的请求。 路由器将在内部处理请求。

如果针对 www.contoso.com/About 使用浏览器的地址栏发出请求,则请求会失败。 应用的 Internet 主机上不存在此类资源,所以返回的是“404 - 找不到”响应。

由于浏览器针对客户端页面请求基于 Internet 的主机,因此 Web 服务器和托管服务必须将对服务器上非物理方式资源的所有请求重写为 index.html 页。 如果返回 index.html,应用的 Blazor 路由器将接管工作并使用正确的资源响应。

部署到 IIS 服务器时,可以将 URL 重写模块与应用的已发布 web.config 文件一起使用。 有关详细信息,请参阅 IIS 部分。

使用 ASP.NET Core 进行托管部署

托管部署通过在 Web 服务器上运行的 ASP.NET Core 应用为浏览器提供 Blazor WebAssembly 应用。

客户端 Blazor WebAssembly 应用与服务器应用的其他任何静态 Web 资产一起发布到服务器应用的 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 文件夹。 这两个应用一起部署。 需要能够托管 ASP.NET Core 应用的 Web 服务器。 对于托管部署,Visual Studio 会在选择 Hosted 选项(使用 dotnet new 命令时为 -ho|--hosted)的情况下,包含 Blazor WebAssembly 应用项目模板(使用 dotnet new 命令时为 blazorwasm 模板) 。

有关详细信息,请参阅以下文章:

具有多个 Blazor WebAssembly 应用的托管部署

应用配置

托管的 Blazor 解决方案可为多个 Blazor WebAssembly 应用提供服务。

备注

本节中的示例引用 Visual Studio 解决方案的用法,但多个客户端应用在托管的 Blazor WebAssembly 程序方案中运行时,不需要使用 Visual Studio 和 Visual Studio 解决方案。 如果你未在使用 Visual Studio,则忽略 {SOLUTION NAME}.sln 文件以及为 Visual Studio 创建的任何其他文件。

如下示例中:

  • 初始(第一个)客户端应用是通过 Blazor WebAssembly 项目模板创建的解决方案的默认客户端项目。 第一个客户端应用可在浏览器中通过端口 5001 上或主机为 firstapp.com 的 URL /FirstApp 访问。
  • 向解决方案添加第二个客户端应用:SecondBlazorApp.Client。 第二个客户端应用可在浏览器中通过端口 5002 上或主机为 secondapp.com 的 URL /SecondApp 访问。

使用现有的托管 Blazor 解决方案,或者从 Blazor 托管的项目模板创建一个新解决方案:

  • 在客户端应用的项目文件中,在 <PropertyGroup> 中添加一个值为 FirstApp<StaticWebAssetBasePath> 属性,以设置项目静态资产的基路径:

    <PropertyGroup>
      ...
      <StaticWebAssetBasePath>FirstApp</StaticWebAssetBasePath>
    </PropertyGroup>
    
  • 向解决方案添加第二个客户端应用:

    • 向解决方案的文件夹添加一个名为 SecondClient 的文件夹。 在添加 SecondClient 文件夹后,通过项目模板创建的解决方案文件夹包含以下解决方案文件和文件夹:

      • Client(文件夹)
      • SecondClient(文件夹)
      • Server(文件夹)
      • Shared(文件夹)
      • {SOLUTION NAME}.sln(文件)

      占位符 {SOLUTION NAME} 是解决方案的名称。

    • 在 Blazor WebAssembly 项目模板的 SecondClient 文件夹中创建一个名为 SecondBlazorApp.Client 的 Blazor WebAssembly 应用。

    • SecondBlazorApp.Client 应用的项目文件中,执行以下操作:

      • <PropertyGroup> 添加一个值为 SecondApp<StaticWebAssetBasePath> 属性:

        <PropertyGroup>
          ...
          <StaticWebAssetBasePath>SecondApp</StaticWebAssetBasePath>
        </PropertyGroup>
        
      • Shared 项目添加一个项目引用:

        <ItemGroup>
          <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
        </ItemGroup>
        

        占位符 {SOLUTION NAME} 是解决方案的名称。

  • 在服务器应用的项目文件中,为新增的 SecondBlazorApp.Client 客户端应用创建一个项目引用:

    <ItemGroup>
      <ProjectReference Include="..\Client\{SOLUTION NAME}.Client.csproj" />
      <ProjectReference Include="..\SecondClient\SecondBlazorApp.Client.csproj" />
      <ProjectReference Include="..\Shared\{SOLUTION NAME}.Shared.csproj" />
    </ItemGroup>
    

    占位符 {SOLUTION NAME} 是解决方案的名称。

  • 在服务器应用的 Properties/launchSettings.json 文件中,配置 Kestrel 配置文件 ({SOLUTION NAME}.Server) 的 applicationUrl,以访问位于端口 5001 和 5002 的客户端应用:

    "applicationUrl": "https://localhost:5001;https://localhost:5002",
    
  • 在服务器应用的 Startup.Configure 方法 (Startup.cs) 中,删除在调用 UseHttpsRedirection 后显示的以下行:

    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllers();
        endpoints.MapFallbackToFile("index.html");
    });
    

    添加将请求映射到客户端应用的中间件。 下面的示例将中间件配置为在以下情况下运行:

    • 原始客户端应用的请求端口为 5001,或新增的客户端应用的请求端口为 5002。

    • 原始客户端应用的请求主机为 firstapp.com,或新增的客户端应用的请求主机为 secondapp.com

      备注

      本部分中显示的示例需要其他配置以实现以下目的:

      • 访问示例主机域 firstapp.comsecondapp.com 上的应用。
      • 获取客户端应用的证书以启用 TLS 安全性 (HTTPS)。

      所需的配置不在本文内容范围内,并且取决于解决方案的托管方式。 有关详细信息,请参阅托管和部署文章

    将以下代码放在先前已删除行的位置:

    app.MapWhen(ctx => ctx.Request.Host.Port == 5001 || 
        ctx.Request.Host.Equals("firstapp.com"), first =>
    {
        first.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/FirstApp" + ctx.Request.Path;
            return nxt();
        });
    
        first.UseBlazorFrameworkFiles("/FirstApp");
        first.UseStaticFiles();
        first.UseStaticFiles("/FirstApp");
        first.UseRouting();
    
        first.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/FirstApp/{*path:nonfile}", 
                "FirstApp/index.html");
        });
    });
    
    app.MapWhen(ctx => ctx.Request.Host.Port == 5002 || 
        ctx.Request.Host.Equals("secondapp.com"), second =>
    {
        second.Use((ctx, nxt) =>
        {
            ctx.Request.Path = "/SecondApp" + ctx.Request.Path;
            return nxt();
        });
    
        second.UseBlazorFrameworkFiles("/SecondApp");
        second.UseStaticFiles();
        second.UseStaticFiles("/SecondApp");
        second.UseRouting();
    
        second.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapFallbackToFile("/SecondApp/{*path:nonfile}", 
                "SecondApp/index.html");
        });
    });
    
  • 在服务器应用的天气预报控制器 (Controllers/WeatherForecastController.cs) 中,将到 WeatherForecastController 的现有路由 ([Route("[controller]")]) 替换为以下路由:

    [Route("FirstApp/[controller]")]
    [Route("SecondApp/[controller]")]
    

    先前添加到服务器应用的 Startup.Configure 方法的中间件会将向 /WeatherForecast 发出的传入请求修改为 /FirstApp/WeatherForecast/SecondApp/WeatherForecast,具体取决于端口 (5001/5002) 或域 (firstapp.com/secondapp.com)。 为了将天气数据从服务器应用返回到客户端应用,需要前面的控制器路由。

多个 Blazor WebAssembly 应用的静态资产和类库

使用以下方法来引用静态资产:

  • 如果资产位于客户端应用的 wwwroot 文件夹中,请正常提供路径:

    <img alt="..." src="/{PATH AND FILE NAME}" />
    

    {PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。

  • 如果资产位于 Razor 类库 (RCL)wwwroot 文件夹中,请按照 ASP.NET Core 的类库中的可重用 Razor UI 中的指导,在客户端应用中引用静态资产:

    <img alt="..." src="_content/{PACKAGE ID}/{PATH AND FILE NAME}" />
    

    {PACKAGE ID} 占位符是库的包 ID。 如果项目文件中没有指定 <PackageId>,则包 ID 默认为项目的程序集名称。 {PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。

有关 RCL 的详细信息,请参阅:

独立部署

独立部署将 Blazor WebAssembly 应用作为客户端直接请求的一组静态文件提供。 任何静态文件服务器均可提供 Blazor 应用。

独立部署资产将发布到 /bin/Release/{TARGET FRAMEWORK}/publish/wwwroot 文件夹。

Azure 应用服务

可以将 Blazor WebAssembly 应用部署到 Windows 上的 Azure 应用服务,该服务在 IIS 上托管应用。

目前不支持将独立的 Blazor WebAssembly 应用部署到适用于 Linux 的 Azure 应用服务。 目前无法提供用于托管应用的 Linux 服务器映像。 正在进行此工作以支持此场景。

Azure 静态 Web 应用

有关详细信息,请参阅教程:在 Azure Static Web Apps 中使用 Blazor 生成静态 Web 应用

IIS

IIS 是适用于 Blazor 应用的强大静态文件服务器。 要配置 IIS 以托管 Blazor,请参阅在 IIS 上生成静态网站

/bin/Release/{TARGET FRAMEWORK}/publish 文件夹中已创建发布的资产。 在 Web 服务器或托管服务上托管 publish 文件夹的内容。

web.config

发布 Blazor 项目时,将使用以下 IIS 配置创建 web.config 文件:

  • 对以下文件扩展名设置 MIME 类型:
    • .dll: application/octet-stream
    • .json: application/json
    • .wasm: application/wasm
    • .woff: application/font-woff
    • .woff2: application/font-woff
  • 对以下 MIME 类型启用 HTTP 压缩:
    • application/octet-stream
    • application/wasm
  • 建立 URL 重写模块规则:
    • 提供应用的静态资产所驻留的子目录 (wwwroot/{PATH REQUESTED})。
    • 创建 SPA 回退路由,以便非文件资产请求能够重定向到应用的静态资产文件夹中的默认文档 (wwwroot/index.html)。

使用自定义 web.config

要使用自定义 web.config 文件,请将自定义 web.config 文件置于项目文件夹的根目录下。 将项目配置为使用应用项目文件中的 PublishIISAssets 发布特定于 IIS 的资源,并发布项目:

<PropertyGroup>
  <PublishIISAssets>true</PublishIISAssets>
</PropertyGroup>

安装 URL 重写模块

重写 URL 必须使用 URL 重写模块。 此模块默认不安装,且不适用于安装为 Web 服务器 (IIS) 角色服务功能。 必须从 IIS 网站下载该模块。 使用 Web 平台安装程序安装模块:

  1. 以本地方式导航到 URL 重写模块下载页。 对于英语版本,请选择“WebPI”以下载 WebPI 安装程序。 对于其他语言,请选择适当的服务器体系结构 (x86/x64) 下载安装程序。
  2. 将安装程序复制到服务器。 运行安装程序。 选择“安装”按钮,并接受许可条款。 安装完成后无需重启服务器。

配置网站

将网站的物理路径设置为应用的文件夹。 该文件夹包含:

  • web.config 文件,IIS 使用该文件配置网站,包括所需的重定向规则和文件内容类型。
  • 应用的静态资产文件夹。

作为 IIS 子应用托管

如果独立应用作为 IIS 子应用托管,请执行下列任一操作:

  • 禁用继承的 ASP.NET Core 模块处理程序。

    通过向文件添加 <handlers> 部分,删除 Blazor 应用的已发布 web.config 文件中的处理程序:

    <handlers>
      <remove name="aspNetCore" />
    </handlers>
    
  • 通过使用 <location> 元素并且将 inheritInChildApplications 设置为 false,禁止继承根(父级)应用的 <system.webServer> 部分:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <location path="." inheritInChildApplications="false">
        <system.webServer>
          <handlers>
            <add name="aspNetCore" ... />
          </handlers>
          <aspNetCore ... />
        </system.webServer>
      </location>
    </configuration>
    

配置应用的基路径外,还需删除处理程序或禁用继承。 在 IIS 中配置子应用时,在应用的 index.html 文件中将应用基路径设置为 IIS 别名。

Brotli 和 Gzip 压缩

本部分仅适用于独立的 Blazor WebAssembly 应用。托管的 Blazor 应用使用默认的 ASP.NET Core 应用 web.config 文件,而不使用本部分中所链接的文件。

通过 web.config 可将 IIS 配置为提供独立 Blazor WebAssembly 应用的 Brotli 或 Gzip 压缩的 Blazor 资产。 若要查看示例配置文件,请参阅 web.config

在以下情况下,可能需要进一步配置示例 web.config 文件:

  • 应用的规范具有以下任意一个要求:
    • 提供不是由示例 web.config 文件配置的压缩文件。
    • 采用非压缩格式提供由示例 web.config 文件配置的压缩文件。
  • 服务器的 IIS 配置(例如 applicationHost.config)提供了服务器级 IIS 默认值。 根据服务器级别配置,应用可能要求 IIS 配置不同于示例 web.config 文件所包含的配置。

疑难解答

如果你看到“500 - 内部服务器错误”,且 IIS 管理器在尝试访问网站配置时抛出错误,请确认是否已安装 URL 重写模块。 如果未安装该模块,则 IIS 无法分析 web.config 文件。 这可以防止 IIS 管理器加载网站配置,并防止网站对 Blazor 的静态文件提供服务。

有关排查部署到 IIS 的问题的详细信息,请参阅 对 Azure 应用服务和 IIS 上的 ASP.NET Core 进行故障排除

Azure 存储

Azure 存储静态文件承载允许无服务器的 Blazor 应用承载。 支持自定义域名、Azure 内容分发网络 (CDN) 以及 HTTPS。

为存储帐户上的静态网站承载启用 blob 服务时:

  • 设置“索引文档名称”到 index.html
  • 设置“错误文档路径”到 index.html。 Razor 组件和其他非文件终结点不会驻留在由 blob 服务存储的静态内容中的物理路径中。 当收到 Blazor 路由器应处理的对这些资源之一的请求时,由 blob 服务生成的“404 - 未找到”错误会将此请求路由到“错误文档路径”。 返回 index.html blob,Blazor 路由器会加载并处理此路径。

如果由于文件的 Content-Type 标头中的 MIME 类型不正确,导致在运行时未加载文件,请执行以下任一操作:

  • 配置工具,用于在部署文件时设置正确的 MIME 类型(Content-Type 标头)。

  • 在部署应用后更改文件的 MIME 类型(Content-Type 标头)。

    在每个文件的存储资源管理器(Azure 门户)中,执行以下操作:

    1. 右键单击该文件并选择“属性”。
    2. 设置“ContentType”并选择“保存”按钮 。

有关更多信息,请参阅 Azure 存储中的静态网站承载

Nginx

以下 nginx.conf 文件已简化,以展示如何配置 Nginx,以便每当在磁盘上找不到相应文件时,就发送 index.html 文件。

events { }
http {
    server {
        listen 80;

        location / {
            root      /usr/share/nginx/html;
            try_files $uri $uri/ /index.html =404;
        }
    }
}

使用 limit_req 设置 NGINX 突发速率限制时,Blazor WebAssembly 应用可能需要一个较大的 burst 参数值来容纳应用发出的请求数。 首先,将值设置为不低于 60:

http {
    server {
        ...

        location / {
            ...

            limit_req zone=one burst=60 nodelay;
        }
    }
}

如果浏览器开发人员工具或网络流量工具指示请求收到“503 - 服务不可用”状态代码,则将该值调高。

有关生产 Nginx Web 服务器配置的详细信息,请参阅 Creating NGINX Plus and NGINX Configuration Files(创建 NGINX 增强版和 NGINX 配置文件)。

Apache

若要将 Blazor WebAssembly 应用部署到 CentOS 7 或更高版本,请执行以下操作:

  1. 创建 Apache 配置文件。 下面的示例展示了一个简化的配置文件 (blazorapp.config):

    <VirtualHost *:80>
        ServerName www.example.com
        ServerAlias *.example.com
    
        DocumentRoot "/var/www/blazorapp"
        ErrorDocument 404 /index.html
    
        AddType application/wasm .wasm
        AddType application/octet-stream .dll
    
        <Directory "/var/www/blazorapp">
            Options -Indexes
            AllowOverride None
        </Directory>
    
        <IfModule mod_deflate.c>
            AddOutputFilterByType DEFLATE text/css
            AddOutputFilterByType DEFLATE application/javascript
            AddOutputFilterByType DEFLATE text/html
            AddOutputFilterByType DEFLATE application/octet-stream
            AddOutputFilterByType DEFLATE application/wasm
            <IfModule mod_setenvif.c>
          BrowserMatch ^Mozilla/4 gzip-only-text/html
          BrowserMatch ^Mozilla/4.0[678] no-gzip
          BrowserMatch bMSIE !no-gzip !gzip-only-text/html
      </IfModule>
        </IfModule>
    
        ErrorLog /var/log/httpd/blazorapp-error.log
        CustomLog /var/log/httpd/blazorapp-access.log common
    </VirtualHost>
    
  2. 将 Apache 配置文件放入 /etc/httpd/conf.d/ 目录(这是 CentOS 7 中的默认 Apache 配置目录)。

  3. 将应用的文件放入 /var/www/blazorapp 目录(配置文件中特定于 DocumentRoot 的位置)。

  4. 重启 Apache 服务。

有关详细信息,请参阅 mod_mimemod_deflate

GitHub 页

要处理 URL 重写,请使用脚本添加 wwwroot/404.html 文件,该脚本可处理到 index.html 页的重定向请求。 有关示例,请参阅 SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库

如果使用项目站点而非组织站点,请在 wwwroot/index.html 中更新 <base> 标记。 将 href 属性值设置为,包含尾部斜杠的 GitHub 存储库名称(例如,/my-repository/)。 在 SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库中,将在发布时通过 .github/workflows/main.yml 配置文件更新基本 href

备注

SteveSandersonMS/BlazorOnGitHubPages GitHub 存储库 不归 .NET Foundation 或 Microsoft 所有,也不由它们提供维护和支持。

主机配置值

在开发环境中,Blazor WebAssembly 应用可以在运行时接受以下主机配置值作为命令行参数。

内容根

--contentroot 参数设置包含应用内容文件的目录的绝对路径(内容根目录)。 在下面的示例中,/content-root-path 是应用的内容根路径。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --contentroot=/content-root-path
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--contentroot=/content-root-path"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --contentroot=/content-root-path
    

基路径

--pathbase 参数可设置使用非根相对 URL 路径本地运行的应用的应用基路径(将 <base> 标记 href 针对暂存和生产设置为 / 之外的路径)。 在下面的示例中,/relative-URL-path 是应用的基路径。 有关详细信息,请参阅应用基路径

重要

不同于向 href 标记的 <base> 提供的路径,传递 --pathbase 参数值时不包括尾部反斜杠 (/)。 如果在 <base> 标记中以 <base href="/CoolApp/"> 形式(包括尾部反斜杠)提供应用基路径,则以 --pathbase=/CoolApp 形式(无尾部反斜杠)传递命令行参数值。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --pathbase=/relative-URL-path
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--pathbase=/relative-URL-path"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --pathbase=/relative-URL-path
    

URL

--urls 参数设置 IP 地址或主机地址,其中包含侦听请求的端口和协议。

  • 以本地方式在命令提示符下运行应用时传递该参数。 在应用的目录中,执行以下操作:

    dotnet run --urls=http://127.0.0.1:0
    
  • 在 IIS Express 配置文件中,向应用的 launchSettings.json 文件添加条目。 如果使用 Visual Studio 调试器并在命令提示符中运行 dotnet run 来运行应用,使用的是此设置。

    "commandLineArgs": "--urls=http://127.0.0.1:0"
    
  • 在 Visual Studio 中,在“属性” > “调试” > “应用程序参数”中指定参数 。 在 Visual Studio 属性页中设置参数可将参数添加到 launchSettings.json 文件。

    --urls=http://127.0.0.1:0
    

配置链接器

Blazor 对每个发布版本执行中间语言 (IL) 链接,以从输出程序集中删除不必要的 IL。 有关详细信息,请参阅 配置 ASP.NET Core Blazor 链接器

更改 DLL 文件的文件扩展名

如果需要更改应用的已发布 .dll 文件的文件扩展名,请按照本部分中的指导进行操作。

发布应用后,使用 shell 脚本或 DevOps 生成管道将 .dll 文件重命名,以使用其他文件扩展名。 将 .dll 文件的目标位置设为应用的已发布输出的 wwwroot 目录中(例如 {CONTENT ROOT}/bin/Release/netstandard2.1/publish/wwwroot)。

在下面的示例中,重命名 .dll 文件,以使用 .bin 文件扩展名。

在 Windows 上:

dir .\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content .\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content .\_framework\blazor.boot.json

如果服务工作进程资产也在使用中,请添加以下命令:

((Get-Content .\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content .\service-worker-assets.js

在 Linux 或 macOS 上:

for f in _framework/_bin/*; do mv "$f" "`echo $f | sed -e 's/\.dll/.bin/g'`"; done
sed -i 's/\.dll"/.bin"/g' _framework/blazor.boot.json

如果服务工作进程资产也在使用中,请添加以下命令:

sed -i 's/\.dll"/.bin"/g' service-worker-assets.js

要使用不同于 .bin 的其他文件扩展名,请在前面的命令中替换 .bin

若要处理压缩的 blazor.boot.json.gzblazor.boot.json.br 文件,请采用以下方法之一:

  • 删除压缩的 blazor.boot.json.gzblazor.boot.json.br 文件。 此方法禁用压缩。
  • 重新压缩更新后的 blazor.boot.json 文件。

上述指导也适用于正在使用服务工作进程资产的情况。 删除或重新压缩 wwwroot/service-worker-assets.js.brwwwroot/service-worker-assets.js.gz。 否则,浏览器中的文件完整性检查将失败。

以下 Windows 示例使用项目根目录中的 PowerShell 脚本。

ChangeDLLExtensions.ps1::

param([string]$filepath,[string]$tfm)
dir $filepath\bin\Release\$tfm\wwwroot\_framework\_bin | rename-item -NewName { $_.name -replace ".dll\b",".bin" }
((Get-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json
Remove-Item $filepath\bin\Release\$tfm\wwwroot\_framework\blazor.boot.json.gz

如果服务工作进程资产也在使用中,请添加以下命令:

((Get-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js -Raw) -replace '.dll"','.bin"') | Set-Content $filepath\bin\Release\$tfm\wwwroot\service-worker-assets.js

在项目文件中,在发布应用后运行脚本:

<Target Name="ChangeDLLFileExtensions" AfterTargets="Publish" Condition="'$(Configuration)'=='Release'">
  <Exec Command="powershell.exe -command &quot;&amp; { .\ChangeDLLExtensions.ps1 '$(SolutionDir)' '$(TargetFramework)'}&quot;" />
</Target>

备注

重命名和延迟加载相同的程序集时,请参阅 在 ASP.NET Core Blazor WebAssembly 中延迟加载程序集 中的指南。

解决完整性检查失败

当 Blazor WebAssembly 下载应用的启动文件时,它会指示浏览器对响应执行完整性检查。 它使用 blazor.boot.json 文件中的信息为 .dll.wasm 和其他文件指定预期的 SHA-256 哈希值。 这对以下原因有所帮助:

  • 它可确保不会出现加载不一致文件集的风险,例如,在用户正在下载应用程序文件时将新部署应用到 Web 服务器的情况。 不一致的文件可能导致未定义的行为。
  • 它可确保用户的浏览器从不缓存不一致或无效的响应,这些响应可能会阻止他们启动应用(即使他们手动刷新了页面也是如此)。
  • 它可以安全地缓存响应,甚至无需检查服务器端更改,直到预期的 SHA-256 哈希本身发生更改,因此,后续页面加载需要较少的请求即可快速完成。

如果你的 Web 服务器返回的响应与预期的 SHA-256 哈希不匹配,你将看到类似于以下内容的错误显示在浏览器的开发人员控制台中:

未能使用计算出的 SHA-256 完整性“IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY=”在资源“https://myapp.example.com/_framework/MyBlazor App.dll”的“integrity”属性中找到有效摘要。 资源已被阻止。

在大多数情况下,这不是完整性检查本身的问题。 相反,它表示存在其他问题,并且完整性检查会警告你其他问题。

诊断完整性问题

生成应用时,生成的 blazor.boot.json 清单将描述生成输出生成时启动资源(例如,.dll.wasm 和其他文件)的 SHA-256 哈希。 只要 blazor.boot.json 中的 SHA-256 哈希与传递到浏览器的文件相匹配,完整性检查就会通过。

此失败的常见原因包括:

  • Web 服务器的响应是一个错误(例如,“404 - 找不到”或“500 - 内部服务器错误”),而不是浏览器所请求的文件。 浏览器会将其报告为完整性检查失败,而不是响应失败。
  • 在文件生成和传递到浏览器之间已更改文件的内容。 下面可能会发生这种情况:
    • 你或生成工具手动修改生成输出的情况。
    • 部署过程的某个方面修改了文件的情况。 例如,在使用基于 Git 的部署机制时,请记住,如果你在 Windows 上提交文件并在 Linux 上检查它们,则 Git 会以透明方式将 Windows 样式的行尾转换为 Unix 样式的行尾。 更改文件行尾将更改 SHA-256 哈希。 若要避免此问题,请考虑使用 .gitattributes 将生成项目视为 binary 文件
    • Web 服务器在提供文件内容的过程中对其进行修改。 例如,某些内容分发网络 (CDN) 会自动尝试缩小 HTML,从而对其进行修改。 可能需要禁用此类功能。

若要诊断哪些功能适用于你的情况,请执行执行操作:

  1. 通过读取错误消息来记下哪个文件触发错误。
  2. 打开浏览器的开发人员工具,然后在“网络”选项卡中查找。如有必要,请重新加载页面以查看请求和响应的列表。 在该列表中查找触发错误的文件。
  3. 检查响应中的 HTTP 状态代码。 如果服务器返回除“200 - 正常”(或其他 2xx 状态代码)以外的任何内容,则需要诊断服务器端问题。 例如,状态代码 403 表示存在授权问题,而状态代码 500 表示服务器以未指定的方式失败。 请参阅服务器端日志以诊断和修复应用。
  4. 如果资源的状态代码为“200 - 正常”,请在浏览器的开发人员工具中查看响应内容,并检查内容是否与预期的数据匹配。 例如,常见问题是错误配置了路由,因此请求甚至返回其他文件的 index.html 数据。 请确保对 .wasm 请求的响应是 WebAssembly 二进制文件,对 .dll 请求的响应是 .NET 程序集二进制文件。 如果不是,则需要诊断服务器端路由问题。
  5. 搜索以验证应用的已发布和已部署输出,并提供完整性 PowerShell 脚本故障排除

如果确认服务器返回看似正确的数据,则必须在生成文件和传递文件之间修改内容。 若要对此进行调查,请执行以下操作:

  • 如果在生成文件后修改文件,请检查生成工具链和部署机制。 例如,在 Git 转换文件行尾时,如前所述。
  • 如设置为动态修改响应(例如,尝试缩小 HTML),请检查 Web 服务器或 CDN 配置。 Web 服务器可以实现 HTTP 压缩(例如,返回 content-encoding: brcontent-encoding: gzip),因为这不会影响解压缩后的结果。 但是,Web 服务器不可以修改未压缩的数据。

完整性 PowerShell 脚本故障排除

使用 integrity.ps1 PowerShell 脚本来验证已发布和已部署的 Blazor 应用。 当应用出现 Blazor 框架无法识别的完整性问题时,该脚本将作为起点提供给 PowerShell Core 6。 应用可能需要自定义脚本,包括在 6.2.7 版之后的 PowerShell 版本上运行时。

此脚本将检查 publish 文件夹中的文件,并从部署的应用中下载这些文件,以检测包含完整性哈希的不同清单中的问题。 这些检查应检测最常见的问题:

  • 你修改了已发布的输出中的文件,但未实现它。
  • 应用未正确部署到部署目标,或者在部署目标的环境中发生了更改。
  • 部署的应用与发布应用的输出之间存在差异。

在 PowerShell 命令行中使用以下命令调用脚本:

.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER}

占位符:

  • {BASE URL}:已部署的应用的 URL。
  • {PUBLISH OUTPUT FOLDER}:应用的 publish 文件夹的路径,或已发布的用于部署应用的位置。

备注

克隆 dotnet/AspNetCore.Docs GitHub 存储库时,integrity.ps1 脚本可能会被 Bitdefender 或系统上存在的另一个病毒扫描程序隔离。 通常,该文件被病毒扫描程序的启发式扫描技术捕获,该技术只查找文件中可能指示存在恶意软件的模式。 若要防止病毒扫描程序隔离该文件,请在克隆存储库之前向病毒扫描程序添加一个例外。 以下示例是 Windows 系统上脚本的一个典型路径。 可以根据需要为其他系统调整路径。 占位符 {USER} 是用户的路径段。

C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1

警告:创建病毒扫描程序例外是危险操作,只有在你确定文件是安全的情况下才可以进行。

将文件的校验和与有效校验和值进行比较并不能保证文件的安全,但以维护校验和值的方式修改文件对于恶意用户来说并不简单。 因此,校验和作为一种常规安全方法很有用。 将本地 integrity.ps1 文件的校验和与以下值之一进行比较:

  • SHA256:6b0dc7aba5d8489136bb2969036432597615b11b4e432535e173ca077a0449c4
  • MD5:f0c800a4c72604bd47f3c19f5f0bb4f4

通过以下命令在 Windows OS 上获取文件的校验和。 为 {PATH AND FILE NAME} 占位符提供路径和文件名,并指明为 {SHA512|MD5} 占位符生成的校验和类型,可以是 SHA256,也可以是 MD5

CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5}

如果你有任何理由担心校验和验证在你的环境中不够安全,请咨询组织的安全领导以获得指导。

有关详细信息,请参阅了解恶意软件和其他威胁

禁用非 PWA 应用的完整性检查

在大多数情况下,不要禁用完整性检查。 禁用完整性检查并不能解决导致意外响应的根本问题,并且会导致丢失前面列出的权益。

在某些情况下,Web 服务器无法用于返回一致的响应,但别无选择,只能禁用完整性检查。 若要禁用完整性检查,请将以下内容添加到 Blazor WebAssembly 项目的 .csproj 文件中的属性组:

<BlazorCacheBootResources>false</BlazorCacheBootResources>

BlazorCacheBootResources 还会根据 SHA-256 哈希禁用 Blazor 缓存 .dll.wasm 和其他文件的默认行为,因为属性指示无法依靠 SHA-256 哈希来确保正确性。 即使有此设置,浏览器的普通 HTTP 缓存仍可能会缓存这些文件,但是否发生这种情况取决于你的 Web 服务器配置和它所提供的 cache-control 标头。

备注

BlazorCacheBootResources 属性不会禁用渐进式 Web 应用程序 (PWA) 的完整性检查。 有关 PWA 的相关指南,请参阅禁用 PWA 的完整性检查部分。

禁用 PWA 的完整性检查

Blazor 的渐进式 Web 应用程序 (PWA) 模板包含建议的 service-worker.published.js 文件,该文件负责获取和存储应用程序文件以供脱机使用。 这是普通应用启动机制的独立进程,具有其自己单独的完整性检查逻辑。

service-worker.published.js 文件中,出现以下行:

.map(asset => new Request(asset.url, { integrity: asset.hash }));

若要禁用完整性检查,请通过将该行更改为以下行来删除 integrity 参数:

.map(asset => new Request(asset.url));

同样,禁用完整性检查意味着会丢失完整性检查提供的安全保证。 例如,如果用户的浏览器在你部署新版本的那一刻缓存应用,则会存在风险,它可能会缓存旧部署中的某些文件和新部署中的某些文件。 如果发生这种情况,则在部署进一步更新之前,应用程序会在中断状态下停滞。