共用方式為


在適用於 ASP.NET Core 的 SignalR 中使用 MessagePack 中樞通訊協定

本文假設讀者已熟悉開始使用 ASP.NET Core SignalR 中所函蓋的主題。

什麼是 MessagePack?

MessagePack 是一種快速、精簡的二進位序列化格式。 在有效能和頻寬考量時此格式非常有用,因為它會建立比 JSON更小的訊息。 查看網路追蹤和記錄時,除非位元組是透過 MessagePack 剖析器傳遞,否則無法讀取二進位訊息。 SignalR 內建 MessagePack 格式支援,並提供用戶端和伺服器要使用的 API。

在伺服器上設定 MessagePack

若要在伺服器上啟用 MessagePack 中樞通訊協定,請在您的應用程式中安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件。 在 Startup.ConfigureServices 方法中,將 AddMessagePackProtocol 新增至 AddSignalR 呼叫,以便在伺服器上啟用 MessagePack 支援。

services.AddSignalR()
    .AddMessagePackProtocol();

注意

JSON 預設為啟用。 新增 MessagePack 可同時支援 JSON 和 MessagePack 用戶端。

若要自訂 MessagePack 格式化資料的方式,AddMessagePackProtocol 可接受委派來設定選項。 在該委派中,可以使用 SerializerOptions 屬性來設定 MessagePack 序列化選項。 如需解析器運作方式的詳細資訊,請造訪 MessagePack-CSharp 的 MessagePack 程式庫。 您可以在想要序列化的物件上使用這些屬性,以定義處理方式。

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.SerializerOptions = MessagePackSerializerOptions.Standard
            .WithResolver(new CustomResolver())
            .WithSecurity(MessagePackSecurity.UntrustedData);
    });

警告

強烈建議您檢閱 CVE-2020-5234,並套用建議的修補檔。 例如,取代 SerializerOptions 時呼叫 .WithSecurity(MessagePackSecurity.UntrustedData)

在用戶端上設定 MessagePack

注意

支援的用戶端預設啟用 JSON。 用戶端只能支援單一通訊協定。 新增 MessagePack 支援會取代任何先前設定的通訊協定。

.NET 用戶端

若要在 .NET 用戶端啟用 MessagePack,請安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件並在 HubConnectionBuilder 呼叫 AddMessagePackProtocol

using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;

var hubConnection = new HubConnectionBuilder()
                        .WithUrl("/chathub")
                        .AddMessagePackProtocol()
                        .Build();

注意

這個 AddMessagePackProtocol 呼叫會接受委派來設定選項,就像伺服器一樣。

JavaScript 用戶端

JavaScript 用戶端的 MessagePack 支援是由 @microsoft/signalr-protocol-msgpack npm 套件提供。 在命令殼層中執行下列命令,以安裝套件:

npm install @microsoft/signalr-protocol-msgpack

安裝 npm 套件之後,可以直接透過 JavaScript 模組載入器使用模組,或參考下列檔案將模組匯入瀏覽器:

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

下列必要的 javaScript 檔案必須依照下面顯示的順序參考:

<script src="~/lib/signalr/signalr.js"></script>
<script src="~/lib/signalr/signalr-protocol-msgpack.js"></script>

.withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) 新增至 HubConnectionBuilder 會將用戶端設定為連線到伺服器時使用 MessagePack 通訊協定。

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
    .build();

目前,JavaScript 用戶端沒有 MessagePack 通訊協定的設定選項。

Java 用戶端

若要使用 Java 啟用 MessagePack,請安裝 com.microsoft.signalr.messagepack 套件。 使用 Gradle 時,將下列這一行程式碼新增至 build.gradle 檔案的 dependencies 區段:

implementation 'com.microsoft.signalr.messagepack:signalr-messagepack:5.0.0'

使用 Maven 時,請在 pom.xml 檔案的 <dependencies> 元素內新增下列幾行:

<dependency>
    <groupId>com.microsoft.signalr.messagepack</groupId>
    <artifactId>signalr</artifactId>
    <version>5.0.0</version>
</dependency>

HubConnectionBuilder 上呼叫 withHubProtocol(new MessagePackHubProtocol())

HubConnection messagePackConnection = HubConnectionBuilder.create("YOUR HUB URL HERE")
    .withHubProtocol(new MessagePackHubProtocol())
    .build();

MessagePack 注意事項

