Partilhar via


Usar o protocolo MessagePack Hub no SignalR para ASP.NET Core

Este artigo pressupõe que o leitor esteja familiarizado com os tópicos abordados em Introdução ao ASP.NET Core SignalR.

O que é MessagePack?

MessagePack é um formato de serialização binária rápido e compacto. É útil quando o desempenho e a largura de banda são uma preocupação porque cria mensagens menores do que JSON. As mensagens binárias são ilegíveis ao examinar rastreamentos de rede e logs, a menos que os bytes sejam passados por um analisador MessagePack. SignalR tem suporte interno para o formato MessagePack e fornece APIs para o cliente e o servidor usarem.

Configurar o MessagePack no servidor

Para habilitar o MessagePack Hub Protocol no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack em seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte ao MessagePack no servidor.

services.AddSignalR()
    .AddMessagePackProtocol();

Observação

JSON está habilitado por padrão. Adicionar o MessagePack permite o suporte para clientes JSON e MessagePack.

Para personalizar como o MessagePack formata dados, AddMessagePackProtocol aceita uma função delegada para definir opções. Nesse delegado, a propriedade SerializerOptions é usada para configurar as opções de serialização do MessagePack. Para obter mais informações sobre como os resolvedores funcionam, visite a biblioteca MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser manipulados.

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

Advertência

É altamente recomendável revisar CVE-2020-5234 e aplicar os patches recomendados. Por exemplo, chamar .WithSecurity(MessagePackSecurity.UntrustedData) ao substituir o SerializerOptions.

Configurar o MessagePack no cliente

Observação

JSON é habilitado por padrão para os clientes suportados. Os clientes só podem suportar um único protocolo. Adicionar suporte ao MessagePack substitui todos os protocolos configurados anteriormente.

Cliente .NET

Para habilitar o MessagePack no cliente .NET, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack e chame AddMessagePackProtocol no HubConnectionBuilder.

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

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

Observação

Esta chamada AddMessagePackProtocol recebe um delegado para configurar opções de forma semelhante ao servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote @microsoft/signalr-protocol-msgpack npm. Instale o pacote executando o seguinte comando em um shell de comando:

npm install @microsoft/signalr-protocol-msgpack

Depois de instalar o pacote npm, o módulo pode ser usado diretamente através de um carregador de módulos JavaScript ou importado para o navegador fazendo referência ao seguinte arquivo:

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

Os seguintes arquivos javaScript necessários devem ser referenciados na ordem mostrada abaixo:

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

Adicionar .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) ao HubConnectionBuilder configura o cliente para usar o protocolo MessagePack ao se conectar a um servidor.

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

No momento, não há opções de configuração para o protocolo MessagePack no cliente JavaScript.

Cliente Java

Para habilitar o MessagePack com Java, instale o pacote com.microsoft.signalr.messagepack. Ao usar o Gradle, adicione a seguinte linha à seção do arquivo build.gradle :

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

Ao usar o Maven, adicione as seguintes linhas dentro do elemento <dependencies> do arquivo pom.xml:

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

Ligue para withHubProtocol(new MessagePackHubProtocol()) em HubConnectionBuilder.

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

Considerações sobre o MessagePack

Há alguns problemas a serem observados ao usar o MessagePack Hub Protocol.

O MessagePack diferencia maiúsculas de minúsculas

O protocolo MessagePack diferencia maiúsculas de minúsculas. Por exemplo, considere a seguinte classe C#:

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

Ao enviar a partir do cliente JavaScript, deves usar os nomes de propriedade PascalCased, já que a capitalização deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não se vinculará corretamente à classe C#. Você pode contornar isso usando o atributo Key para especificar um nome diferente para a propriedade MessagePack. Para obter mais informações, consulte a documentação MessagePack-CSharp.

DateTime.Kind não é preservado ao serializar/desserializar

O protocolo MessagePack não fornece uma maneira de codificar o valor Kind de um DateTime. Como resultado, ao desserializar uma data, o protocolo MessagePack Hub converte-la-á para o formato UTC se o DateTime.Kind for DateTimeKind.Local; caso contrário, não alterará a hora e passá-la-á como está. Se você estiver trabalhando com valores DateTime, recomendamos converter para UTC antes de enviá-los. Converta-os de UTC para a hora local quando os receber.

