注释
此版本不是本文的最新版本。 有关本文的最新版本,请参阅 .NET 7 版本。
本文介绍如何配置托管 Blazor WebAssembly 应用以托管多个 Blazor WebAssembly 应用。
配置
选择符合托管要求的本文版本,即端口/域托管(例如,:5001/:5002或firstapp.comsecondapp.com/)或路由子路径托管(例如,/FirstApp和)。/SecondApp
使用当前托管选择时,本文介绍端口/域托管(例如,:5001/:5002或)。firstapp.com/secondapp.com
在以下示例中:
- 托管
应用的项目名称位于名为 < a0/> 的文件夹中。 - 在添加第二个客户端应用之前,解决方案中的三个项目位于
MultipleBlazorApps.ClientClient文件夹、MultipleBlazorApps.ServerServer文件夹和MultipleBlazorApps.SharedShared文件夹中。 - 初始客户端应用是从 Blazor WebAssembly 项目模板创建的解决方案的默认客户端项目。
- 另一个客户端应用将添加到解决方案,
位于名为 < a0/a0> 的文件夹中。 - (可选)服务器项目(
MultipleBlazorApps.Server)可以将页面或视图用作 Razor 页面或 MVC 应用。 - 第一个客户端应用可在端口 5001 或主机
firstapp.com的浏览器中访问。 第二个客户端应用可在端口 5002 或主机secondapp.com的浏览器中访问。
通过当前选择,本文介绍路由子路径托管(例如 /FirstApp ,和 /SecondApp)。
在以下示例中:
- 托管
应用的项目名称位于名为 < a0/> 的文件夹中。 - 在添加第二个客户端应用之前,解决方案中的三个项目位于
MultipleBlazorApps.ClientClient文件夹、MultipleBlazorApps.ServerServer文件夹和MultipleBlazorApps.SharedShared文件夹中。 - 初始客户端应用是从 Blazor WebAssembly 项目模板创建的解决方案的默认客户端项目。
- 另一个客户端应用将添加到解决方案,
位于名为 < a0/a0> 的文件夹中。 - (可选)服务器项目(
MultipleBlazorApps.Server)可以将页面或视图作为正式 Razor 页面或 MVC 应用提供服务。 - 这两个客户端应用都
MultipleBlazorApps.Server使用项目Properties/launchSettings.json文件在其值中applicationUrl定义的默认端口。 第一个客户端应用可在子路径的/FirstApp浏览器中访问。 第二个客户端应用可在子路径的/SecondApp浏览器中访问。
本文中所示的示例需要以下附加配置:
- 直接在示例主机域上访问应用,
firstapp.com以及secondapp.com。 - 用于启用 TLS/HTTPS 安全性的客户端应用的证书。
- 将服务器应用配置为 Razor Pages 应用,以便实现以下功能:
- 将 Razor 组件集成到页面或视图中。
- 预呈现 Razor 组件。
上述配置超出了本文的范围。 有关详细信息,请参阅以下资源:
- 托管和部署 ASP.NET Core
- 在 ASP.NET Core 中强制使用 HTTPS
- 将 ASP.NET 核心Razor组件与托管Blazor WebAssembly解决方案中的 MVC 或 Razor Pages 集成
使用现有托管Blazor WebAssembly解决方案或通过传递-ho|--hosted选项从Blazor WebAssembly项目模板创建新的托管Blazor WebAssembly解决方案(如果使用 .NET CLI 或在 IDE 中创建项目时在 Visual Studio 中选择“ASP.NET 核心托管”复选框)。
对命名 MultipleBlazorApps 和命名项目 MultipleBlazorApps的解决方案使用文件夹。
在名为 <MultipleBlazorApps.SecondClient 的第二Blazor WebAssembly个客户端应用。 将项目添加为独立 Blazor WebAssembly 应用。 若要创建独立 Blazor WebAssembly 应用,请在使用 .NET CLI 时不要传递 -ho|--hosted 该选项;如果使用 Visual Studio,请不要使用 ASP.NET Core 托管 复选框。
对项目进行以下更改 MultipleBlazorApps.SecondClient :
- 将
FetchData组件 (Pages/FetchData.razor) 从Client/Pages文件夹复制到SecondClient/Pages该文件夹。 此步骤是必需的,因为独立 Blazor WebAssembly 应用不为天气数据调用 Server 项目的控制器,因此它使用静态数据文件。 通过将组件复制到FetchData添加的项目,第二个客户端应用还会对服务器 API 进行 Web API 调用,以获取天气数据。 - 删除文件夹
SecondClient/wwwroot/sample-data,因为weather.json文件夹中的文件未使用。
下表描述了添加文件夹和项目后 SecondClient 解决方案的文件夹和 MultipleBlazorApps.SecondClient 项目名称。
| 物理文件夹 | 项目名称 | Description |
|---|---|---|
Client |
MultipleBlazorApps.Client |
Blazor WebAssembly 客户端应用 |
SecondClient |
MultipleBlazorApps.SecondClient |
Blazor WebAssembly 客户端应用 |
Server |
MultipleBlazorApps.Server |
ASP.NET Core 服务器应用 |
Shared |
MultipleBlazorApps.Shared |
共享资源项目 |
该项目 MultipleBlazorApps.Server 为这两 Blazor WebAssembly 个客户端应用提供服务,并通过 MVC 控制器向客户端应用的 FetchData 组件提供天气数据。 或者, MultipleBlazorApps.Server 项目还可以作为传统 Razor 页面或 MVC 应用提供页面或视图。 本文稍后将介绍启用服务页面或视图的步骤。
注释
本文中的演示使用项目和SecondApp项目的静态 Web 资产路径名称FirstAppMultipleBlazorApps.Client。MultipleBlazorApps.SecondClient 名称“FirstApp”和“”SecondApp仅用于演示目的。 可以接受其他名称来区分客户端应用,例如App1App2/,/Client1Client2,1/2或类似的命名方案。
当通过端口或域将请求路由到客户端应用时,“FirstApp”和“”SecondApp在 内部 用于路由请求并为静态资产提供响应,并且不会在浏览器的地址栏中看到。
注释
本文中的演示使用项目和SecondApp项目的静态 Web 资产路径名称FirstAppMultipleBlazorApps.Client。MultipleBlazorApps.SecondClient 名称“FirstApp”和“”SecondApp仅用于演示目的。 可以接受其他名称来区分客户端应用,例如App1App2/,/Client1Client2,1/2或类似的命名方案。
“FirstApp”和“”SecondApp也出现在浏览器的地址栏中,因为请求会使用这些名称路由到两个客户端应用。 支持其他有效的 URL 路由段,并且路由段不需要严格匹配用于在内部路由静态 Web 资产的名称。 对内部静态资产路由和应用请求路由使用“FirstApp”和“”SecondApp仅用于本文示例中的召集。
在第一个客户端应用的项目文件中(MultipleBlazorApps.Client.csproj)将属性添加到<StaticWebAssetBasePath><PropertyGroup>值为FirstApp设置项目静态资产的基本路径的属性:
<StaticWebAssetBasePath>FirstApp</StaticWebAssetBasePath>
在 MultipleBlazorApps.SecondClient 应用的项目文件中(MultipleBlazorApps.SecondClient.csproj):
将
<StaticWebAssetBasePath>属性添加到<PropertyGroup>值为SecondApp:<StaticWebAssetBasePath>SecondApp</StaticWebAssetBasePath>将项目的项目引用
MultipleBlazorApps.Shared添加到 :<ItemGroup><ItemGroup> <ProjectReference Include="..\Shared\MultipleBlazorApps.Shared.csproj" /> </ItemGroup>
在服务器应用的项目文件中(Server/MultipleBlazorApps.Server.csproj),在以下项<ItemGroup>中创建已添加MultipleBlazorApps.SecondClient客户端应用的项目引用:
<ProjectReference Include="..\SecondClient\MultipleBlazorApps.SecondClient.csproj" />
在服务器应用的Properties/launchSettings.json文件中,配置applicationUrlKestrel配置文件(MultipleBlazorApps.Server)以访问端口 5001 和 5002 上的客户端应用。 如果将本地环境配置为使用示例域,则 URL applicationUrl 可用于 firstapp.com 和 secondapp.com 不使用端口。
注释
此演示中的端口使用允许在本地浏览器中访问客户端项目,而无需配置本地托管环境,以便 Web 浏览器可以通过主机配置访问客户端应用, firstapp.com 以及 secondapp.com。 在生产方案中,典型的配置是使用子域来区分客户端应用。
例如:
- 端口将从此演示的配置中删除。
- 主机更改为使用子域,例如
www.contoso.com网站访问者和admin.contoso.com管理员。 - 其他客户端应用可以包含其他主机,如果服务器应用也是 Razor 提供页面或视图的 Pages 或 MVC 应用,则至少需要另外一个主机。
如果计划从服务器应用提供页面或视图,请使用Properties/launchSettings.json文件中的以下applicationUrl设置,该设置允许以下访问:
- (可选) Razor Pages 或 MVC 应用(
MultipleBlazorApps.Server项目)在端口 5000 响应请求。 - 对第一个客户端(
MultipleBlazorApps.Client项目)的请求的响应位于端口 5001。 - 对第二个客户端(
MultipleBlazorApps.SecondClient项目)的请求的响应位于端口 5002。
"applicationUrl": "https://localhost:5000;https://localhost:5001;https://localhost:5002",
如果不打算让服务器应用提供页面或视图,并且只为客户端应用提供服务 Blazor WebAssembly ,请使用以下设置,该设置允许以下访问:
- 第一个客户端应用在端口 5001 上做出响应。
- 第二个客户端应用在端口 5002 上响应。
"applicationUrl": "https://localhost:5001;https://localhost:5002",
在服务器应用的 Program.cs 文件中,删除调用后 UseHttpsRedirection出现的以下代码:
如果计划从服务器应用提供页面或视图,请删除以下代码行:
- app.UseBlazorFrameworkFiles();- app.MapFallbackToFile("index.html");如果计划服务器应用仅为客户端应用提供服务 Blazor WebAssembly ,请删除以下代码:
- app.UseBlazorFrameworkFiles(); ... - app.UseRouting(); - app.MapRazorPages(); - app.MapControllers(); - app.MapFallbackToFile("index.html");保留静态文件中间件:
app.UseStaticFiles();
添加将请求映射到客户端应用的中间件。 以下示例将中间件配置为在第一个客户端应用的请求端口为 5001 或第二个客户端应用的 5002 时运行,或者请求主机为
firstapp.com第一个客户端应用或secondapp.com第二个客户端应用。注释
在本地系统上使用具有本地浏览器的主机(
firstapp.com/secondapp.com)需要超出本文范围的其他配置。 对于此方案的本地测试,建议使用端口。 典型的生产应用配置为使用子域,例如www.contoso.com站点访问者和admin.contoso.com管理员。 使用适当的 DNS 和服务器配置(超出了本文的范围,具体取决于所使用的技术),应用会响应以下代码中命名的任何主机的请求。从中删除行
Program.cs的位置app.UseBlazorFrameworkFiles();,放置以下代码: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"); }); });警告
依赖于主机头的 API(如 HttpRequest.Host 和 RequireHost)可能会受到客户端的欺骗。
若要防止主机和端口欺骗,请使用以下方法之一:
- 使用检查端口的 HttpContext.Connection (ConnectionInfo.LocalPort)。
- 采用主机筛选。
添加将请求映射到客户端应用的中间件。 以下示例将中间件配置为在请求子路径用于
/FirstApp第一个客户端应用或/SecondApp第二个客户端应用时运行。从中删除行
Program.cs的位置app.UseBlazorFrameworkFiles();,放置以下代码:app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments("/FirstApp", StringComparison.OrdinalIgnoreCase), first => { 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.Path.StartsWithSegments("/SecondApp", StringComparison.OrdinalIgnoreCase), second => { second.UseBlazorFrameworkFiles("/SecondApp"); second.UseStaticFiles(); second.UseStaticFiles("/SecondApp"); second.UseRouting(); second.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapFallbackToFile("/SecondApp/{*path:nonfile}", "SecondApp/index.html"); }); });在每个客户端应用中设置基路径:
在第一个客户端应用
index.html的文件(Client/wwwroot/index.html)中<base>,更新标记值以反映子路径。 尾部斜杠是必需的:<base href="/FirstApp/" />在第二个客户端应用
index.html的文件(SecondClient/wwwroot/index.html)中<base>,更新标记值以反映子路径。 尾部斜杠是必需的:<base href="/SecondApp/" />
有关详细信息 UseStaticFiles,请参阅 ASP.NET 核心 Blazor 静态文件。
有关详细信息 UseBlazorFrameworkFiles , MapFallbackToFile请参阅以下资源:
- Microsoft.AspNetCore.Builder.ComponentsWebAssemblyApplicationBuilderExtensions.UseBlazorFrameworkFiles (参考源)
- Microsoft.AspNetCore.Builder.StaticFilesEndpointRouteBuilderExtensions.MapFallbackToFile (参考源)
注释
指向 .NET 引用源的文档链接通常会加载存储库的默认分支,该分支代表正在进行的 .NET 下一版本的开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉菜单。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)。
从客户端应用到 /WeatherForecast 服务器 API 中的请求要么是请求 /FirstApp/WeatherForecast ,要么 /SecondApp/WeatherForecast 取决于哪个客户端应用发出请求。 因此,从服务器 API 返回天气数据的控制器路由需要修改以包含路径段。
在服务器应用的天气预报控制器()中,将现有路线(Controllers/WeatherForecastController.cs[Route("[controller]")])WeatherForecastController替换为以下路由,这考虑到了客户端请求路径:
[Route("FirstApp/[controller]")]
[Route("SecondApp/[controller]")]
如果计划从服务器应用提供页面,请将页面 IndexRazor 添加到 Pages 服务器应用的文件夹:
Pages/Index.cshtml:
@page
@model MultipleBlazorApps.Server.Pages.IndexModel
@{
ViewData["Title"] = "Home";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Home</title>
</head>
<body>
<div class="main">
<div class="content px-4">
<div>
<h1>Welcome</h1>
<p>Hello from Razor Pages!</p>
</div>
</div>
</div>
</body>
</html>
Pages/Index.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MultipleBlazorApps.Server.Pages;
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MultipleBlazorApps.Server.Pages
{
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
}
注释
上 Index 一页只是为了演示目的而采用的最小示例。 如果应用需要其他 Razor Pages 资产(如布局、样式、脚本和导入),请从从 Razor Pages 项目模板创建的应用获取它们。 有关详细信息,请参阅 Razor ASP.NET Core 中的 Pages 体系结构和概念。
如果计划从服务器应用提供 MVC 视图,请添加视图 Index 和 Home 控制器:
Views/Home/Index.cshtml:
@{
ViewData["Title"] = "Home";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Home</title>
</head>
<body>
<div class="main">
<div class="content px-4">
<div>
<h1>Welcome</h1>
<p>Hello from MVC!</p>
</div>
</div>
</div>
</body>
</html>
Controllers/HomeController.cs:
using Microsoft.AspNetCore.Mvc;
namespace MultipleBlazorApps.Server.Controllers;
public class HomeController : Controller
{
public IActionResult Index() => View();
}
注释
前面的 Index 视图只是为了演示目的而采用的最小示例。 如果应用需要其他 MVC 资产(如布局、样式、脚本和导入),请从从 MVC 项目模板创建的应用获取它们。 有关详细信息,请参阅 ASP.NET Core MVC 入门。
有关在页面或服务器应用视图中使用Razor客户端应用中的组件的详细信息,请参阅将 ASP.NET Core Razor 组件与托管Blazor WebAssembly解决方案中的 MVC 或 Razor Pages 集成。
运行应用
MultipleBlazorApps.Server运行项目:
- 在 . 处
https://localhost:5001访问初始客户端应用。 - 在 . 处
https://localhost:5002访问添加的客户端应用。 - 如果服务器应用配置为提供页面或视图,则访问
Index页面或视图。https://localhost:5000
- 在 . 处
https://localhost:{DEFAULT PORT}/FirstApp访问初始客户端应用。 - 在 . 处
https://localhost:{DEFAULT PORT}/SecondApp访问添加的客户端应用。 - 如果服务器应用配置为提供页面或视图,则访问
Index页面或视图。https://localhost:{DEFAULT PORT}
在前面的示例 URL 中,{DEFAULT PORT}占位符是项目Properties/launchSettings.json文件在其值中applicationUrl定义MultipleBlazorApps.Server的默认端口。
重要
使用 dotnet watch (或 dotnet run) 命令(.NET CLI)运行应用时,确认命令 shell 在解决方案的文件夹中打开 Server 。
使用 Visual Studio 的“开始”按钮运行应用时,请确认项目 MultipleBlazorApps.Server 已设置为启动项目(在解决方案资源管理器中突出显示)。
静态资产
当资产位于客户端应用的 wwwroot 文件夹中时,请在组件中提供静态资产请求路径:
<img alt="..." src="{PATH AND FILE NAME}" />
{PATH AND FILE NAME} 占位符是 wwwroot 下的路径和文件名。
例如,吉普图像 (jeep-yj.png) 的源位于vehiclewwwroot:
<img alt="Jeep Wrangler YJ" src="vehicle/jeep-yj.png" />
Razor 类库 (RCL) 支持
将 Razor 类库 (RCL) 作为新项目添加到解决方案:
- 右键单击 解决方案资源管理器 中的解决方案,然后选择“ 添加新>项目”。
-
Razor使用类库项目模板创建项目。 本节中的示例使用项目名称
ComponentLibrary,这也是 RCL 的程序集名称。 不要选中 “支持”页和视图 复选框。
对于每个托管Blazor WebAssembly客户端应用,请在解决方案资源管理器中右键单击每个客户端项目并选择“添加>项目引用”,为 RCL 项目创建项目引用。
通过以下任一方法在客户端应用中使用 RCL 中的组件:
@using将指令置于 RCL 命名空间的组件顶部,并为该组件添加Razor语法。 以下示例适用于具有程序集名称ComponentLibrary的 RCL:@using ComponentLibrary ... <Component1 />提供 RCL 的命名空间以及 Razor 组件的语法。 此方法不需要
@using组件文件顶部的指令。 以下示例适用于具有程序集名称ComponentLibrary的 RCL:<ComponentLibrary.Component1 />
注释
还可以将指令 @using 放入每个客户端应用的 _Import.razor 文件中,这使得 RCL 的命名空间全局可用于该项目中的组件。
当任何其他静态资产位于 wwwroot RCL 的文件夹中时,请根据 具有 ASP.NET Core 的类库中可重用 Razor UI 中的指南在客户端应用中引用静态资产:
<img alt="..." src="_content/{PACKAGE ID}/{PATH AND FILE NAME}" />
占 {PACKAGE ID} 位符是 RCL 的 包 ID。 如果项目文件中没有指定 <PackageId>,则包 ID 默认为项目的程序集名称。 占{PATH AND FILE NAME}位符是路径和文件名。wwwroot
以下示例显示了 RCL 文件夹文件夹中吉普图像 (jeep-yj.png) vehicle 的 wwwroot 标记。 以下示例适用于具有程序集名称 ComponentLibrary的 RCL:
<img alt="Jeep Wrangler YJ" src="_content/ComponentLibrary/vehicle/jeep-yj.png" />