使用 MessagePack 中樞通訊協定時,必須注意幾個問題。

MessagePack 會區分大小寫

MessagePack 通訊協定會區分大小寫。 例如,請參考下列 C# 類別:

public class ChatMessage
{
    public string Sender { get; }
    public string Message { get; }
}

從 JavaScript 用戶端傳送時,您必須使用 PascalCased 屬性名稱,因為大小寫必須完全符合 C# 類別。 例如:

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

使用 camelCased 名稱無法正確繫結至 C# 類別。 您可以使用 Key 屬性為 MessagePack 屬性指定不同的名稱,以解決此問題。 如需詳細資訊,請參閱 MessagePack-CSharp 文件

序列化/還原序列化時,不會保留 DateTime.Kind

MessagePack 通訊協定無法編碼 DateTimeKind 值。 因此,還原日期的序列化時,如果 DateTime.KindDateTimeKind.Local,則 MessagePack 中樞通訊協定會轉換成 UTC 格式,否則不會轉換時間並照原樣傳遞。 如果您是使用 DateTime 值,建議您先轉換成 UTC 再傳送。 當您收到時間時,請將它們從 UTC 轉換為當地時間。

「預先」編譯環境中的 MessagePack 支援

.NET 用戶端使用 MessagePack-CSharp 程式庫,而伺服器則利用程式碼產生功能來最佳化序列化。 因此,預設不支援使用「預先」編譯的環境 (例如 Xamarin iOS 或 Unity)。 您可以透過「預先產生」序列化程式/還原序列化程式碼,在這些環境中使用 MessagePack。 如需詳細資訊,請參閱 MessagePack-CSharp 文件。 在預先產生序列化程式之後,您可以使用傳遞至 AddMessagePackProtocol 的設定委派進行註冊:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        StaticCompositeResolver.Instance.Register(
            MessagePack.Resolvers.GeneratedResolver.Instance,
            MessagePack.Resolvers.StandardResolver.Instance
        );
        options.SerializerOptions = MessagePackSerializerOptions.Standard
            .WithResolver(StaticCompositeResolver.Instance)
            .WithSecurity(MessagePackSecurity.UntrustedData);
    });

MessagePack 中的型別檢查更為嚴格

JSON 中樞通訊協定會在還原序列化期間執行型別轉換。 例如,如果傳入物件的屬性值是數字 ({ foo: 42 }),但 .NET 類別中的屬性屬於 string 型別,則會轉換值。 不過,MessagePack 不會執行此轉換,而且會擲回顯示在伺服器端記錄 (以及在主控台) 中的例外狀況:

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

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2937

Java 中的字元和字串

在 Java 用戶端中,char 物件會序列化為一個字元的 String 物件。 這與將那些物件序列化為 short 物件的 C# 和 JavaScript 用戶端形成對比。 MessagePack 規格本身不會定義 char 物件的行為,因此由程式庫作者決定如何序列化它們。 用戶端之間的行為差異是因為我們用於實作的程式庫不同。

其他資源

本文假設讀者已熟悉開始使用 ASP.NET Core SignalR 中所函蓋的主題。

什麼是 MessagePack?

MessagePack 是一種快速、精簡的二進位序列化格式。 在有效能和頻寬考量時此格式非常有用,因為它會建立比 JSON 更小的訊息。 查看網路追蹤和記錄時,除非位元組是透過 MessagePack 剖析器傳遞,否則無法讀取二進位訊息。 SignalR 內建 MessagePack 格式支援,並提供用戶端和伺服器要使用的 API。

在伺服器上設定 MessagePack

若要在伺服器上啟用 MessagePack 中樞通訊協定,請在您的應用程式中安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件。 在 Startup.ConfigureServices 方法中,將 AddMessagePackProtocol 新增至 AddSignalR 呼叫,以便在伺服器上啟用 MessagePack 支援。

注意

JSON 預設為啟用。 新增 MessagePack 可同時支援 JSON 和 MessagePack 用戶端。

services.AddSignalR()
    .AddMessagePackProtocol();

若要自訂 MessagePack 格式化資料的方式,AddMessagePackProtocol 可接受委派來設定選項。 在該委派中,可以使用 SerializerOptions 屬性來設定 MessagePack 序列化選項。 如需解析器運作方式的詳細資訊,請造訪 MessagePack-CSharp 的 MessagePack 程式庫。 您可以在想要序列化的物件上使用這些屬性,以定義處理方式。

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.SerializerOptions = MessagePackSerializerOptions.Standard
            .WithResolver(new CustomResolver())
            .WithSecurity(MessagePackSecurity.UntrustedData);
    });

