Использование протокола MessagePack Hub для SignalR ASP.NET Core

В этой статье предполагается, что читатель знаком с темами, описанными в статье "Начало работы с ASP.NET Core SignalR".

Что такое MessagePack?

MessagePack — это быстрый и компактный формат двоичной сериализации. Это полезно, если производительность и пропускная способность являются проблемой, так как она создает небольшие сообщения, чем JSON. Двоичные сообщения недоступны при просмотре сетевых трассировок и журналов, если только байты не передаются через средство синтаксического анализа MessagePack. SignalR имеет встроенную поддержку формата MessagePack и предоставляет API-интерфейсы для использования клиента и сервера.

Настройка MessagePack на сервере

Чтобы включить протокол MessagePack Hub на сервере, установите Microsoft.AspNetCore.SignalR.Protocols.MessagePack пакет в приложении. В методе Startup.ConfigureServices добавьте AddMessagePackProtocol к вызову AddSignalR, чтобы включить поддержку MessagePack на сервере.

services.AddSignalR()
    .AddMessagePackProtocol();

Примечание.

JSON включен по умолчанию. Добавление MessagePack обеспечивает поддержку клиентов JSON и MessagePack.

Чтобы настроить то, как MessagePack форматирует данные, AddMessagePackProtocol принимает делегат, позволяющий настроить параметры. В этом делегате свойство SerializerOptions используется для настройки параметров сериализации MessagePack. Для получения дополнительной информации о том, как работают резолверы, посетите библиотеку MessagePack MessagePack-CSharp. Атрибуты можно использовать для объектов, которые необходимо сериализовать, чтобы определить способ их обработки.

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

Предупреждение

Настоятельно рекомендуется просматривать CVE-2020-5234 и применять рекомендуемые исправления. Например, вызов .WithSecurity(MessagePackSecurity.UntrustedData) при замене SerializerOptions.

Настройка MessagePack на клиенте

Примечание.

JSON включен по умолчанию для поддерживаемых клиентов. Клиенты могут поддерживать только один протокол. Добавление поддержки MessagePack заменяет все ранее настроенные протоколы.

Клиент .NET

Чтобы включить MessagePack в клиенте .NET, установите пакет Microsoft.AspNetCore.SignalR.Protocols.MessagePack и вызовите AddMessagePackProtocol для HubConnectionBuilder.

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

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

Примечание.

Этот AddMessagePackProtocol вызов принимает делегат для настройки параметров так же, как сервер.

Клиент на JavaScript

Поддержка MessagePack для клиента JavaScript предоставляется пакетом npm @microsoft/signalr-protocol-msgpack . Установите пакет, выполнив следующую команду в командной оболочке:

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();

В настоящее время нет параметров конфигурации для протокола MessagePack на клиенте JavaScript.

Клиент на Java

Чтобы включить MessagePack с Java, установите com.microsoft.signalr.messagepack пакет. При использовании Gradle добавьте следующую строку в dependencies раздел файла build.gradle :

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

При использовании Maven добавьте следующие строки внутрь элемента <dependencies> файла pom.xml:

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

Позвоните withHubProtocol(new MessagePackHubProtocol()) через HubConnectionBuilder.

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

Рекомендации по MessagePack

При использовании протокола MessagePack Hub существует несколько проблем.

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 не предусматривает способа кодирования значения Kind объекта DateTime. В результате, при десериализации даты протокол MessagePack Hub преобразует её в формат UTC, если DateTime.Kind имеет значение DateTimeKind.Local; в противном случае он не будет изменять время, а передаст его как есть. Если вы работаете со значениями DateTime , рекомендуется преобразовать их в формате UTC перед отправкой. Преобразуйте их из UTC в местное время при получении.

Поддержка MessagePack в среде опережающей компиляции

