具有 API 网关和微服务的 Blazor 服务器指南

Guidance for Blazor Server with API Gateway and Microservices

到目前为止,我还没有找到有关将 Blazor Server(不是 WebAssembly)与 API 网关和微服务一起使用的指南。讨论这些 Blazor 以及 API 网关和微服务的文章总是指 Blazor WebAssembly (Wasm)。 (是否假定 Blazor Server 应用程序不会使用微服务?此外,就其价值而言,选择 Blazor Server 而不是 Blazor WebAssembly 的原因是为了更好地保护知识 属性。)

无论如何... 我想知道 Blazor Server 应用程序是否应该位于网关前面,通过网关发送其内部 API 调用到网关后面的微服务,就像这样……

[浏览器] ----(SignalR)--- [Blazor 服务器应用程序] ----(https)---- [API 网关] ----(http)- --- [微服务]

或者将应用程序放在网关后面更有意义,让 SignalR 连接隧道穿过网关,就像这样……

[浏览器] ----(SignalR)---- [API 网关] ----(SignalR)---- [Blazor 服务器应用程序] ----(http) ---- [微服务]

请记住在建立 SignalR 连接之前浏览器中应用程序的初始加载。需要单独处理吗?它会影响上面给出的选项的选择吗?我缺少更好的解决方案吗?

我们在 Azure Service Fabric 中使用 Blazor 服务器端作为微服务集合的前端 运行(本身是无状态的 ASP.NET 核心服务),因此这是一个完全合法的场景.我们的整个应用程序 运行 都在 Azure 的云中,所以我将使用它的产品来描述我们的实现。

为了更好地分离关注点并更容易保护互联网边界,我们为 public API 和 SignalR 提供了两个单独的服务。 API 网关因此充当安全边界,因为它可以处理 HTTPS 卸载,位于面向 public 的子网上并路由到虚拟网络上的内部服务。提供 SignalR 访问的服务然后 运行 在应用程序网关实例后面的面向 public 的子网上(用于防火墙 + 服务路由功能)。

因此,我们将两个服务设置如下:

SignalR 服务

[浏览器] -- (SignalR) -- [Azure 应用程序网关] -- [Azure Service Fabric - Blazor 服务器端应用程序] -- [SF 服务远程处理] -- [微服务]

API 服务

[浏览器] -- (https) -- [Azure API 管理] -- (http) -- [Azure Service Fabric - ASP.NET 核心 API] -- [SF Service Remoting] -- [微服务]

微服务陷阱 - 数据保护

特别是在支持身份验证时,ASP.NET Core 会在内部设置一些密钥来加密 Blazor 会话中使用的状态。在微服务场景中,除非您在整个网络堆栈(负载均衡器、网关、路由器等)中一直采用粘性路由,以确保所有请求始终命中同一个实例(并且大概您的实例不会重建并在您不注意时转移到其他地方),您将 运行 陷入关于未能取消保护状态的模糊且无用的错误。

修复非常简单 - 在 ASP.NET 核心服务的 Startup.cs 中,确保设置 services.AddDataProtection(); 并进行适当的配置。由于我们使用的是 Service Fabric,因此我们使用的 third-party library 非常适合用于此目的。要使用,请将 NuGet 包 SoCreate.AspNetCore.DatapProtection.ServiceFabric 安装到您的 ASP.NET 核心服务,然后将以下内容简单地放入您的 Startup.cs 中的 ConfigureServices() 方法中:

public void ConfigureServices(IServiceCollection services)
{
  //...
  services.AddDataProtection().PersistKeysToServiceFabricDistributedCache(opt => {
    //Unique for this ASP.NET Core microservice, don't use a new GUID each time or you'll be back where you started without a single store for all the services
    opt.CacheStoreId = new Guid("b87d03b5-f8d1-456f-966d-11d2c4d9774d"); 

    //Specifically points to the service to use - if not set, it'll be automatically discovered
    opt.CacheStoreServiceUri = new Uri("fabric:/MyApplication/DataProtectionStore");   });
  //...
}

您可以在后一种方法中指定其他选项,以识别它应该使用的特定服务和唯一 ID(以便服务可以跨应用程序共享),但如果它在同一个应用程序中而您不这样做如果不指定这些选项,它会自己找到。

然后创建一个有状态服务实例,在反映服务名称的文件中从 NuGet 安装 SoCreate.Extensions.Caching.ServiceFabric,并将其从 StatefulService 继承替换为从 [=17= 继承].删除服务中的所有其他内容,但设置更新的构造函数。在名为“DataProtectionStore”的服务中,您的 DataProtectionStore.cs 文件应如下所示:

using System.Fabric;
using SoCreate.Extensions.Caching.ServiceFabric

namespace DataProtectionStore
{
  internal sealed class DataProtectionStore : DistributedCacheStoreService
  {
    protected override int MaxCacheSizeInMegabytes => 500; //Optional

    public DataProtectionStore(StatefulServiceContext context) : base(context, message => ServiceEventSource.Current.ServiceMessage(context, message))
    {
    }
  }
}

您可以找到其他有关在“网络场”或microservices/distributed 环境 here 中托管 ASP.NET Core 的有用指南。