在 SignalR 中使用 MessagePack 集线器协议 ASP.NET CoreUse MessagePack Hub Protocol in SignalR for ASP.NET Core

本文内容

作者: Brennan Conroy

本文假定读者熟悉入门中介绍的主题。

什么是 MessagePack?What is MessagePack?

MessagePack是一种快速且紧凑的二进制序列化格式。当性能和带宽需要考虑时,它很有用,因为它会创建比JSON更小的消息。由于它是二进制格式,因此在查看网络跟踪和日志时会无法读取消息,除非这些字节是通过 MessagePack 分析器传递的。 SignalR 提供对 MessagePack 格式的内置支持,并为客户端和服务器提供要使用的 Api。

在服务器上配置 MessagePackConfigure MessagePack on the server

若要在服务器上启用 MessagePack 集线器协议,请在应用程序中安装 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 包。在 Startup.cs 文件中,将 AddMessagePackProtocol 添加到 AddSignalR 调用,以在服务器上启用 MessagePack 支持。

备注

默认情况下启用 JSON。添加 MessagePack 可支持 JSON 和 MessagePack 客户端。

  1. services.AddSignalR()
  2. .AddMessagePackProtocol();

若要自定义 MessagePack 如何设置数据的格式,AddMessagePackProtocol 获取用于配置选项的委托。在该委托中,FormatterResolvers 属性可用于配置 MessagePack 序列化选项。有关解析程序工作方式的详细信息,请访问MessagePack-CSharp上的 MessagePack 库。属性可用于要序列化的对象,以定义应如何处理它们。

  1. services.AddSignalR()
  2. .AddMessagePackProtocol(options =>
  3. {
  4. options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
  5. {
  6. MessagePack.Resolvers.StandardResolver.Instance
  7. };
  8. });

警告

强烈建议查看CVE-2020-5234和应用建议的修补程序。例如,将 MessagePackSecurity.Active 静态属性设置为 MessagePackSecurity.UntrustedData设置 MessagePackSecurity.Active 需要手动安装MessagePack 的1.9 版。安装 MessagePack 1.9. x 升级 SignalR 使用的版本。如果 MessagePackSecurity.Active 未设置为 MessagePackSecurity.UntrustedData,则恶意客户端可能会导致拒绝服务。MessagePackSecurity.Active 设置 Program.Main中,如下面的代码所示:

  1. public static void Main(string[] args)
  2. {
  3. MessagePackSecurity.Active = MessagePackSecurity.UntrustedData;
  4. CreateHostBuilder(args).Build().Run();
  5. }

在客户端上配置 MessagePackConfigure MessagePack on the client

备注

默认情况下,为支持的客户端启用 JSON。客户端只能支持一个协议。添加 MessagePack 支持将替换任何以前配置的协议。

.NET 客户端.NET client

若要在 .NET 客户端中启用 MessagePack,请安装 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 包,并 AddMessagePackProtocol HubConnectionBuilder上调用。

  1. var hubConnection = new HubConnectionBuilder()
  2. .WithUrl("/chatHub")
  3. .AddMessagePackProtocol()
  4. .Build();

备注

AddMessagePackProtocol 调用采用一个委托来配置与服务器类似的选项。

JavaScript 客户端JavaScript client

@microsoft/signalr-protocol-msgpack npm 包提供对 JavaScript 客户端的 MessagePack 支持。通过在命令行界面中执行以下命令来安装包:

  1. npm install @microsoft/signalr-protocol-msgpack

@aspnet/signalr-protocol-msgpack npm 包提供对 JavaScript 客户端的 MessagePack 支持。通过在命令行界面中执行以下命令来安装包:

  1. npm install @aspnet/signalr-protocol-msgpack

安装 npm 包后,可以通过 JavaScript 模块加载程序直接使用该模块,或通过引用以下文件将该模块导入到浏览器中:

node_modules\@microsoft\signalr-protocol-msgpack\dist\browser\signalr-protocol-msgpack.js

node_modules\@aspnet\signalr-protocol-msgpack\dist\browser\signalr-protocol-msgpack.js

在浏览器中,还必须引用 msgpack5 库。使用 <script> 标记创建一个引用。可在node_modules \msgpack5\dist\msgpack5.js找到库。

备注

