Desenvolvimento de funções do Azure e a configuração com o serviço do Azure SignalR
Os aplicativos do Azure Functions podem usar as associações do Azure SignalR Service para adicionar recursos em tempo real. Os aplicativos cliente usam SDKs de cliente disponíveis em vários idiomas para se conectar ao Azure SignalR Service e receber mensagens em tempo real.
Este artigo descreve os conceitos para desenvolver e configurar um aplicativo Azure Function integrado ao SignalR Service.
Importante
As cadeias de conexão brutas aparecem neste artigo somente para fins de demonstração.
Uma cadeia de conexão inclui as informações de autorização necessárias para que o seu aplicativo acesse o serviço Azure Web PubSub. A chave de acesso dentro da cadeia de conexão é semelhante a uma senha raiz para o serviço. Em ambientes de produção, sempre proteja suas chaves de acesso. Use o Azure Key Vault para gerenciar e rotacionar suas chaves com segurança, proteger sua cadeia de conexão usando o Microsoft Entra ID e autorizar o acesso com o Microsoft Entra ID.
Evite distribuir chaves de acesso para outros usuários, fazer hard-coding com elas ou salvá-las em qualquer lugar em texto sem formatação que seja acessível a outras pessoas. Gire suas chaves se você acredita que elas podem ter sido comprometidas.
Configuração do serviço SignalR
O Serviço do Azure SignalR pode ser configurado de diferentes modos. Quando usado com o Azure Functions, o serviço deve ser configurado no modo Sem servidor.
No portal do Azure, localize a página Configurações do recurso SignalR Service. Defina o modo de serviço como Sem servidor.
Desenvolvimento do Azure Functions
Um aplicativo em tempo real sem servidor criado com o Azure Functions e o serviço do Azure SignalR requer pelo menos dois Azure Functions:
- Uma função
negotiate
que o cliente chama para obter um token de acesso válido do Serviço do SignalR e uma URL do ponto de extremidade. - Uma ou mais funções que lidam com mensagens enviadas do Serviço do SignalR para clientes.
Função de negociação
Um aplicativo cliente requer um token de acesso válido para se conectar ao Azure SignalR Service. Um token de acesso pode ser anônimo ou autenticado para uma ID de usuário. Os aplicativos do serviço SignalR sem servidor exigem um ponto de extremidade HTTP denominado negotiate
para obter um token e outras informações de conexão, como a URL do ponto de extremidade do serviço SignalR.
Use uma Função do Azure disparada por HTTP e a associação de entrada SignalRConnectionInfo
para gerar o objeto de informações de conexão. A função deve ter uma rota HTTP que termina em /negotiate
.
Com o modelo baseado em classe em C#, você não precisa da associação de entrada SignalRConnectionInfo
e pode adicionar declarações personalizadas com muito mais facilidade. Para obter mais informações, confira Experiência de negociação no modelo com classe base.
Para obter mais informações sobre a função negotiate
, consulte Desenvolvimento do Azure Functions.
Para saber mais sobre como criar um token autenticado, consulte Usando a Autenticação do Serviço de Aplicativo.
Lidar com mensagens enviadas do serviço SignalR
Use a ligação SignalRTrigger
para lidar com mensagens enviadas do serviço SignalR. Você pode ser notificado quando os clientes enviam mensagens ou se conectam ou desconectam.
Para obter mais informações, consulte a referência de associação do gatilho de Serviço do SignalR.
Você também precisa configurar seu endpoint de função como um endpoint upstream para que o serviço acione a função quando houver mensagem de um cliente. Para mais informações sobre como configurar endpoints upstream, confira Endpoints upstream.
Observação
O Serviço do SignalR não dá suporte à mensagem StreamInvocation
de um cliente no modo sem servidor.
Envio de mensagens e gerenciamento de membros do grupo
Use a associação de saída SignalR
para enviar mensagens para clientes conectados ao Serviço SignalR do Azure. Você pode transmitir mensagens a todos os clientes ou enviá-las ao subconjunto de clientes. Por exemplo, envie apenas mensagens para clientes autenticados com uma ID de usuário específica ou apenas para um grupo específico.
Os usuários podem ser adicionados a um ou mais grupos. Você também pode usar a ligação de saída SignalR
para adicionar ou remover usuários de / para grupos.
Para obter mais informações, consulte a SignalR
SignalR.
Hubs SignalR
SignalR tem um conceito de hubs. Cada conexão de cliente e cada mensagem enviada do Azure Functions tem como escopo um hub específico. Você pode usar hubs como uma forma de separar suas conexões e mensagens em namespaces lógicos.
Modelo baseado em classe
O modelo baseado em classe é dedicado a C#.
O modelo com classe base fornece uma melhor experiência de programação, que pode substituir as associações de entrada e saída do SignalR pelos seguintes recursos:
- Negociação mais flexível, envio de mensagens e gerenciamento da experiência de grupos.
- Há suporte para mais funcionalidades de gerenciamento, inclusive conexões de fechamento, verificando se existe uma conexão, um usuário ou um grupo.
- Hub fortemente tipado
- Nome do hub unificado e configuração da cadeia de conexão em um só lugar.
O código a seguir demonstra como gravar as associações do SignalR no modelo com classe base:
Em primeiro lugar, defina o hub derivado de uma classe ServerlessHub
:
[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub
{
private const string HubName = nameof(Functions); // Used by SignalR trigger only
public Functions(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
[Function("negotiate")]
public async Task<HttpResponseData> Negotiate([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
var negotiateResponse = await NegotiateAsync(new() { UserId = req.Headers.GetValues("userId").FirstOrDefault() });
var response = req.CreateResponse();
response.WriteBytes(negotiateResponse.ToArray());
return response;
}
[Function("Broadcast")]
public Task Broadcast(
[SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
{
return Clients.All.SendAsync("newMessage", new NewMessage(invocationContext, message));
}
[Function("JoinGroup")]
public Task JoinGroup([SignalRTrigger(HubName, "messages", "JoinGroup", "connectionId", "groupName")] SignalRInvocationContext invocationContext, string connectionId, string groupName)
{
return Groups.AddToGroupAsync(connectionId, groupName);
}
}
No arquivo Program.cs, registre o hub sem servidor:
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(b => b.Services
.AddServerlessHub<Functions>())
.Build();
Experiência de negociação no modelo com classe base
Em vez de usar a associação de entrada SignalR [SignalRConnectionInfoInput]
, a negociação no modelo baseado em classe pode ser mais flexível. A classe base ServerlessHub
tem um método NegotiateAsync
, que permite aos usuários personalizar opções de negociação, como userId
, claims
etc.
Task<BinaryData> NegotiateAsync(NegotiationOptions? options = null)
Envio de mensagens e gerenciamento da experiência no modelo com classe base
Você pode enviar mensagens, gerenciar grupos ou gerenciar clientes acessando os membros fornecidos pela classe base ServerlessHub
.
ServerlessHub.Clients
para enviar mensagens aos clientes.ServerlessHub.Groups
para gerenciar conexões com grupos, como adicionar conexões a grupos, remover conexões de grupos.ServerlessHub.UserGroups
para gerenciar usuários com grupos, como adicionar usuários a grupos, remover usuários de grupos.ServerlessHub.ClientManager
para verificar a existência de conexões, fechar conexões etc.
Hub fortemente tipado
O hub fortemente tipado permite que você use métodos fortemente tipados ao enviar mensagens aos clientes. Para usar um hub fortemente tipado no modelo com classe base, extraia os métodos de cliente em uma interface T
e faça com que a classe do hub seja derivada de ServerlessHub<T>
.
O código a seguir é um exemplo de interface para métodos de cliente.
public interface IChatClient
{
Task newMessage(NewMessage message);
}
Então você pode usar os métodos fortemente tipados da seguinte maneira.
As cadeias de conexão brutas aparecem nesse artigo apenas para fins de demonstração. Em ambientes de produção, sempre proteja suas chaves de acesso. Use o Azure Key Vault para gerenciar e rotacionar suas chaves com segurança, proteger sua cadeia de conexão usando o Microsoft Entra ID e autorizar o acesso com o Microsoft Entra ID.
[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub<IChatClient>
{
private const string HubName = nameof(Functions); // Used by SignalR trigger only
public Functions(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
[Function("Broadcast")]
public Task Broadcast(
[SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
{
return Clients.All.newMessage(new NewMessage(invocationContext, message));
}
}
Observação
Você pode obter um exemplo de projeto completo do GitHub.
Nome do hub unificado e configuração da cadeia de conexão em um só lugar
- O nome da classe do hub sem servidor é usado automaticamente como
HubName
. - Você deve ter notado o atributo
SignalRConnection
usado nas classes do hub sem servidor da seguinte maneira:
Ele permite que você personalize o local em que está a cadeia de conexão do hub sem servidor. Se estiver ausente, o valor padrão[SignalRConnection("AzureSignalRConnectionString")] public class Functions : ServerlessHub<IChatClient>
AzureSignalRConnectionString
será usado.
Importante
Os gatilhos do SignalR e os hubs sem servidor são independentes. Portanto, o nome da classe do hub sem servidor e atributo SignalRConnection
não alteram as configurações dos gatilhos do SignalR, mesmo que você use os gatilhos do SignalR dentro do hub sem servidor.
Desenvolvimento de cliente
Os aplicativos de cliente SignalR podem usar o SDK do cliente SignalR em um dos vários idiomas para conectar-se facilmente e receber mensagens do Serviço SignalR do Azure.
Configurando uma conexão de cliente
Para se conectar ao SignalR Service, um cliente deve concluir uma negociação de conexão bem-sucedida que consiste nestas etapas:
- Faça uma solicitação para
negotiate
o endpoint HTTP discutido acima para obter informações de conexão válidas - Conecte-se ao serviço SignalR usando a URL do terminal de serviço e o token de acesso obtido do ponto de extremidade
negotiate
.
Os SDKs do cliente SignalR já contêm a lógica necessária para realizar o handshake de negociação. Passe a URL do endpoint de negociação, sem o segmento negotiate
, para o HubConnectionBuilder
do SDK. Aqui está um exemplo em JavaScript:
const connection = new signalR.HubConnectionBuilder()
.withUrl("https://my-signalr-function-app.azurewebsites.net/api")
.build();
Por convenção, o SDK anexa /negotiate
automaticamente ao URL e o usa para iniciar a negociação.
Observação
Se você estiver usando o JavaScript /TypeScript SDK em um navegador, será necessário habilitar o compartilhamento de recursos de origem cruzada (CORS) em seu aplicativo de funções.
Para obter mais informações sobre como usar o SDK do cliente SignalR, consulte a documentação do seu idioma:
Enviando mensagens de um cliente para o serviço
Se você configurou o upstream para o recurso SignalR, pode enviar mensagens de um cliente para o Azure Functions usando qualquer cliente SignalR. Aqui está um exemplo em JavaScript:
connection.send("method1", "arg1", "arg2");
Configuração do Azure Functions
Os aplicativos do Azure Function que se integram ao Azure SignalR Service podem ser implantados como qualquer aplicativo típico do Azure Function, usando técnicas como implantação contínua, implantação zip e execução do pacote.
No entanto, há algumas considerações especiais para aplicativos que usam as ligações SignalR Service. Se o cliente for executado em um navegador, o CORS deve ser ativado. E se o aplicativo exigir autenticação, você pode integrar o ponto de extremidade de negociação com a Autenticação do Serviço de Aplicativo.
Habilitando CORS
O cliente JavaScript/TypeScript faz solicitações HTTP para a função de negociação para iniciar a negociação da conexão. Quando o aplicativo cliente está hospedado em um domínio diferente do aplicativo Azure Function, o compartilhamento de recursos de origem cruzada (CORS) deve ser habilitado no aplicativo de funções ou o navegador bloqueará as solicitações.
Localhost
Ao executar o aplicativo Function em seu computador local, você pode adicionar uma seção Host
a local.settings.json para habilitar o CORS. Na seção Host
, adicione duas propriedades:
CORS
- insira o URL base que é a origem do aplicativo clienteCORSCredentials
- defina-o paratrue
para permitir solicitações "withCredentials
Exemplo:
{
"IsEncrypted": false,
"Values": {
// values
},
"Host": {
"CORS": "http://localhost:8080",
"CORSCredentials": true
}
}
Cloud - Azure Functions CORS
Para habilitar o CORS em um aplicativo Function do Azure, vá para a tela de configuração do CORS na guia Recursos da plataforma de seu aplicativo de Função no portal do Azure.
Observação
A configuração do CORS ainda não está disponível no plano de consumo do Azure Functions em Linux. Use o Gerenciamento de API do Azure para habilitar o CORS.
O CORS com Access-Control-Allow-Credentials deve ser habilitado para o cliente SignalR chamar a função de negociação. Para habilitá-lo, marque a caixa de seleção.
Na seção origens permitidas, adicione uma entrada com a URL base de origem do seu aplicativo Web.
Cloud - Gerenciamento de API do Azure
O Gerenciamento de API do Azure fornece um gateway de API que adiciona recursos aos serviços de back-end existentes. Você pode usá-lo para adicionar CORS ao seu aplicativo de funções. Ele oferece um nível de consumo com preços de pagamento por ação e uma concessão gratuita mensal.
Consulte a documentação de Gerenciamento de API para obter informações sobre como importar um aplicativo Azure Function. Depois de importado, você pode adicionar uma política de entrada para habilitar CORS com suporte Access-Control-Allow-Credentials.
<cors allow-credentials="true">
<allowed-origins>
<origin>https://azure-samples.github.io</origin>
</allowed-origins>
<allowed-methods>
<method>GET</method>
<method>POST</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
Configure seus clientes SignalR para usar a URL de Gerenciamento de API.
Usando a autenticação do Serviço de Aplicativo
O Azure Functions tem autenticação interna, com suporte para provedores populares, como Facebook, X, Conta da Microsoft, Google e Microsoft Entra ID. Esse recurso pode ser integrado à associação SignalRConnectionInfo
para criar conexões com o Azure SignalR Service que são autenticadas para uma ID de usuário. Seu aplicativo pode enviar mensagens usando a ligação de saída SignalR
direcionada a esse ID de usuário.
No portal do Azure, na guia de Recursos da plataforma do seu aplicativo de função, abra a janela Configurações de autenticação/autorização. Siga a documentação para Autenticação de Serviço de Aplicativo para configurar a autenticação usando um provedor de identidade de sua escolha.
Depois de configuradas, as solicitações HTTP autenticadas incluem os cabeçalhos x-ms-client-principal-name
e x-ms-client-principal-id
que contêm o nome de usuário e a ID do usuário da identidade autenticada, respectivamente.
Você pode usar esses cabeçalhos em sua configuração de ligação SignalRConnectionInfo
para criar conexões autenticadas. Este é um exemplo de função de negociação do C# que usa o cabeçalho x-ms-client-principal-id
.
[FunctionName("negotiate")]
public static SignalRConnectionInfo Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
[SignalRConnectionInfo
(HubName = "chat", UserId = "{headers.x-ms-client-principal-id}")]
SignalRConnectionInfo connectionInfo)
{
// connectionInfo contains an access key token with a name identifier claim set to the authenticated user
return connectionInfo;
}
Em seguida, você pode enviar mensagens para esse usuário, definindo a propriedade UserId
de uma mensagem SignalR.
[FunctionName("SendMessage")]
public static Task SendMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]object message,
[SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
// the message will only be sent to these user IDs
UserId = "userId1",
Target = "newMessage",
Arguments = new [] { message }
});
}
Para obter informações sobre outras linguagens, consulte as associações do Azure SignalR Service para Azure Functions.
Próximas etapas
Neste artigo, você aprendeu como desenvolver e configurar aplicativos de serviço SignalR sem servidor usando o Azure Functions. Tente criar um aplicativo você mesmo usando um dos inícios rápidos ou tutoriais na página Visão geral do serviço SignalR.