Suporte ao MessagePack em ambiente de compilação antecipada

A biblioteca MessagePack-CSharp, utilizada pelo cliente e servidor .NET, emprega a geração de código para otimizar a serialização. Como resultado, ele não é suportado por padrão em ambientes que usam compilação "antecipada", como NET Multi-platform App UI (.NET MAUI) ou Unity. É possível usar o MessagePack nesses ambientes "pré-gerando" o código do serializador/desserializador. Para obter mais informações, consulte a documentação MessagePack-CSharp. Depois de pré-gerar os serializadores, você pode registrá-los usando o delegado de configuração passado para 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);
    });

As verificações de tipo são mais rigorosas no MessagePack

O protocolo JSON Hub executará conversões de tipo durante a desserialização. Por exemplo, se o objeto de entrada tiver um valor de propriedade que seja um número ({ foo: 42 }), mas a propriedade na classe .NET for do tipo string, o valor será convertido. No entanto, o MessagePack não executa essa conversão e lançará uma exceção que pode ser vista nos logs do lado do servidor (e no console):

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

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2937.

Caracteres e strings em Java

No cliente Java, os objetos char serão serializados como objetos String de um único caractere. Isso contrasta com o cliente C# e JavaScript, que os serializam como objetos short. A especificação do MessagePack em si não define o comportamento para char objetos, portanto, cabe ao autor da biblioteca determinar como serializá-los. A diferença de comportamento entre os nossos clientes é resultado das bibliotecas que usamos para as nossas implementações.

Recursos adicionais

Este artigo pressupõe que o leitor esteja familiarizado com os tópicos abordados em Introdução ao ASP.NET Core SignalR.

O que é MessagePack?

MessagePack é um formato de serialização binária rápido e compacto. É útil quando o desempenho e a largura de banda são uma preocupação porque cria mensagens menores em comparação com JSON. As mensagens binárias são ilegíveis ao examinar rastreamentos de rede e logs, a menos que os bytes sejam passados por um analisador MessagePack. SignalR tem suporte interno para o formato MessagePack e fornece APIs para o cliente e o servidor usarem.

Configurar o MessagePack no servidor

Para habilitar o MessagePack Hub Protocol no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack em seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte ao MessagePack no servidor.

Observação

JSON está habilitado por padrão. Adicionar o MessagePack permite o suporte para clientes JSON e MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Para personalizar como o MessagePack formatará os seus dados, o AddMessagePackProtocol aceita um delegado para configurar as opções. Nesse delegado, a propriedade SerializerOptions pode ser usada para configurar as opções de serialização do MessagePack. Para obter mais informações sobre como os resolvedores funcionam, visite a biblioteca MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser manipulados.

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

Advertência

É altamente recomendável revisar CVE-2020-5234 e aplicar os patches recomendados. Por exemplo, chamar .WithSecurity(MessagePackSecurity.UntrustedData) ao substituir o SerializerOptions.

Configurar o MessagePack no cliente

Observação

JSON é habilitado por padrão para os clientes suportados. Os clientes só podem suportar um único protocolo. Adicionar suporte ao MessagePack substituirá todos os protocolos configurados anteriormente.

Cliente .NET

Para habilitar o MessagePack no cliente .NET, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack e chame AddMessagePackProtocol no HubConnectionBuilder.

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

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

Observação

Esta chamada AddMessagePackProtocol recebe um delegado para configurar opções de forma semelhante ao servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote @microsoft/signalr-protocol-msgpack npm. Instale o pacote executando o seguinte comando em um shell de comando:

npm install @microsoft/signalr-protocol-msgpack

Depois de instalar o pacote npm, o módulo pode ser usado diretamente através de um carregador de módulos JavaScript ou importado para o navegador fazendo referência ao seguinte arquivo:

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

Em um navegador, a biblioteca msgpack5 também deve ser referenciada. Use uma tag <script> para criar uma referência. A biblioteca pode ser encontrada em node_modules\msgpack5\dist\msgpack5.js.

Observação

Ao usar o elemento <script>, a ordem é importante. Se signalr-protocol-msgpack.js for referenciado antes msgpack5.js, ocorrerá um erro ao tentar se conectar com o MessagePack. signalr.js também é necessário antes 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>

