Nguon: Microsoft Learn · .NET 8.0

Sử dụng MessagePack Hub Protocol trong SignalR cho ASP.NET Core

Nguồn: Use MessagePack Hub Protocol in SignalR for ASP.NET Core

Bài viết này giả định rằng người đọc đã quen thuộc với các chủ đề được đề cập trong Bắt đầu với ASP.NET Core SignalR.

MessagePack là gì?

MessagePack là định dạng tuần tự hóa (serialization) nhị phân nhanh và gọn nhẹ. Nó hữu ích khi hiệu suất và băng thông là mối quan tâm vì nó tạo ra các thông điệp nhỏ hơn so với JSON. Các thông điệp nhị phân không thể đọc được khi xem network traces và logs trừ khi các byte được truyền qua một MessagePack parser. SignalR có hỗ trợ tích hợp cho định dạng MessagePack và cung cấp API cho client và server sử dụng.

Cấu hình MessagePack trên server

Để bật MessagePack Hub Protocol trên server, cài đặt package Microsoft.AspNetCore.SignalR.Protocols.MessagePack trong ứng dụng của bạn. Trong phương thức Startup.ConfigureServices, thêm AddMessagePackProtocol vào lệnh gọi AddSignalR để bật hỗ trợ MessagePack trên server.

csharp
services.AddSignalR()
    .AddMessagePackProtocol();

Lưu ý: JSON được bật theo mặc định. Thêm MessagePack sẽ bật hỗ trợ cho cả client JSON và MessagePack.

Để tùy chỉnh cách MessagePack định dạng dữ liệu, AddMessagePackProtocol nhận một delegate để cấu hình tùy chọn. Trong delegate đó, thuộc tính SerializerOptions được sử dụng để cấu hình các tùy chọn tuần tự hóa MessagePack. Để biết thêm thông tin về cách các resolver hoạt động, hãy truy cập thư viện MessagePack tại MessagePack-CSharp. Các attribute có thể được sử dụng trên các đối tượng bạn muốn tuần tự hóa để xác định cách chúng sẽ được xử lý.

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

Cảnh báo: Chúng tôi khuyến nghị xem xét CVE-2020-5234 và áp dụng các bản vá được đề xuất. Ví dụ: gọi .WithSecurity(MessagePackSecurity.UntrustedData) khi thay thế SerializerOptions.

Cấu hình MessagePack trên client

Lưu ý: JSON được bật theo mặc định cho các client được hỗ trợ. Các client chỉ có thể hỗ trợ một protocol duy nhất. Thêm hỗ trợ MessagePack sẽ thay thế bất kỳ protocol nào đã được cấu hình trước đó.

.NET client

Để bật MessagePack trong .NET Client, cài đặt package Microsoft.AspNetCore.SignalR.Protocols.MessagePack và gọi AddMessagePackProtocol trên HubConnectionBuilder.

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

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

Lưu ý: Lệnh gọi AddMessagePackProtocol này nhận một delegate để cấu hình tùy chọn giống như trên server.

JavaScript client

Hỗ trợ MessagePack cho JavaScript client được cung cấp bởi package npm @microsoft/signalr-protocol-msgpack. Cài đặt package bằng cách thực hiện lệnh sau trong command shell:

bash
npm install @microsoft/signalr-protocol-msgpack

Sau khi cài đặt package npm, module có thể được sử dụng trực tiếp qua JavaScript module loader hoặc được nhập vào trình duyệt bằng cách tham chiếu file sau:

node\_modules\@microsoft\signalr-protocol-msgpack\dist\browser\signalr-protocol-msgpack.js

Các file JavaScript bắt buộc phải được tham chiếu theo thứ tự như sau:

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

Thêm .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) vào HubConnectionBuilder để cấu hình client sử dụng MessagePack protocol khi kết nối đến server.

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

Hiện tại, không có tùy chọn cấu hình nào cho MessagePack protocol trên JavaScript client.

Java client