警告

強烈建議您檢閱 CVE-2020-5234,並套用建議的修補檔。 例如,取代 SerializerOptions 時呼叫 .WithSecurity(MessagePackSecurity.UntrustedData)

在用戶端上設定 MessagePack

注意

支援的用戶端預設啟用 JSON。 用戶端只能支援單一通訊協定。 新增 MessagePack 支援會取代任何先前設定的通訊協定。

.NET 用戶端

若要在 .NET 用戶端啟用 MessagePack,請安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件並在 HubConnectionBuilder 呼叫 AddMessagePackProtocol

using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;

var hubConnection = new HubConnectionBuilder()
                        .WithUrl("/chathub")
                        .AddMessagePackProtocol()
                        .Build();

注意

這個 AddMessagePackProtocol 呼叫會接受委派來設定選項,就像伺服器一樣。

JavaScript 用戶端

JavaScript 用戶端的 MessagePack 支援是由 @microsoft/signalr-protocol-msgpack npm 套件提供。 在命令殼層中執行下列命令,以安裝套件:

npm install @microsoft/signalr-protocol-msgpack

安裝 npm 套件之後,可以直接透過 JavaScript 模組載入器使用模組,或參考下列檔案將模組匯入瀏覽器:

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

在瀏覽器中,也必須參考 msgpack5 程式庫。 使用 <script> 標記來建立參考。 程式庫位於 node_modules\msgpack5\dist\msgpack5.js

注意

使用 <script> 元素時,順序很重要。 如果在 msgpack5.js 之前參考 signalr-protocol-msgpack.js,則在嘗試連線 MessagePack 時就會發生錯誤。 signalr.js 也必須在 signalr-protocol-msgpack.js 之前。

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

.withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) 新增至 HubConnectionBuilder 會將用戶端設定為連線到伺服器時使用 MessagePack 通訊協定。

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
    .build();

注意

目前,JavaScript 用戶端沒有 MessagePack 通訊協定的設定選項。

Java 用戶端

若要使用 Java 啟用 MessagePack,請安裝 com.microsoft.signalr.messagepack 套件。 使用 Gradle 時,將下列這一行程式碼新增至 build.gradle 檔案的 dependencies 區段:

implementation 'com.microsoft.signalr.messagepack:signalr-messagepack:5.0.0'

使用 Maven 時,請在 pom.xml 檔案的 <dependencies> 元素內新增下列幾行:

<dependency>
    <groupId>com.microsoft.signalr.messagepack</groupId>
    <artifactId>signalr</artifactId>
    <version>5.0.0</version>
</dependency>

HubConnectionBuilder 上呼叫 withHubProtocol(new MessagePackHubProtocol())

HubConnection messagePackConnection = HubConnectionBuilder.create("YOUR HUB URL HERE")
    .withHubProtocol(new MessagePackHubProtocol())
    .build();

MessagePack 注意事項

使用 MessagePack 中樞通訊協定時,必須注意幾個問題。

MessagePack 會區分大小寫

MessagePack 通訊協定會區分大小寫。 例如,請參考下列 C# 類別:

public class ChatMessage
{
    public string Sender { get; }
    public string Message { get; }
}

從 JavaScript 用戶端傳送時,您必須使用 PascalCased 屬性名稱,因為大小寫必須完全符合 C# 類別。 例如:

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

使用 camelCased 名稱無法正確繫結至 C# 類別。 您可以使用 Key 屬性為 MessagePack 屬性指定不同的名稱,以解決此問題。 如需詳細資訊,請參閱 MessagePack-CSharp 文件

序列化/還原序列化時,不會保留 DateTime.Kind

MessagePack 通訊協定無法編碼 DateTimeKind 值。 因此,還原日期的序列化時,如果 DateTime.KindDateTimeKind.Local,則 MessagePack 中樞通訊協定會轉換成 UTC 格式,否則不會轉換時間並照原樣傳遞。 如果您是使用 DateTime 值,建議您先轉換成 UTC 再傳送。 當您收到時間時,請將它們從 UTC 轉換為當地時間。

JavaScript 中的 MessagePack 不支援 DateTime.MinValue