Adicionar .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) ao HubConnectionBuilder configurará o cliente para usar o protocolo MessagePack ao se conectar a um servidor.

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

Observação

No momento, não há opções de configuração para o protocolo MessagePack no cliente JavaScript.

Cliente Java

Para habilitar o MessagePack com Java, instale o pacote com.microsoft.signalr.messagepack. Ao usar o Gradle, adicione a seguinte linha à seção do arquivo build.gradle :

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

Ao usar o Maven, adicione as seguintes linhas dentro do elemento <dependencies> do arquivo pom.xml:

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

Ligue para withHubProtocol(new MessagePackHubProtocol()) em HubConnectionBuilder.

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

Considerações sobre o MessagePack

Há alguns problemas a serem observados ao usar o MessagePack Hub Protocol.

O MessagePack diferencia maiúsculas de minúsculas

O protocolo MessagePack diferencia maiúsculas de minúsculas. Por exemplo, considere a seguinte classe C#:

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

Ao enviar a partir do cliente JavaScript, deves usar os nomes de propriedade PascalCased, já que a capitalização deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não se vinculará corretamente à classe C#. Você pode contornar isso usando o atributo Key para especificar um nome diferente para a propriedade MessagePack. Para obter mais informações, consulte a documentação MessagePack-CSharp.

DateTime.Kind não é preservado ao serializar/desserializar

O protocolo MessagePack não fornece uma maneira de codificar o valor Kind de um DateTime. Como resultado, ao desserializar uma data, o protocolo MessagePack Hub converte-la-á para o formato UTC se o DateTime.Kind for DateTimeKind.Local; caso contrário, não alterará a hora e passá-la-á como está. Se você estiver trabalhando com valores DateTime, recomendamos converter para UTC antes de enviá-los. Converta-os de UTC para a hora local quando os receber.

DateTime.MinValue não é suportado pelo MessagePack em JavaScript

A biblioteca msgpack5 usada pelo cliente JavaScript SignalR não suporta o tipo timestamp96 no MessagePack. Este tipo é usado para codificar valores de data muito grandes (muito cedo no passado ou muito longe no futuro). O valor de DateTime.MinValue é January 1, 0001, que deve ser codificado em um valor timestamp96. Por isso, o envio de DateTime.MinValue para um cliente JavaScript não é suportado. Quando DateTime.MinValue é recebido pelo cliente JavaScript, o seguinte erro é gerado:

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

Normalmente, DateTime.MinValue é usado para codificar um valor "ausente" ou null. Se você precisar codificar esse valor em MessagePack, use um valor de DateTime anulável (DateTime?) ou codifique um valor de bool separado indicando se a data está presente.

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2228.

Suporte ao MessagePack em ambiente de compilação antecipada

A biblioteca MessagePack-CSharp, utilizada pelo cliente e servidor .NET, emprega a geração de código para otimizar a serialização. Como resultado, ele não é suportado por padrão em ambientes que usam compilação "antecipada", como NET Multi-platform App UI (.NET MAUI) ou Unity. É possível usar o MessagePack nesses ambientes "pré-gerando" o código do serializador/desserializador. Para obter mais informações, consulte a documentação MessagePack-CSharp. Depois de pré-gerar os serializadores, você pode registrá-los usando o delegado de configuração passado para 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);
    });

As verificações de tipo são mais rigorosas no MessagePack

O protocolo JSON Hub executará conversões de tipo durante a desserialização. Por exemplo, se o objeto de entrada tiver um valor de propriedade que seja um número ({ foo: 42 }), mas a propriedade na classe .NET for do tipo string, o valor será convertido. No entanto, o MessagePack não executa essa conversão e lançará uma exceção que pode ser vista nos logs do lado do servidor (e no console):

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

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2937.

Caracteres e strings em Java

No cliente Java, os objetos char serão serializados como objetos String de um único caractere. Isso contrasta com o cliente C# e JavaScript, que os serializam como objetos short. A especificação do MessagePack em si não define o comportamento para char objetos, portanto, cabe ao autor da biblioteca determinar como serializá-los. A diferença de comportamento entre os nossos clientes é resultado das bibliotecas que usamos para as nossas implementações.

Recursos adicionais

Este artigo pressupõe que o leitor esteja familiarizado com os tópicos abordados em Introdução ao ASP.NET Core SignalR.

O que é MessagePack?

