Usar o Protocolo de Hub do MessagePack no SignalR para ASP.NET Core

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

O que é o 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 ele cria mensagens menores do que o JSON. As mensagens binárias são ilegíveis ao se observar rastreamentos e logs de rede, a menos que os bytes sejam passados por meio de um analisador de MessagePack. O 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 Protocolo de Hub do MessagePack no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack no seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte do MessagePack no servidor.

services.AddSignalR()
    .AddMessagePackProtocol();

Observação

JSON está habilitado por padrão. A adição do MessagePack habilita o suporte para clientes JSON e MessagePack.

Para personalizar como MessagePack formata dados, AddMessagePackProtocol usa um delegado para configurar 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 do MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser tratados.

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

Aviso

É altamente recomendável revisar o 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

O JSON é habilitado por padrão nos clientes com suporte. Os clientes só podem dar suporte a 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 em HubConnectionBuilder.

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

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

Observação

Essa chamada AddMessagePackProtocol usa um delegado para configurar opções, assim como o servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote npm @microsoft/signalr-protocol-msgpack. 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 por meio de um carregador de módulo 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 são necessários e 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 o Java, instale o pacote com.microsoft.signalr.messagepack. Ao usar o Gradle, adicione a seguinte linha à seção dependencies 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>

Chame withHubProtocol(new MessagePackHubProtocol()) em HubConnectionBuilder.

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

Considerações do MessagePack

Há alguns problemas a serem considerados ao usar o Protocolo de Hub do MessagePack.

O MessagePack diferencia as letras maiúsculas de minúsculas

O protocolo MessagePack diferencia as letras 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, você deve usar nomes de propriedade PascalCased, pois o uso de letras maiúsculas e minúsculas deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não será associado 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 do 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 de Hub do MessagePack será convertido no formato UTC se o DateTime.Kind for DateTimeKind.Local , caso contrário, ele não mexerá na hora e a passará como está. Se você estiver trabalhando com valores DateTime, recomendamos converter em UTC antes de enviá-los. Converta-os de UTC para a hora local quando você os receber.

Suporte ao MessagePack no ambiente de compilação "ahead-of-time"

A biblioteca MessagePack-CSharp usada pelo cliente .NET e pelo servidor usa a geração de código para otimizar a serialização. Como resultado, não há suporte por padrão em ambientes que usam compilação "ahead-of-time" (como Xamarin iOS ou Unity). É possível usar o MessagePack nesses ambientes "gerando previamente" o código serializador/desserializador. Para obter mais informações, confira a documentação do MessagePack-CSharp. Depois de gerar previamente os serializadores, você poderá 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 de Hub do JSON 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 gerará uma exceção que pode ser vista em 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, confira o problema do GitHub aspnet/SignalR#2937.

Caracteres e cadeias de caracteres no Java

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

Recursos adicionais

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

O que é o 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 ele cria mensagens menores em comparação com JSON. As mensagens binárias são ilegíveis ao se observar rastreamentos e logs de rede, a menos que os bytes sejam passados por meio de um analisador de MessagePack. O 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 Protocolo de Hub do MessagePack no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack no seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte do MessagePack no servidor.

Observação

JSON está habilitado por padrão. A adição do MessagePack habilita o suporte para clientes JSON e MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Para personalizar como MessagePack formatará seus dados, AddMessagePackProtocol usa um delegado para configurar 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 do MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser tratados.

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

Aviso

É altamente recomendável revisar o 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

O JSON é habilitado por padrão nos clientes com suporte. Os clientes só podem dar suporte a 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 em HubConnectionBuilder.

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

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

Observação

Essa chamada AddMessagePackProtocol usa um delegado para configurar opções, assim como o servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote npm @microsoft/signalr-protocol-msgpack. 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 por meio de um carregador de módulo 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 marca <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 de ,msgpack5.js ocorrerá um erro ao tentar se conectar com o MessagePack. Também é necessário que signalr.js venha antes de 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 o Java, instale o pacote com.microsoft.signalr.messagepack. Ao usar o Gradle, adicione a seguinte linha à seção dependencies 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>

Chame withHubProtocol(new MessagePackHubProtocol()) em HubConnectionBuilder.

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

Considerações do MessagePack

Há alguns problemas a serem considerados ao usar o Protocolo de Hub do MessagePack.

O MessagePack diferencia as letras maiúsculas de minúsculas