SignalR JavaScript 用戶端使用的 msgpack5 程式庫不支援 MessagePack 中的 timestamp96 型別。 此型別是用來編碼非常大的日期值 (很早的過去或很遠的未來)。 DateTime.MinValue 的值是 January 1, 0001,必須在 timestamp96 值中編碼。 因此,不支援將 DateTime.MinValue 傳送至 JavaScript 用戶端。 當 JavaScript 用戶端收到 DateTime.MinValue 時,會擲回下列錯誤:

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

通常,DateTime.MinValue 是用來編碼「遺漏」或 null 值。 如果您需要在 MessagePack 中編碼該值,請使用可為 Null 的 DateTime 值 (DateTime?) 或編碼個別的 bool 值,指出該日期是否存在。

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2228

「預先」編譯環境中的 MessagePack 支援

.NET 用戶端使用 MessagePack-CSharp 程式庫,而伺服器則利用程式碼產生功能來最佳化序列化。 因此,預設不支援使用「預先」編譯的環境 (例如 Xamarin iOS 或 Unity)。 您可以透過「預先產生」序列化程式/還原序列化程式碼,在這些環境中使用 MessagePack。 如需詳細資訊,請參閱 MessagePack-CSharp 文件。 在預先產生序列化程式之後,您可以使用傳遞至 AddMessagePackProtocol 的設定委派進行註冊:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        StaticCompositeResolver.Instance.Register(
            MessagePack.Resolvers.GeneratedResolver.Instance,
            MessagePack.Resolvers.StandardResolver.Instance
        );
        options.SerializerOptions = MessagePackSerializerOptions.Standard
            .WithResolver(StaticCompositeResolver.Instance)
            .WithSecurity(MessagePackSecurity.UntrustedData);
    });

MessagePack 中的型別檢查更為嚴格

JSON 中樞通訊協定會在還原序列化期間執行型別轉換。 例如,如果傳入物件的屬性值是數字 ({ foo: 42 }),但 .NET 類別中的屬性屬於 string 型別,則會轉換值。 不過,MessagePack 不會執行此轉換,而且會擲回顯示在伺服器端記錄 (以及在主控台) 中的例外狀況:

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

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2937

Java 中的字元和字串

在 Java 用戶端中,char 物件會序列化為一個字元的 String 物件。 這與將那些物件序列化為 short 物件的 C# 和 JavaScript 用戶端形成對比。 MessagePack 規格本身不會定義 char 物件的行為,因此由程式庫作者決定如何序列化它們。 用戶端之間的行為差異是因為我們用於實作的程式庫不同。

其他資源

本文假設讀者已熟悉開始使用 ASP.NET Core SignalR 中所函蓋的主題。

什麼是 MessagePack?

MessagePack 是一種快速、精簡的二進位序列化格式。 在有效能和頻寬考量時此格式非常有用,因為它會建立比 JSON 更小的訊息。 查看網路追蹤和記錄時,除非位元組是透過 MessagePack 剖析器傳遞,否則無法讀取二進位訊息。 SignalR 內建 MessagePack 格式支援,並提供用戶端和伺服器要使用的 API。

在伺服器上設定 MessagePack

若要在伺服器上啟用 MessagePack 中樞通訊協定,請在您的應用程式中安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件。 在 Startup.ConfigureServices 方法中,將 AddMessagePackProtocol 新增至 AddSignalR 呼叫,以便在伺服器上啟用 MessagePack 支援。

注意

JSON 預設為啟用。 新增 MessagePack 可同時支援 JSON 和 MessagePack 用戶端。

services.AddSignalR()
    .AddMessagePackProtocol();

若要自訂 MessagePack 格式化資料的方式,AddMessagePackProtocol 可接受委派來設定選項。 在該委派中,可以使用 FormatterResolvers 屬性來設定 MessagePack 序列化選項。 如需解析器運作方式的詳細資訊,請造訪 MessagePack-CSharp 的 MessagePack 程式庫。 您可以在想要序列化的物件上使用這些屬性,以定義處理方式。

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
        {
            MessagePack.Resolvers.StandardResolver.Instance
        };
    });

警告