MessagePack é um formato de serialização binária rápido e compacto. É útil quando o desempenho e a largura de banda são uma preocupação porque cria mensagens menores em comparação com JSON. As mensagens binárias são ilegíveis ao examinar rastreamentos de rede e logs, a menos que os bytes sejam passados por um analisador MessagePack. SignalR tem suporte interno para o formato MessagePack e fornece APIs para o cliente e o servidor usarem.

Configurar o MessagePack no servidor

Para habilitar o MessagePack Hub Protocol no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack em seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte ao MessagePack no servidor.

Observação

JSON está habilitado por padrão. Adicionar o MessagePack permite o suporte para clientes JSON e MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Para personalizar como o MessagePack formatará os seus dados, o AddMessagePackProtocol aceita um delegado para configurar as opções. Nesse delegado, a propriedade FormatterResolvers pode ser usada para configurar as opções de serialização do MessagePack. Para obter mais informações sobre como os resolvedores funcionam, visite a biblioteca MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser manipulados.

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

Advertência

É altamente recomendável revisar CVE-2020-5234 e aplicar os patches recomendados. Por exemplo, definir a propriedade MessagePackSecurity.Active static como MessagePackSecurity.UntrustedData. A configuração do MessagePackSecurity.Active requer a instalação manual de uma versão 1.9.x do MessagePack. A instalação do MessagePack 1.9.x atualiza a versão que SignalR usa. MessagePack versão 2.x introduziu alterações significativas e é incompatível com SignalR versões 3.1 e anteriores. Quando MessagePackSecurity.Active não está definido como MessagePackSecurity.UntrustedData, um cliente mal-intencionado pode causar uma negação de serviço. Defina MessagePackSecurity.Active em Program.Main, conforme mostrado no código a seguir:

using MessagePack;

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

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

Configurar o MessagePack no cliente

Observação

JSON é habilitado por padrão para os clientes suportados. Os clientes só podem suportar um único protocolo. Adicionar suporte ao MessagePack substituirá todos os protocolos configurados anteriormente.

Cliente .NET

Para habilitar o MessagePack no cliente .NET, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack e chame AddMessagePackProtocol no HubConnectionBuilder.

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

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

Observação

Esta chamada AddMessagePackProtocol recebe um delegado para configurar opções de forma semelhante ao servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote @microsoft/signalr-protocol-msgpack npm. Instale o pacote executando o seguinte comando em um shell de comando:

npm install @microsoft/signalr-protocol-msgpack

Depois de instalar o pacote npm, o módulo pode ser usado diretamente através de um carregador de módulos JavaScript ou importado para o navegador fazendo referência ao seguinte arquivo:

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

Em um navegador, a biblioteca msgpack5 também deve ser referenciada. Use uma tag <script> para criar uma referência. A biblioteca pode ser encontrada em node_modules\msgpack5\dist\msgpack5.js.

Observação

Ao usar o elemento <script>, a ordem é importante. Se signalr-protocol-msgpack.js for referenciado antes msgpack5.js, ocorrerá um erro ao tentar se conectar com o MessagePack. signalr.js também é necessário antes 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>

Adicionar .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) ao HubConnectionBuilder configurará o cliente para usar o protocolo MessagePack ao se conectar a um servidor.

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

Observação

No momento, não há opções de configuração para o protocolo MessagePack no cliente JavaScript.

Considerações sobre o MessagePack

Há alguns problemas a serem observados ao usar o MessagePack Hub Protocol.

O MessagePack diferencia maiúsculas de minúsculas

O protocolo MessagePack diferencia maiúsculas de minúsculas. Por exemplo, considere a seguinte classe C#:

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

Ao enviar a partir do cliente JavaScript, deves usar os nomes de propriedade PascalCased, já que a capitalização deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não se vinculará corretamente à classe C#. Você pode contornar isso usando o atributo Key para especificar um nome diferente para a propriedade MessagePack. Para obter mais informações, consulte a documentação MessagePack-CSharp.

DateTime.Kind não é preservado ao serializar/desserializar

O protocolo MessagePack não fornece uma maneira de codificar o valor Kind de um DateTime. Como resultado, ao desserializar uma data, o MessagePack Hub Protocol assume que a data de entrada está no formato UTC. Se estiver a trabalhar com valores de DateTime na hora local, recomendamos converter para UTC antes de os enviar. Converta-os de UTC para a hora local quando os receber.

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2632.