O protocolo MessagePack diferencia as letras 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, você deve usar nomes de propriedade PascalCased, pois o uso de letras maiúsculas e minúsculas deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não será associado 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 do 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 de Hub do MessagePack será convertido no formato UTC se o DateTime.Kind for DateTimeKind.Local , caso contrário, ele não mexerá na hora e a passará como está. Se você estiver trabalhando com valores DateTime, recomendamos converter em UTC antes de enviá-los. Converta-os de UTC para a hora local quando você os receber.

Não há suporte para DateTime.MinValue no MessagePack em JavaScript

A biblioteca msgpack5 usada pelo cliente JavaScript SignalR não dá suporte ao tipo timestamp96 no MessagePack. Esse 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, não há suporte para enviar DateTime.MinValue para um cliente JavaScript. 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 null ou "ausente". Se você precisar codificar esse valor no MessagePack, use um valor anulável DateTime (DateTime?) ou codifique um valor bool separado indicando se a data está presente.

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

Suporte ao MessagePack no ambiente de compilação "ahead-of-time"

A biblioteca MessagePack-CSharp usada pelo cliente .NET e pelo servidor usa a geração de código para otimizar a serialização. Como resultado, não há suporte por padrão em ambientes que usam compilação "ahead-of-time" (como Xamarin iOS ou Unity). É possível usar o MessagePack nesses ambientes "gerando previamente" o código serializador/desserializador. Para obter mais informações, confira a documentação do MessagePack-CSharp. Depois de gerar previamente os serializadores, você poderá 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 de Hub do JSON 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 gerará uma exceção que pode ser vista em 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, confira o problema do GitHub aspnet/SignalR#2937.

Caracteres e cadeias de caracteres no Java

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

Recursos adicionais

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

O que é o 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 ele cria mensagens menores em comparação com JSON. As mensagens binárias são ilegíveis ao se observar rastreamentos e logs de rede, a menos que os bytes sejam passados por meio de um analisador de MessagePack. O 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 Protocolo de Hub do MessagePack no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack no seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte do MessagePack no servidor.

Observação

JSON está habilitado por padrão. A adição do MessagePack habilita o suporte para clientes JSON e MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Para personalizar como MessagePack formatará seus dados, AddMessagePackProtocol usa um delegado para configurar 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 do MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser tratados.

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

Aviso

É altamente recomendável revisar o CVE-2020-5234 e aplicar os patches recomendados. Por exemplo, definir a propriedade estática MessagePackSecurity.Active como MessagePackSecurity.UntrustedData. Definir o MessagePackSecurity.Active requer a instalação manual da versão 1.9.x do MessagePack. A instalação do MessagePack 1.9.x atualiza a versão do SignalR usada. A versão 2.x do MessagePack introduziu alterações interruptivas e é incompatível com as versões 3.1 e anteriores do SignalR. Quando MessagePackSecurity.Active não estiver definido como MessagePackSecurity.UntrustedData, um cliente mal-intencionado poderá causar uma negação de serviço. Defina MessagePackSecurity.Active no 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

O JSON é habilitado por padrão nos clientes com suporte. Os clientes só podem dar suporte a 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 em HubConnectionBuilder.

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

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

Observação

Essa chamada AddMessagePackProtocol usa um delegado para configurar opções, assim como o servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote npm @microsoft/signalr-protocol-msgpack. 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 por meio de um carregador de módulo 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 marca <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 de ,msgpack5.js ocorrerá um erro ao tentar se conectar com o MessagePack. Também é necessário que signalr.js venha antes de 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 do MessagePack

Há alguns problemas a serem considerados ao usar o Protocolo de Hub do MessagePack.

O MessagePack diferencia as letras maiúsculas de minúsculas

O protocolo MessagePack diferencia as letras 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, você deve usar nomes de propriedade PascalCased, pois o uso de letras maiúsculas e minúsculas deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não será associado 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 do 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 de Hub do MessagePack pressupõe que a data de entrada esteja no formato UTC. Se você estiver trabalhando com valores DateTime no horário local, recomendamos converter em UTC antes de enviá-los. Converta-os de UTC para a hora local quando você os receber.

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

Não há suporte para DateTime.MinValue no MessagePack em JavaScript

A biblioteca msgpack5 usada pelo cliente JavaScript SignalR não dá suporte ao tipo timestamp96 no MessagePack. Esse 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, não há suporte para enviar DateTime.MinValue para um cliente JavaScript. 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 null ou "ausente". Se você precisar codificar esse valor no MessagePack, use um valor anulável DateTime (DateTime?) ou codifique um valor bool separado indicando se a data está presente.

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

Suporte ao MessagePack no ambiente de compilação "ahead-of-time"