強烈建議您檢閱 CVE-2020-5234,並套用建議的修補檔。 例如,將 MessagePackSecurity.Active 靜態屬性設定為 MessagePackSecurity.UntrustedData。 設定 MessagePackSecurity.Active 需要手動安裝 1.9.x 版的 MessagePack。 安裝 MessagePack 1.9.x 會升級 SignalR 使用的版本。 MessagePack 2.x 版推出重大變更,且與 SignalR 3.1 版和更早版本不相容。 當 MessagePackSecurity.Active 未設定為 MessagePackSecurity.UntrustedData 時,惡意用戶端可能會造成拒絕服務。 在 Program.Main 中設定 MessagePackSecurity.Active,如下列程式碼所示:

using MessagePack;

public static void Main(string[] args)
{
  MessagePackSecurity.Active = MessagePackSecurity.UntrustedData;

  CreateHostBuilder(args).Build().Run();
}

在用戶端上設定 MessagePack

注意

支援的用戶端預設啟用 JSON。 用戶端只能支援單一通訊協定。 新增 MessagePack 支援會取代任何先前設定的通訊協定。

.NET 用戶端

若要在 .NET 用戶端啟用 MessagePack,請安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件並在 HubConnectionBuilder 呼叫 AddMessagePackProtocol

using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;

var hubConnection = new HubConnectionBuilder()
                        .WithUrl("/chathub")
                        .AddMessagePackProtocol()
                        .Build();

注意

這個 AddMessagePackProtocol 呼叫會接受委派來設定選項,就像伺服器一樣。

JavaScript 用戶端

JavaScript 用戶端的 MessagePack 支援是由 @microsoft/signalr-protocol-msgpack npm 套件提供。 在命令殼層中執行下列命令,以安裝套件:

npm install @microsoft/signalr-protocol-msgpack

安裝 npm 套件之後,可以直接透過 JavaScript 模組載入器使用模組,或參考下列檔案將模組匯入瀏覽器:

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

在瀏覽器中,也必須參考 msgpack5 程式庫。 使用 <script> 標記來建立參考。 程式庫位於 node_modules\msgpack5\dist\msgpack5.js

注意

使用 <script> 元素時,順序很重要。 如果在 msgpack5.js 之前參考 signalr-protocol-msgpack.js,則在嘗試連線 MessagePack 時就會發生錯誤。 signalr.js 也必須在 signalr-protocol-msgpack.js 之前。

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

.withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) 新增至 HubConnectionBuilder 會將用戶端設定為連線到伺服器時使用 MessagePack 通訊協定。

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
    .build();

注意

目前,JavaScript 用戶端沒有 MessagePack 通訊協定的設定選項。

MessagePack 注意事項

使用 MessagePack 中樞通訊協定時,必須注意幾個問題。

MessagePack 會區分大小寫

MessagePack 通訊協定會區分大小寫。 例如,請參考下列 C# 類別:

public class ChatMessage
{
    public string Sender { get; }
    public string Message { get; }
}

從 JavaScript 用戶端傳送時,您必須使用 PascalCased 屬性名稱,因為大小寫必須完全符合 C# 類別。 例如:

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

使用 camelCased 名稱無法正確繫結至 C# 類別。 您可以使用 Key 屬性為 MessagePack 屬性指定不同的名稱,以解決此問題。 如需詳細資訊,請參閱 MessagePack-CSharp 文件

序列化/還原序列化時,不會保留 DateTime.Kind

MessagePack 通訊協定無法編碼 DateTimeKind 值。 因此,還原日期的序列化時,MessagePack 中樞通訊協定會假設傳入日期為 UTC 格式。 如果您是使用當地時間的 DateTime 值,建議先轉換成 UTC 再傳送。 當您收到時間時,請將它們從 UTC 轉換為當地時間。

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2632

JavaScript 中的 MessagePack 不支援 DateTime.MinValue

SignalR JavaScript 用戶端使用的 msgpack5 程式庫不支援 MessagePack 中的 timestamp96 型別。 此型別是用來編碼非常大的日期值 (很早的過去或很遠的未來)。 DateTime.MinValue 的值是 January 1, 0001,必須在 timestamp96 值中編碼。 因此,不支援將 DateTime.MinValue 傳送至 JavaScript 用戶端。 當 JavaScript 用戶端收到 DateTime.MinValue 時,會擲回下列錯誤:

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

通常,DateTime.MinValue 是用來編碼「遺漏」或 null 值。 如果您需要在 MessagePack 中編碼該值,請使用可為 Null 的 DateTime 值 (DateTime?) 或編碼個別的 bool 值,指出該日期是否存在。

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2228