DateTime.MinValue não é suportado pelo MessagePack em JavaScript

A biblioteca msgpack5 usada pelo cliente JavaScript SignalR não suporta o tipo timestamp96 no MessagePack. Este tipo é usado para codificar valores de data muito grandes (muito cedo no passado ou muito longe no futuro). O valor de DateTime.MinValue é January 1, 0001, que deve ser codificado em um valor timestamp96. Por isso, o envio de DateTime.MinValue para um cliente JavaScript não é suportado. Quando DateTime.MinValue é recebido pelo cliente JavaScript, o seguinte erro é gerado:

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

Normalmente, DateTime.MinValue é usado para codificar um valor "ausente" ou null. Se você precisar codificar esse valor em MessagePack, use um valor de DateTime anulável (DateTime?) ou codifique um valor de bool separado indicando se a data está presente.

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2228.

Suporte ao MessagePack em ambiente de compilação antecipada

A biblioteca MessagePack-CSharp, utilizada pelo cliente e servidor .NET, emprega a geração de código para otimizar a serialização. Como resultado, ele não é suportado por padrão em ambientes que usam compilação "antecipada", como NET Multi-platform App UI (.NET MAUI) ou Unity. É possível usar o MessagePack nesses ambientes "pré-gerando" o código do serializador/desserializador. Para obter mais informações, consulte a documentação MessagePack-CSharp. Depois de pré-gerar os serializadores, você pode registrá-los usando o delegado de configuração passado para AddMessagePackProtocol:

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

As verificações de tipo são mais rigorosas no MessagePack

O protocolo JSON Hub executará conversões de tipo durante a desserialização. Por exemplo, se o objeto de entrada tiver um valor de propriedade que seja um número ({ foo: 42 }), mas a propriedade na classe .NET for do tipo string, o valor será convertido. No entanto, o MessagePack não executa essa conversão e lançará uma exceção que pode ser vista nos logs do lado do servidor (e no console):

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

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2937.

Recursos adicionais

Este artigo pressupõe que o leitor esteja familiarizado com os tópicos abordados em Introdução ao ASP.NET Core SignalR.

O que é MessagePack?

MessagePack é um formato de serialização binária rápido e compacto. É útil quando o desempenho e a largura de banda são uma preocupação porque cria mensagens menores em comparação com JSON. As mensagens binárias são ilegíveis ao examinar rastreamentos de rede e logs, a menos que os bytes sejam passados por um analisador MessagePack. SignalR tem suporte interno para o formato MessagePack e fornece APIs para o cliente e o servidor usarem.

Configurar o MessagePack no servidor

Para habilitar o MessagePack Hub Protocol no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack em seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte ao MessagePack no servidor.

Observação

JSON está habilitado por padrão. Adicionar o MessagePack permite o suporte para clientes JSON e MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Para personalizar como o MessagePack formatará os seus dados, o AddMessagePackProtocol aceita um delegado para configurar as opções. Nesse delegado, a propriedade FormatterResolvers pode ser usada para configurar as opções de serialização do MessagePack. Para obter mais informações sobre como os resolvedores funcionam, visite a biblioteca MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser manipulados.

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

Advertência

É altamente recomendável revisar CVE-2020-5234 e aplicar os patches recomendados. Por exemplo, definir a propriedade MessagePackSecurity.Active static como MessagePackSecurity.UntrustedData. A configuração do MessagePackSecurity.Active requer a instalação manual de uma versão 1.9.x do MessagePack. A instalação do MessagePack 1.9.x atualiza a versão que SignalR usa. Quando MessagePackSecurity.Active não está definido como MessagePackSecurity.UntrustedData, um cliente mal-intencionado pode causar uma negação de serviço. Defina MessagePackSecurity.Active em Program.Main, conforme mostrado no código a seguir:

using MessagePack;

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

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

Configurar o MessagePack no cliente

Observação

JSON é habilitado por padrão para os clientes suportados. Os clientes só podem suportar um único protocolo. Adicionar suporte ao MessagePack substituirá todos os protocolos configurados anteriormente.

Cliente .NET

Para habilitar o MessagePack no cliente .NET, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack e chame AddMessagePackProtocol no HubConnectionBuilder.

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

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