A biblioteca MessagePack-CSharp usada pelo cliente .NET e pelo servidor usa a geração de código para otimizar a serialização. Como resultado, não há suporte por padrão em ambientes que usam compilação "ahead-of-time" (como Xamarin iOS ou Unity). É possível usar o MessagePack nesses ambientes "gerando previamente" o código serializador/desserializador. Para obter mais informações, confira a documentação do MessagePack-CSharp. Depois de gerar previamente os serializadores, você poderá 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 de Hub do JSON 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 gerará uma exceção que pode ser vista em 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, confira o problema do GitHub aspnet/SignalR#2937.

Recursos adicionais

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

O que é o 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 ele cria mensagens menores em comparação com JSON. As mensagens binárias são ilegíveis ao se observar rastreamentos e logs de rede, a menos que os bytes sejam passados por meio de um analisador de MessagePack. O 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 Protocolo de Hub do MessagePack no servidor, instale o pacote Microsoft.AspNetCore.SignalR.Protocols.MessagePack no seu aplicativo. No método Startup.ConfigureServices, adicione AddMessagePackProtocol à chamada AddSignalR para habilitar o suporte do MessagePack no servidor.

Observação

JSON está habilitado por padrão. A adição do MessagePack habilita o suporte para clientes JSON e MessagePack.

services.AddSignalR()
    .AddMessagePackProtocol();

Para personalizar como MessagePack formatará seus dados, AddMessagePackProtocol usa um delegado para configurar 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 do MessagePack em MessagePack-CSharp. Os atributos podem ser usados nos objetos que você deseja serializar para definir como eles devem ser tratados.

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

Aviso

É altamente recomendável revisar o CVE-2020-5234 e aplicar os patches recomendados. Por exemplo, definir a propriedade estática MessagePackSecurity.Active como MessagePackSecurity.UntrustedData. Definir o MessagePackSecurity.Active requer a instalação manual da versão 1.9.x do MessagePack. A instalação do MessagePack 1.9.x atualiza a versão do SignalR usada. Quando MessagePackSecurity.Active não estiver definido como MessagePackSecurity.UntrustedData, um cliente mal-intencionado poderá causar uma negação de serviço. Defina MessagePackSecurity.Active no 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

O JSON é habilitado por padrão nos clientes com suporte. Os clientes só podem dar suporte a 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 em HubConnectionBuilder.

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

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

Observação

Essa chamada AddMessagePackProtocol usa um delegado para configurar opções, assim como o servidor.

Cliente JavaScript

O suporte do MessagePack para o cliente JavaScript é fornecido pelo pacote npm @aspnet/signalr-protocol-msgpack. 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 por meio de um carregador de módulo 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 marca <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 de ,msgpack5.js ocorrerá um erro ao tentar se conectar com o MessagePack. Também é necessário que signalr.js venha antes de 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 do MessagePack

Há alguns problemas a serem considerados ao usar o Protocolo de Hub do MessagePack.

O MessagePack diferencia as letras maiúsculas de minúsculas

O protocolo MessagePack diferencia as letras 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, você deve usar nomes de propriedade PascalCased, pois o uso de letras maiúsculas e minúsculas deve corresponder exatamente à classe C#. Por exemplo:

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

O uso de nomes camelCased não será associado 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 do 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 de Hub do MessagePack pressupõe que a data de entrada esteja no formato UTC. Se você estiver trabalhando com valores DateTime no horário local, recomendamos converter em UTC antes de enviá-los. Converta-os de UTC para a hora local quando você os receber.

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

Não há suporte para DateTime.MinValue no MessagePack em JavaScript

A biblioteca msgpack5 usada pelo cliente JavaScript SignalR não dá suporte ao tipo timestamp96 no MessagePack. Esse 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, não há suporte para enviar DateTime.MinValue para um cliente JavaScript. 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 null ou "ausente". Se você precisar codificar esse valor no MessagePack, use um valor anulável DateTime (DateTime?) ou codifique um valor bool separado indicando se a data está presente.

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

Suporte ao MessagePack no ambiente de compilação "ahead-of-time"

A biblioteca MessagePack-CSharp usada pelo cliente .NET e pelo servidor usa a geração de código para otimizar a serialização. Como resultado, não há suporte por padrão em ambientes que usam compilação "ahead-of-time" (como Xamarin iOS ou Unity). É possível usar o MessagePack nesses ambientes "gerando previamente" o código serializador/desserializador. Para obter mais informações, confira a documentação do MessagePack-CSharp. Depois de gerar previamente os serializadores, você poderá 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 de Hub do JSON 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 gerará uma exceção que pode ser vista em 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, confira o problema do GitHub aspnet/SignalR#2937.

Recursos adicionais