Поделиться через


Использование протокола 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-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 добавьте в элемент 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 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, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, 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 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.

Chars and Strings in 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-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 добавьте в элемент 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 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.MinValue имеет значение January 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, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, 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 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.

Chars and Strings in 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-CSharp. Атрибуты можно использовать для объектов, которые необходимо сериализовать, чтобы определить способ их обработки.

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

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

Настоятельно рекомендуется просматривать CVE-2020-5234 и применять рекомендуемые исправления. Например, присвойте статическому свойству MessagePackSecurity.Active MessagePackSecurity.UntrustedDataзначение . MessagePackSecurity.Active Установка необходимой вручную установки пакета MessagePack версии 1.9.x. При установке 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.MinValue имеет значение January 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, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, 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 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-CSharp. Атрибуты можно использовать для объектов, которые необходимо сериализовать, чтобы определить способ их обработки.

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

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

Настоятельно рекомендуется просматривать CVE-2020-5234 и применять рекомендуемые исправления. Например, присвойте статическому свойству MessagePackSecurity.Active MessagePackSecurity.UntrustedDataзначение . MessagePackSecurity.Active Установка необходимой вручную установки пакета MessagePack версии 1.9.x. При установке 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 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.MinValue , January 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, использует создание кода для оптимизации сериализации. В результате она не поддерживается по умолчанию в средах, использующих компиляцию "заранее" (например, 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 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.

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