Observação

Esta chamada AddMessagePackProtocol recebe um delegado para configurar opções de forma semelhante ao servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote @aspnet/signalr-protocol-msgpack npm. Instale o pacote executando o seguinte comando em um shell de comando:

npm install @aspnet/signalr-protocol-msgpack

Depois de instalar o pacote npm, o módulo pode ser usado diretamente através de um carregador de módulos JavaScript ou importado para o navegador fazendo referência ao seguinte arquivo:

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

Em um navegador, a biblioteca msgpack5 também deve ser referenciada. Use uma tag <script> para criar uma referência. A biblioteca pode ser encontrada em node_modules\msgpack5\dist\msgpack5.js.

Observação

Ao usar o elemento <script>, a ordem é importante. Se signalr-protocol-msgpack.js for referenciado antes msgpack5.js, ocorrerá um erro ao tentar se conectar com o MessagePack. signalr.js também é necessário antes 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>

Adicionar .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) ao HubConnectionBuilder configurará o cliente para usar o protocolo MessagePack ao se conectar a um servidor.

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

Observação

No momento, não há opções de configuração para o protocolo MessagePack no cliente JavaScript.

Considerações sobre o MessagePack

Há alguns problemas a serem observados ao usar o MessagePack Hub Protocol.

O MessagePack diferencia maiúsculas de minúsculas

O protocolo MessagePack diferencia maiúsculas de minúsculas. Por exemplo, considere a seguinte classe C#:

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

Ao enviar a partir do cliente JavaScript, deves usar os nomes de propriedade PascalCased, já que a capitalização deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não se vinculará corretamente à classe C#. Você pode contornar isso usando o atributo Key para especificar um nome diferente para a propriedade MessagePack. Para obter mais informações, consulte a documentação MessagePack-CSharp.

DateTime.Kind não é preservado ao serializar/desserializar

O protocolo MessagePack não fornece uma maneira de codificar o valor Kind de um DateTime. Como resultado, ao desserializar uma data, o MessagePack Hub Protocol assume que a data de entrada está no formato UTC. Se estiver a trabalhar com valores de DateTime na hora local, recomendamos converter para UTC antes de os enviar. Converta-os de UTC para a hora local quando os receber.

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2632.

DateTime.MinValue não é suportado pelo MessagePack em JavaScript

A biblioteca msgpack5 usada pelo cliente JavaScript SignalR não suporta o tipo timestamp96 no MessagePack. Este tipo é usado para codificar valores de data muito grandes (muito cedo no passado ou muito longe no futuro). O valor de DateTime.MinValue é January 1, 0001, que deve ser codificado em um valor de timestamp96. Por isso, o envio de DateTime.MinValue para um cliente JavaScript não é suportado. Quando DateTime.MinValue é recebido pelo cliente JavaScript, o seguinte erro é gerado:

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

Normalmente, DateTime.MinValue é usado para codificar um valor "ausente" ou null. Se você precisar codificar esse valor em MessagePack, use um valor de DateTime anulável (DateTime?) ou codifique um valor de bool separado indicando se a data está presente.

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2228.

Suporte ao MessagePack em ambiente de compilação antecipada

A biblioteca MessagePack-CSharp, utilizada pelo cliente e servidor .NET, emprega a geração de código para otimizar a serialização. Como resultado, ele não é suportado por padrão em ambientes que usam compilação "antecipada", como NET Multi-platform App UI (.NET MAUI) ou Unity. É possível usar o MessagePack nesses ambientes "pré-gerando" o código do serializador/desserializador. Para obter mais informações, consulte a documentação MessagePack-CSharp. Depois de pré-gerar os serializadores, você pode registrá-los usando o delegado de configuração passado para AddMessagePackProtocol:

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

As verificações de tipo são mais rigorosas no MessagePack

O protocolo JSON Hub executará conversões de tipo durante a desserialização. Por exemplo, se o objeto de entrada tiver um valor de propriedade que seja um número ({ foo: 42 }), mas a propriedade na classe .NET for do tipo string, o valor será convertido. No entanto, o MessagePack não executa essa conversão e lançará uma exceção que pode ser vista nos logs do lado do servidor (e no console):

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

Para obter mais informações sobre essa limitação, consulte Problema do GitHub aspnet/SignalR#2937.

Recursos adicionais