使用 <script> 元素时,顺序很重要。如果在msgpack5之前引用signalr-protocol-msgpack ,则在尝试与 MessagePack 连接时将出现错误。signalrsignalr-protocol-msgpack之前也是必需的。

  1. <script src="~/lib/signalr/signalr.js"></script>
  2. <script src="~/lib/msgpack5/msgpack5.js"></script>
  3. <script src="~/lib/signalr/signalr-protocol-msgpack.js"></script>

HubConnectionBuilder 中添加 .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) 会将客户端配置为在连接到服务器时使用 MessagePack 协议。

  1. const connection = new signalR.HubConnectionBuilder()
  2. .withUrl("/chatHub")
  3. .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
  4. .build();

备注

目前,JavaScript 客户端上没有用于 MessagePack 协议的配置选项。

MessagePack 兼容MessagePack quirks

使用 MessagePack 集线器协议时,需要注意几个问题。

MessagePack 区分大小写MessagePack is case-sensitive

MessagePack 协议区分大小写。例如,请看下面C#的类:

  1. public class ChatMessage
  2. {
  3. public string Sender { get; }
  4. public string Message { get; }
  5. }

从 JavaScript 客户端发送时,必须使用 PascalCased 属性名称,因为大小写必须与C#类完全匹配。例如:

  1. connection.invoke("SomeMethod", { Sender: "Sally", Message: "Hello!" });

使用 camelCased 名称不会正确绑定到C#类。可以通过使用 Key 特性为 MessagePack 属性指定一个不同的名称来解决此情况。有关详细信息,请参阅MessagePack-CSharp 文档

序列化/反序列化时不保留 DateTime. KindDateTime.Kind is not preserved when serializing/deserializing

MessagePack 协议不提供对 DateTimeKind 值进行编码的方法。因此,在对日期进行反序列化时,MessagePack Hub 协议假设传入日期为 UTC 格式。如果你在本地时间使用 DateTime 值,则建议在发送这些值之前将其转换为 UTC。接收到本地时间时将它们从 UTC 转换为本地时间。

有关此限制的详细信息,请参阅 GitHub issue aspnet/SignalR#2632

JavaScript 中的 MessagePack 不支持 MinValueDateTime.MinValue is not supported by MessagePack in JavaScript

SignalR JavaScript 客户端使用的msgpack5库不支持 MessagePack 中的 timestamp96 类型。此类型用于编码非常大的日期值(在过去或未来很大程度上)。DateTime.MinValue 的值 January 1, 0001 必须用 timestamp96 值进行编码。因此,不支持向 JavaScript 客户端发送 DateTime.MinValue当 JavaScript 客户端接收到 DateTime.MinValue 时,将引发以下错误:

  1. Uncaught Error: unable to find ext type 255 at decoder.js:427

通常,DateTime.MinValue 用于对 "缺少" 或 null 值进行编码。如果需要在 MessagePack 中对该值进行编码,请使用可以为 null 的 DateTime 值(DateTime?),或对表示日期是否存在的单独 bool 值进行编码。

有关此限制的详细信息,请参阅 GitHub issue aspnet/SignalR#2228

"提前" 编译环境中的 MessagePack 支持MessagePack support in "ahead-of-time" compilation environment

.NET 客户端和服务器使用的MessagePack-CSharp库使用代码生成来优化序列化。因此,默认情况下,在使用 "提前" 编译(如 Xamarin iOS 或 Unity)的环境中,默认情况下不支持此方法。可以通过 "预生成" 序列化程序/反序列化程序代码,在这些环境中使用 MessagePack。有关详细信息,请参阅MessagePack-CSharp 文档预生成序列化程序后,可以使用传递给 AddMessagePackProtocol的配置委托注册它们:

  1. services.AddSignalR()
  2. .AddMessagePackProtocol(options =>
  3. {
  4. options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
  5. {
  6. MessagePack.Resolvers.GeneratedResolver.Instance,
  7. MessagePack.Resolvers.StandardResolver.Instance
  8. };
  9. });

类型检查在 MessagePack 中更加严格Type checks are more strict in MessagePack

JSON 集线器协议将在反序列化过程中执行类型转换。例如,如果传入的对象的属性值为数字({ foo: 42 }),但 .NET 类的属性为类型 string,则将转换该值。但是,MessagePack 不会执行此转换,并将引发可在服务器端日志中显示的异常(在控制台中):

  1. InvalidDataException: Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.

有关此限制的详细信息,请参阅 GitHub issue aspnet/SignalR#2937

相关资源Related resources