Библиотека MessagePack-CSharp, используемая клиентом и сервером .NET, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, многоплатформенный пользовательский интерфейс приложения NET (.NET MAUI) или 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 Hub будет выполнять преобразования типов во время десериализации. Например, если входящий объект имеет значение свойства, которое является числом ({ 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 issue aspnet/SignalR#2937.

Символы и строки в Java

В клиенте char Java объекты будут сериализованы как однозначные String объекты. Это отличается от клиента C# и JavaScript, который сериализует их в виде short объектов. Сама спецификация MessagePack не определяет поведение для char объектов, поэтому автор библиотеки может определить, как сериализовать их. Разница в поведении между нашими клиентами является результатом библиотек, которые мы использовали для наших реализаций.

Дополнительные ресурсы

В этой статье предполагается, что читатель знаком с темами, описанными в статье "Начало работы с ASP.NET Core SignalR".

Что такое MessagePack?

MessagePack — это быстрый и компактный формат двоичной сериализации. Это полезно, если производительность и пропускная способность являются проблемой, так как она создает небольшие сообщения по сравнению с JSON. Двоичные сообщения недоступны при просмотре сетевых трассировок и журналов, если только байты не передаются через средство синтаксического анализа MessagePack. SignalR имеет встроенную поддержку формата MessagePack и предоставляет API-интерфейсы для использования клиента и сервера.

Настройка MessagePack на сервере

Чтобы включить протокол MessagePack Hub на сервере, установите Microsoft.AspNetCore.SignalR.Protocols.MessagePack пакет в приложении. В методе Startup.ConfigureServices добавьте AddMessagePackProtocol к вызову AddSignalR, чтобы включить поддержку MessagePack на сервере.

Примечание.

JSON включен по умолчанию. Добавление MessagePack обеспечивает поддержку клиентов JSON и MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Чтобы настроить формат, в котором MessagePack будет представлять ваши данные, AddMessagePackProtocol принимает делегат для настройки параметров. В этом делегате свойство SerializerOptions можно использовать для настройки параметров сериализации MessagePack. Для получения дополнительной информации о том, как работают резолверы, посетите библиотеку MessagePack MessagePack-CSharp. Атрибуты можно использовать для объектов, которые необходимо сериализовать, чтобы определить способ их обработки.

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

Предупреждение

Настоятельно рекомендуется просматривать CVE-2020-5234 и применять рекомендуемые исправления. Например, вызов .WithSecurity(MessagePackSecurity.UntrustedData) при замене SerializerOptions.

Настройка MessagePack на клиенте

Примечание.

JSON включен по умолчанию для поддерживаемых клиентов. Клиенты могут поддерживать только один протокол. Добавление поддержки MessagePack заменит все ранее настроенные протоколы.

Клиент .NET

Чтобы включить MessagePack в клиенте .NET, установите пакет Microsoft.AspNetCore.SignalR.Protocols.MessagePack и вызовите AddMessagePackProtocol для HubConnectionBuilder.

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

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

Примечание.

Этот AddMessagePackProtocol вызов принимает делегат для настройки параметров так же, как сервер.

Клиент на JavaScript

Поддержка MessagePack для клиента JavaScript предоставляется пакетом npm @microsoft/signalr-protocol-msgpack . Установите пакет, выполнив следующую команду в командной оболочке:

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> элемента порядок важен. Если signalr-protocol-msgpack.js ссылка указана раньше msgpack5.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();

Примечание.

В настоящее время нет параметров конфигурации для протокола MessagePack на клиенте JavaScript.

Клиент на Java

Чтобы включить MessagePack с Java, установите com.microsoft.signalr.messagepack пакет. При использовании Gradle добавьте следующую строку в dependencies раздел файла build.gradle :

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

При использовании Maven добавьте следующие строки внутрь элемента <dependencies> файла pom.xml:

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

Позвоните withHubProtocol(new MessagePackHubProtocol()) через HubConnectionBuilder.

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

Рекомендации по MessagePack

При использовании протокола MessagePack Hub существует несколько проблем.

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 не предусматривает способа кодирования значения Kind объекта DateTime. В результате, при десериализации даты протокол MessagePack Hub преобразует её в формат UTC, если DateTime.Kind имеет значение DateTimeKind.Local; в противном случае он не будет изменять время, а передаст его как есть. Если вы работаете со значениями DateTime , рекомендуется преобразовать их в формате UTC перед отправкой. Преобразуйте их из UTC в местное время при получении.

DateTime.MinValue не поддерживается MessagePack в JavaScript

Библиотека msgpack5, используемая в клиенте SignalR JavaScript, не поддерживает тип timestamp96 в MessagePack. Этот тип используется для кодирования очень больших значений дат (либо в начале прошлого или очень далеко в будущем). Значение DateTime.MinValueJanuary 1, 0001, и оно должно быть закодировано в значении timestamp96. Из-за этого отправка DateTime.MinValue клиенту JavaScript не поддерживается. При DateTime.MinValue получении клиентом JavaScript возникает следующая ошибка:

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

DateTime.MinValue Обычно используется для кодирования "отсутствующих" или null значений. Если необходимо закодировать это значение в MessagePack, используйте значение DateTime, допускающее значение NULL (DateTime?), или закодируйте отдельное значение bool, указывающее, указана ли дата.

Дополнительные сведения об этом ограничении см. в статье GitHub о проблеме aspnet/SignalR#2228.

Поддержка MessagePack в среде опережающей компиляции

Библиотека MessagePack-CSharp, используемая клиентом и сервером .NET, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, многоплатформенный пользовательский интерфейс приложения NET (.NET MAUI) или 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 Hub будет выполнять преобразования типов во время десериализации. Например, если входящий объект имеет значение свойства, которое является числом ({ 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 issue aspnet/SignalR#2937.

Символы и строки в Java

В клиенте char Java объекты будут сериализованы как однозначные String объекты. Это отличается от клиента C# и JavaScript, который сериализует их в виде short объектов. Сама спецификация MessagePack не определяет поведение для char объектов, поэтому автор библиотеки может определить, как сериализовать их. Разница в поведении между нашими клиентами является результатом библиотек, которые мы использовали для наших реализаций.

Дополнительные ресурсы

В этой статье предполагается, что читатель знаком с темами, описанными в статье "Начало работы с ASP.NET Core SignalR".

Что такое MessagePack?

MessagePack — это быстрый и компактный формат двоичной сериализации. Это полезно, если производительность и пропускная способность являются проблемой, так как она создает небольшие сообщения по сравнению с JSON. Двоичные сообщения недоступны при просмотре сетевых трассировок и журналов, если только байты не передаются через средство синтаксического анализа MessagePack. SignalR имеет встроенную поддержку формата MessagePack и предоставляет API-интерфейсы для использования клиента и сервера.

Настройка MessagePack на сервере

Чтобы включить протокол MessagePack Hub на сервере, установите Microsoft.AspNetCore.SignalR.Protocols.MessagePack пакет в приложении. В методе Startup.ConfigureServices добавьте AddMessagePackProtocol к вызову AddSignalR, чтобы включить поддержку MessagePack на сервере.

Примечание.

JSON включен по умолчанию. Добавление MessagePack обеспечивает поддержку клиентов JSON и MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Чтобы настроить формат, в котором MessagePack будет представлять ваши данные, AddMessagePackProtocol принимает делегат для настройки параметров. В этом делегате свойство FormatterResolvers можно использовать для настройки параметров сериализации MessagePack. Для получения дополнительной информации о том, как работают резолверы, посетите библиотеку MessagePack MessagePack-CSharp. Атрибуты можно использовать для объектов, которые необходимо сериализовать, чтобы определить способ их обработки.

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, вредоносный клиент может привести к отказу в обслуживании. Установите MessagePackSecurity.Active в Program.Main, как показано в следующем коде:

using MessagePack;

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

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

Настройка MessagePack на клиенте

Примечание.

JSON включен по умолчанию для поддерживаемых клиентов. Клиенты могут поддерживать только один протокол. Добавление поддержки MessagePack заменит все ранее настроенные протоколы.

Клиент .NET

Чтобы включить MessagePack в клиенте .NET, установите пакет Microsoft.AspNetCore.SignalR.Protocols.MessagePack и вызовите AddMessagePackProtocol для HubConnectionBuilder.

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

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

Примечание.

Этот AddMessagePackProtocol вызов принимает делегат для настройки параметров так же, как сервер.

Клиент на JavaScript

Поддержка MessagePack для клиента JavaScript предоставляется пакетом npm @microsoft/signalr-protocol-msgpack . Установите пакет, выполнив следующую команду в командной оболочке:

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> элемента порядок важен. Если signalr-protocol-msgpack.js ссылка указана раньше msgpack5.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();

Примечание.

В настоящее время нет параметров конфигурации для протокола MessagePack на клиенте JavaScript.

Рекомендации по MessagePack

При использовании протокола MessagePack Hub существует несколько проблем.

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 не предусматривает способа кодирования значения Kind объекта DateTime. В результате, при десериализации даты протокол MessagePack Hub предполагает, что входящая дата представлена в формате UTC. Если вы работаете со значениями DateTime в локальном времени, рекомендуется преобразовать их в формате UTC перед отправкой. Преобразуйте их из UTC в местное время при получении.

Дополнительные сведения об этом ограничении см. в статье GitHub о проблеме aspnet/SignalR#2632.

DateTime.MinValue не поддерживается MessagePack в JavaScript

Библиотека msgpack5, используемая в клиенте SignalR JavaScript, не поддерживает тип timestamp96 в MessagePack. Этот тип используется для кодирования очень больших значений дат (либо в начале прошлого или очень далеко в будущем). Значение DateTime.MinValueJanuary 1, 0001, и оно должно быть закодировано в значении timestamp96. Из-за этого отправка DateTime.MinValue клиенту JavaScript не поддерживается. При DateTime.MinValue получении клиентом JavaScript возникает следующая ошибка:

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

DateTime.MinValue Обычно используется для кодирования "отсутствующих" или null значений. Если необходимо закодировать это значение в MessagePack, используйте значение DateTime, допускающее значение NULL (DateTime?), или закодируйте отдельное значение bool, указывающее, указана ли дата.

Дополнительные сведения об этом ограничении см. в статье GitHub о проблеме aspnet/SignalR#2228.

Поддержка MessagePack в среде опережающей компиляции

Библиотека MessagePack-CSharp, используемая клиентом и сервером .NET, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, многоплатформенный пользовательский интерфейс приложения NET (.NET MAUI) или 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 Hub будет выполнять преобразования типов во время десериализации. Например, если входящий объект имеет значение свойства, которое является числом ({ 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 issue aspnet/SignalR#2937.

Дополнительные ресурсы

В этой статье предполагается, что читатель знаком с темами, описанными в статье "Начало работы с ASP.NET Core SignalR".

Что такое MessagePack?

MessagePack — это быстрый и компактный формат двоичной сериализации. Это полезно, если производительность и пропускная способность являются проблемой, так как она создает небольшие сообщения по сравнению с JSON. Двоичные сообщения недоступны при просмотре сетевых трассировок и журналов, если только байты не передаются через средство синтаксического анализа MessagePack. SignalR имеет встроенную поддержку формата MessagePack и предоставляет API-интерфейсы для использования клиента и сервера.

Настройка MessagePack на сервере

Чтобы включить протокол MessagePack Hub на сервере, установите Microsoft.AspNetCore.SignalR.Protocols.MessagePack пакет в приложении. В методе Startup.ConfigureServices добавьте AddMessagePackProtocol к вызову AddSignalR, чтобы включить поддержку MessagePack на сервере.

Примечание.

JSON включен по умолчанию. Добавление MessagePack обеспечивает поддержку клиентов JSON и MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Чтобы настроить формат, в котором MessagePack будет представлять ваши данные, AddMessagePackProtocol принимает делегат для настройки параметров. В этом делегате свойство FormatterResolvers можно использовать для настройки параметров сериализации MessagePack. Для получения дополнительной информации о том, как работают резолверы, посетите библиотеку MessagePack MessagePack-CSharp. Атрибуты можно использовать для объектов, которые необходимо сериализовать, чтобы определить способ их обработки.

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, вредоносный клиент может вызвать отказ в обслуживании. Установите MessagePackSecurity.Active в Program.Main, как показано в следующем коде:

using MessagePack;

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

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

Настройка MessagePack на клиенте

Примечание.

JSON включен по умолчанию для поддерживаемых клиентов. Клиенты могут поддерживать только один протокол. Добавление поддержки MessagePack заменит все ранее настроенные протоколы.

Клиент .NET

Чтобы включить MessagePack в клиенте .NET, установите пакет Microsoft.AspNetCore.SignalR.Protocols.MessagePack и вызовите AddMessagePackProtocol для HubConnectionBuilder.

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

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

Примечание.

Этот AddMessagePackProtocol вызов принимает делегат для настройки параметров так же, как сервер.

Клиент на JavaScript

Поддержка MessagePack для клиента JavaScript предоставляется пакетом npm @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> элемента порядок важен. Если signalr-protocol-msgpack.js ссылка указана раньше msgpack5.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();

Примечание.

В настоящее время нет параметров конфигурации для протокола MessagePack на клиенте JavaScript.

Рекомендации по MessagePack

При использовании протокола MessagePack Hub существует несколько проблем.

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 не предусматривает способа кодирования значения Kind объекта DateTime. В результате, при десериализации даты протокол MessagePack Hub предполагает, что входящая дата представлена в формате UTC. Если вы работаете со значениями DateTime в локальном времени, рекомендуется преобразовать их в формате UTC перед отправкой. Преобразуйте их из UTC в местное время при получении.

Дополнительные сведения об этом ограничении см. в статье GitHub о проблеме aspnet/SignalR#2632.

DateTime.MinValue не поддерживается MessagePack в JavaScript

Библиотека msgpack5, используемая в клиенте SignalR JavaScript, не поддерживает тип timestamp96 в MessagePack. Этот тип используется для кодирования очень больших значений дат (либо в начале прошлого или очень далеко в будущем). Значение DateTime.MinValueJanuary 1, 0001, которое должно быть закодировано в формате значения timestamp96. Из-за этого отправка DateTime.MinValue клиенту JavaScript не поддерживается. При DateTime.MinValue получении клиентом JavaScript возникает следующая ошибка:

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

DateTime.MinValue Обычно используется для кодирования "отсутствующих" или null значений. Если необходимо закодировать это значение в MessagePack, используйте значение DateTime, допускающее значение NULL (DateTime?), или закодируйте отдельное значение bool, указывающее, указана ли дата.

Дополнительные сведения об этом ограничении см. в статье GitHub о проблеме aspnet/SignalR#2228.

Поддержка MessagePack в среде опережающей компиляции

Библиотека MessagePack-CSharp, используемая клиентом и сервером .NET, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, многоплатформенный пользовательский интерфейс приложения NET (.NET MAUI) или 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 Hub будет выполнять преобразования типов во время десериализации. Например, если входящий объект имеет значение свойства, которое является числом ({ 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 issue aspnet/SignalR#2937.

Дополнительные ресурсы