「預先」編譯環境中的 MessagePack 支援

.NET 用戶端使用 MessagePack-CSharp 程式庫,而伺服器則利用程式碼產生功能來最佳化序列化。 因此,預設不支援使用「預先」編譯的環境 (例如 Xamarin iOS 或 Unity)。 您可以透過「預先產生」序列化程式/還原序列化程式碼,在這些環境中使用 MessagePack。 如需詳細資訊,請參閱 MessagePack-CSharp 文件。 在預先產生序列化程式之後,您可以使用傳遞至 AddMessagePackProtocol 的設定委派進行註冊:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
        {
            MessagePack.Resolvers.GeneratedResolver.Instance,
            MessagePack.Resolvers.StandardResolver.Instance
        };
    });

MessagePack 中的型別檢查更為嚴格

JSON 中樞通訊協定會在還原序列化期間執行型別轉換。 例如,如果傳入物件的屬性值是數字 ({ foo: 42 }),但 .NET 類別中的屬性屬於 string 型別,則會轉換值。 不過,MessagePack 不會執行此轉換,而且會擲回顯示在伺服器端記錄 (以及在主控台) 中的例外狀況:

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

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2937

其他資源

本文假設讀者已熟悉開始使用 ASP.NET Core SignalR 中所函蓋的主題。

什麼是 MessagePack?

MessagePack 是一種快速、精簡的二進位序列化格式。 在有效能和頻寬考量時此格式非常有用,因為它會建立比 JSON 更小的訊息。 查看網路追蹤和記錄時,除非位元組是透過 MessagePack 剖析器傳遞,否則無法讀取二進位訊息。 SignalR 內建 MessagePack 格式支援,並提供用戶端和伺服器要使用的 API。

在伺服器上設定 MessagePack

若要在伺服器上啟用 MessagePack 中樞通訊協定,請在您的應用程式中安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件。 在 Startup.ConfigureServices 方法中,將 AddMessagePackProtocol 新增至 AddSignalR 呼叫,以便在伺服器上啟用 MessagePack 支援。

注意

JSON 預設為啟用。 新增 MessagePack 可同時支援 JSON 和 MessagePack 用戶端。

services.AddSignalR()
    .AddMessagePackProtocol();

若要自訂 MessagePack 格式化資料的方式,AddMessagePackProtocol 可接受委派來設定選項。 在該委派中,可以使用 FormatterResolvers 屬性來設定 MessagePack 序列化選項。 如需解析器運作方式的詳細資訊,請造訪 MessagePack-CSharp 的 MessagePack 程式庫。 您可以在想要序列化的物件上使用這些屬性,以定義處理方式。

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
        {
            MessagePack.Resolvers.StandardResolver.Instance
        };
    });

警告

強烈建議您檢閱 CVE-2020-5234,並套用建議的修補檔。 例如,將 MessagePackSecurity.Active 靜態屬性設定為 MessagePackSecurity.UntrustedData。 設定 MessagePackSecurity.Active 需要手動安裝 1.9.x 版的 MessagePack。 安裝 MessagePack 1.9.x 會升級 SignalR 使用的版本。 當 MessagePackSecurity.Active 未設定為 MessagePackSecurity.UntrustedData 時,惡意用戶端可能會造成拒絕服務。 在 Program.Main 中設定 MessagePackSecurity.Active,如下列程式碼所示:

using MessagePack;

public static void Main(string[] args)
{
  MessagePackSecurity.Active = MessagePackSecurity.UntrustedData;

  CreateHostBuilder(args).Build().Run();
}

在用戶端上設定 MessagePack

注意

支援的用戶端預設啟用 JSON。 用戶端只能支援單一通訊協定。 新增 MessagePack 支援會取代任何先前設定的通訊協定。

.NET 用戶端

若要在 .NET 用戶端啟用 MessagePack,請安裝 Microsoft.AspNetCore.SignalR.Protocols.MessagePack 套件並在 HubConnectionBuilder 呼叫 AddMessagePackProtocol

using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;

var hubConnection = new HubConnectionBuilder()
                        .WithUrl("/chathub")
                        .AddMessagePackProtocol()
                        .Build();

注意

這個 AddMessagePackProtocol 呼叫會接受委派來設定選項,就像伺服器一樣。

JavaScript 用戶端