Để bật MessagePack với Java, cài đặt package com.microsoft.signalr.messagepack. Khi sử dụng Gradle, thêm dòng sau vào phần dependencies của file build.gradle:

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

Khi sử dụng Maven, thêm các dòng sau vào trong phần tử <dependencies> của file pom.xml:

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

Gọi withHubProtocol(new MessagePackHubProtocol()) trên HubConnectionBuilder.

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

Các lưu ý về MessagePack

Có một số vấn đề cần lưu ý khi sử dụng MessagePack Hub Protocol.

MessagePack phân biệt chữ hoa/thường (case-sensitive)

MessagePack protocol phân biệt chữ hoa/thường. Ví dụ, hãy xem xét lớp C# sau:

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

Khi gửi từ JavaScript client, bạn phải sử dụng tên thuộc tính PascalCased, vì chữ hoa/thường phải khớp chính xác với lớp C#. Ví dụ:

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

Sử dụng tên camelCased sẽ không bind đúng với lớp C#. Bạn có thể giải quyết điều này bằng cách sử dụng attribute Key để chỉ định tên khác cho thuộc tính MessagePack. Để biết thêm thông tin, xem tài liệu MessagePack-CSharp.

DateTime.Kind không được bảo toàn khi tuần tự hóa/giải tuần tự hóa

MessagePack protocol không cung cấp cách mã hóa giá trị Kind của DateTime. Do đó, khi giải tuần tự hóa một ngày, MessagePack Hub Protocol sẽ chuyển đổi sang định dạng UTC nếu DateTime.KindDateTimeKind.Local, nếu không, nó sẽ không thay đổi thời gian và truyền nguyên như vậy. Nếu bạn đang làm việc với các giá trị DateTime, chúng tôi khuyên bạn nên chuyển đổi sang UTC trước khi gửi chúng. Chuyển đổi chúng từ UTC sang giờ địa phương khi bạn nhận chúng.

Hỗ trợ MessagePack trong môi trường biên dịch "ahead-of-time"

Thư viện MessagePack-CSharp được sử dụng bởi .NET client và server sử dụng code generation để tối ưu hóa tuần tự hóa. Do đó, nó không được hỗ trợ theo mặc định trong các môi trường sử dụng biên dịch "ahead-of-time", chẳng hạn như NET Multi-platform App UI (.NET MAUI) hoặc Unity. Có thể sử dụng MessagePack trong các môi trường này bằng cách "pre-generating" code serializer/deserializer. Để biết thêm thông tin, xem tài liệu MessagePack-CSharp. Sau khi đã pre-generate các serializer, bạn có thể đăng ký chúng bằng cách sử dụng configuration delegate được truyền cho AddMessagePackProtocol:

csharp
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);
    });

Kiểm tra kiểu nghiêm ngặt hơn trong MessagePack

JSON Hub Protocol sẽ thực hiện chuyển đổi kiểu trong quá trình giải tuần tự hóa. Ví dụ: nếu đối tượng đến có giá trị thuộc tính là số ({ foo: 42 }) nhưng thuộc tính trong lớp .NET có kiểu string, giá trị sẽ được chuyển đổi. Tuy nhiên, MessagePack không thực hiện chuyển đổi này và sẽ ném ra ngoại lệ có thể thấy trong server-side logs (và trong console):

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

Để biết thêm thông tin về hạn chế này, xem GitHub issue aspnet/SignalR#2937.

Chars và Strings trong Java

Trong Java client, các đối tượng char sẽ được tuần tự hóa dưới dạng đối tượng String một ký tự. Điều này trái ngược với C# và JavaScript client, vốn tuần tự hóa chúng dưới dạng đối tượng short. Bản thân đặc tả MessagePack không định nghĩa hành vi cho các đối tượng char, vì vậy tác giả thư viện quyết định cách tuần tự hóa chúng. Sự khác biệt trong hành vi giữa các client là kết quả của các thư viện được sử dụng trong cách triển khai.