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
- ASP.NET Core SignalR .NET Client
- Cliente ASP.NET Core JavaScriptSignalR
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
- ASP.NET Core SignalR .NET Client
- Cliente ASP.NET Core JavaScriptSignalR
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
- ASP.NET Core SignalR .NET Client
- Cliente ASP.NET Core JavaScriptSignalR
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
- ASP.NET Core SignalR .NET Client
- Cliente ASP.NET Core JavaScriptSignalR