JavaScript 用戶端的 MessagePack 支援是由 @aspnet/signalr-protocol-msgpack npm 套件提供。 在命令殼層中執行下列命令,以安裝套件:

npm install @aspnet/signalr-protocol-msgpack

安裝 npm 套件之後,可以直接透過 JavaScript 模組載入器使用模組,或參考下列檔案將模組匯入瀏覽器:

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

在瀏覽器中,也必須參考 msgpack5 程式庫。 使用 <script> 標記來建立參考。 程式庫位於 node_modules\msgpack5\dist\msgpack5.js

注意

使用 <script> 元素時,順序很重要。 如果在 msgpack5.js 之前參考 signalr-protocol-msgpack.js,則在嘗試連線 MessagePack 時就會發生錯誤。 signalr.js 也必須在 signalr-protocol-msgpack.js 之前。

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

.withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) 新增至 HubConnectionBuilder 會將用戶端設定為連線到伺服器時使用 MessagePack 通訊協定。

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
    .build();

注意

目前,JavaScript 用戶端沒有 MessagePack 通訊協定的設定選項。

MessagePack 注意事項

使用 MessagePack 中樞通訊協定時,必須注意幾個問題。

MessagePack 會區分大小寫

MessagePack 通訊協定會區分大小寫。 例如,請參考下列 C# 類別:

public class ChatMessage
{
    public string Sender { get; }
    public string Message { get; }
}

從 JavaScript 用戶端傳送時,您必須使用 PascalCased 屬性名稱,因為大小寫必須完全符合 C# 類別。 例如:

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

使用 camelCased 名稱無法正確繫結至 C# 類別。 您可以使用 Key 屬性為 MessagePack 屬性指定不同的名稱,以解決此問題。 如需詳細資訊,請參閱 MessagePack-CSharp 文件

序列化/還原序列化時,不會保留 DateTime.Kind

MessagePack 通訊協定無法編碼 DateTimeKind 值。 因此,還原日期的序列化時,MessagePack 中樞通訊協定會假設傳入日期為 UTC 格式。 如果您是使用當地時間的 DateTime 值,建議先轉換成 UTC 再傳送。 當您收到時間時,請將它們從 UTC 轉換為當地時間。

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2632

JavaScript 中的 MessagePack 不支援 DateTime.MinValue

SignalR JavaScript 用戶端使用的 msgpack5 程式庫不支援 MessagePack 中的 timestamp96 型別。 此型別是用來編碼非常大的日期值 (很早的過去或很遠的未來)。 DateTime.MinValue 的值是 January 1, 0001,必須在 timestamp96 值中編碼。 因此,不支援將 DateTime.MinValue 傳送至 JavaScript 用戶端。 當 JavaScript 用戶端收到 DateTime.MinValue 時,會擲回下列錯誤:

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

通常,DateTime.MinValue 是用來編碼「遺漏」或 null 值。 如果您需要在 MessagePack 中編碼該值,請使用可為 Null 的 DateTime 值 (DateTime?) 或編碼個別的 bool 值,指出該日期是否存在。

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2228

「預先」編譯環境中的 MessagePack 支援

.NET 用戶端使用 MessagePack-CSharp 程式庫,而伺服器則利用程式碼產生功能來最佳化序列化。 因此,預設不支援使用「預先」編譯的環境 (例如 Xamarin iOS 或 Unity)。 您可以透過「預先產生」序列化程式/還原序列化程式碼,在這些環境中使用 MessagePack。 如需詳細資訊,請參閱 MessagePack-CSharp 文件。 在預先產生序列化程式之後,您可以使用傳遞至 AddMessagePackProtocol 的設定委派進行註冊:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
        {
            MessagePack.Resolvers.GeneratedResolver.Instance,
            MessagePack.Resolvers.StandardResolver.Instance
        };
    });

MessagePack 中的型別檢查更為嚴格

JSON 中樞通訊協定會在還原序列化期間執行型別轉換。 例如,如果傳入物件的屬性值是數字 ({ foo: 42 }),但 .NET 類別中的屬性屬於 string 型別,則會轉換值。 不過,MessagePack 不會執行此轉換,而且會擲回顯示在伺服器端記錄 (以及在主控台) 中的例外狀況:

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

如需這項限制的詳細資訊,請參閱 GitHub 問題 aspnet/SignalR#2937

其他資源