Desarrollo y configuración de Azure Functions con Azure SignalR Service

Las aplicaciones de Azure Functions pueden usar los enlaces de Azure SignalR Service para agregar funcionalidades en tiempo real. Las aplicaciones cliente usan los SDK de cliente disponibles en varios lenguajes para conectarse a Azure SignalR Service y recibir mensajes en tiempo real.

En este artículo se describen los conceptos para desarrollar y configurar una aplicación de función de Azure que se integra con SignalR Service.

Configuración de SignalR Service

Azure SignalR Service puede configurarse de distintos modos. Cuando se usa con Azure Functions, el servicio debe configurarse en modo Sin servidor.

En Azure Portal, busque la página Configuración del recurso de SignalR Service. Establezca Modo de servicio en Sin servidor.

Modo SignalR Service

Desarrollo de Azure Functions

Una aplicación en tiempo real sin servidor creada con Azure Functions y Azure SignalR Service requiere al menos dos funciones de Azure:

  • Una función negotiate a la que el cliente llama para obtener un token de acceso válido de SignalR Service y la dirección URL del punto de conexión.
  • Una o varias funciones que controlan los mensajes enviados desde SignalR Service a los clientes.

Función de negociación

Una aplicación cliente requiere un token de acceso válido para conectarse a Azure SignalR Service. Puede ser un token de acceso anónimo o autenticado para un identificador de usuario. Las aplicaciones de SignalR Service sin servidor requieren un punto de conexión HTTP denominado negotiate para obtener un token y otra información de conexión, como la dirección URL del punto de conexión de SignalR Service.

Use una función de Azure desencadenada por HTTP y el enlace de entrada SignalRConnectionInfo para generar el objeto de información de conexión. La función debe tener una ruta HTTP que termina en /negotiate.

Con el modelo basado en clases en C#, no necesita el enlace de entrada SignalRConnectionInfo y puede agregar notificaciones personalizadas con mucha más facilidad. Para obtener más información, vea Experiencia de negociación en el modelo basado en clases.

Para obtener más información sobre la función negotiate, consulte Desarrollo de Azure Functions.

Para aprender a crear un token autenticado, consulte Uso de la autenticación de App Service.

Control de los mensajes enviados desde Signalr Service

Use el enlace del SignalRTrigger para controlar los mensajes enviados desde Signalr Service. Puede recibir notificaciones cuando los clientes envían mensajes, o bien se conectan o desconectan.

Para obtener más información, consulte la referencia del enlace del desencadenador de SignalR Service.

También debe configurar el punto de conexión de la función como un punto de conexión ascendente para que el servicio desencadene la función cuando haya un mensaje de un cliente. Para obtener más información sobre cómo configurar puntos de conexión ascendentes, vea Puntos de conexión ascendentes.

Nota:

SignalR Service no admite el mensaje StreamInvocation de un cliente en modo sin servidor.

Envío de mensajes y administración de la pertenencia a grupos

Use el enlace de salida SignalR para enviar mensajes a los clientes conectados a Azure SignalR Service. Puede difundir mensajes a todos los clientes o puede enviarlos a un subconjunto de clientes. Por ejemplo, envíe mensajes solo a los clientes autenticados con un identificador de usuario específico o solo a un grupo específico.

Se pueden agregar usuarios a uno o más grupos. También puede usar el enlace de salida de SignalR para agregar o quitar usuarios en los grupos.

Para más información, consulte la SignalR de referencia del enlace de salida.

Concentradores de SignalR

SignalR tiene un concepto de concentradores. Cada conexión de cliente y cada mensaje enviado desde Azure Functions se limitan a un concentrador concreto. Puede usar concentradores como una manera de separar las conexiones y los mensajes en espacios de nombres lógicos.

Modelo basado en clases

El modelo basado en clases está dedicado a C#.

El modelo basado en clases proporciona una mejor experiencia de programación, que puede reemplazar los enlaces de entrada y salida de SignalR, por las siguientes características:

  • Una experiencia más flexible de negociación, envío de mensajes y administración de grupos.
  • Se admiten más funcionalidades de administración, como el cierre de conexiones o la comprobación de la existencia de una conexión, un usuario o un grupo.
  • Centro fuertemente tipado
  • Configuración de la cadena de conexión y el nombre del centro unificado en un solo lugar.

En el código siguiente se muestra cómo escribir enlaces de SignalR en el modelo basado en clases:

En primer lugar, defina el centro derivado de una clase 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);
    }
}

En el archivo Program.cs, registre el centro sin servidor:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(b => b.Services
        .AddServerlessHub<Functions>())
    .Build();

Experiencia de negociación en el modelo basado en clases

En lugar de utilizar el enlace de entrada de SignalR [SignalRConnectionInfoInput], la negociación en el modelo basado en clases puede ser más flexible. La clase base ServerlessHub tiene un método NegotiateAsync, que permite a los usuarios personalizar opciones de negociación como userId, claims, etc.

Task<BinaryData> NegotiateAsync(NegotiationOptions? options = null)

Envío de mensajes y administración de la experiencia en el modelo basado en clases

Puede enviar mensajes, administrar grupos o administrar clientes accediendo a los miembros proporcionados por la clase base ServerlessHub.

  • ServerlessHub.Clients para enviar mensajes a los clientes.
  • ServerlessHub.Groups para administrar conexiones con grupos, como agregar conexiones a grupos, quitar conexiones de grupos.
  • ServerlessHub.UserGroups para administrar usuarios con grupos, como agregar usuarios a grupos, quitar usuarios de grupos.
  • ServerlessHub.ClientManager para comprobar la existencia de conexiones, cerrar conexiones, etc.

Centro fuertemente tipado

El centro fuertemente tipado permite usar métodos fuertemente tipados al enviar mensajes a los clientes. Para usar un centro fuertemente tipado en el modelo basado en clases, extraiga métodos de cliente en una interfaz T y convierta la clase de centro derivada de ServerlessHub<T>.

El código siguiente es un ejemplo de interfaz para los métodos de cliente.

public interface IChatClient
{
    Task newMessage(NewMessage message);
}

A continuación, puede usar los métodos fuertemente tipados de la siguiente manera:

[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));
    }
}

Nota:

Puede obtener un ejemplo de proyecto completo de GitHub.

Configuración de la cadena de conexión y el nombre del centro unificado en un solo lugar

  • El nombre de clase del centro sin servidor se usa automáticamente como HubName.
  • Es posible que haya observado el atributo SignalRConnection usado en las clases de centro sin servidor de la siguiente manera:
    [SignalRConnection("AzureSignalRConnectionString")]
    public class Functions : ServerlessHub<IChatClient>
    
    Permite personalizar dónde está la cadena de conexión para el centro sin servidor. Si no está presente, se usa el valor predeterminado AzureSignalRConnectionString.

Importante

Los desencadenadores SignalR y los centros sin servidor son independientes. Por lo tanto, el nombre de clase del centro sin servidor y el atributo SignalRConnection no cambian la configuración de los desencadenadores de SignalR, aunque use desencadenadores de SignalR dentro del centro sin servidor.

Desarrollo de cliente

Las aplicaciones cliente de SignalR pueden utilizar el SDK de cliente de SignalR en uno de varios lenguajes para conectarse fácilmente a Azure SignalR Service y recibir mensajes de este mismo servicio.

Configuración de una conexión de cliente

Para conectarse a SignalR Service, un cliente debe realizar una negociación de conexión correcta que consta de estos pasos:

  1. Realice una solicitud al punto de conexión HTTP negotiate mencionado anteriormente para obtener información de conexión válida
  2. Conéctese a SignalR Service mediante la dirección URL del punto de conexión de servicio y el token de acceso obtenido del punto de conexión negotiate

Los SDK de cliente de SignalR ya contienen la lógica necesaria para realizar el enlace de negociación. Pase la dirección URL del punto de conexión de negociación, menos el segmento negotiate, al elemento HubConnectionBuilder del SDK. Este es un ejemplo en JavaScript:

const connection = new signalR.HubConnectionBuilder()
  .withUrl("https://my-signalr-function-app.azurewebsites.net/api")
  .build();

Por convención, el SDK anexa automáticamente /negotiate a la dirección URL y lo usa para comenzar la negociación.

Nota:

Si va a usar el SDK de JavaScript/TypeScript en un explorador, deberá habilitar el uso compartido de recursos entre orígenes (CORS) en la aplicación de función.

Para más información sobre cómo usar el SDK de cliente de SignalR, consulte la documentación correspondiente a su lenguaje:

Envío de mensajes desde un cliente hasta el servicio

Si ha configurado el nivel ascendente para el recurso de SignalR, puede enviar mensajes desde un cliente a Azure Functions mediante cualquier cliente de SignalR. Este es un ejemplo en JavaScript:

connection.send("method1", "arg1", "arg2");

Configuración de Azure Functions

Las aplicaciones de Azure Functions que se integran con Azure SignalR Service se pueden implementar como cualquier aplicación de función de Azure normal, mediante técnicas como implementación continua, implementación de archivos ZIP y ejecutar desde el paquete.

Sin embargo, hay algunas consideraciones especiales para las aplicaciones que usan los enlaces de SignalR Service. Si el cliente se ejecuta en un explorador, CORS debe estar habilitado. Y si la aplicación requiere autenticación, puede integrar el punto de conexión de negociación con la autenticación de App Service.

Habilitación de CORS

El cliente de JavaScript/TypeScript realiza solicitudes HTTP a la función de negociación para iniciar la negociación de la conexión. Cuando la aplicación cliente se hospeda en un dominio diferente al de la aplicación de función de Azure, se debe habilitar el uso compartido de recursos entre orígenes (CORS) en la aplicación de función o el explorador bloqueará las solicitudes.

Localhost

Cuando se ejecuta la aplicación de función en el equipo local, puede agregar una sección Host a local.settings.json para habilitar CORS. En la sección Host, agregue dos propiedades:

  • CORS: escriba la URL base que es el origen de la aplicación cliente.
  • CORSCredentials: establézcala en true para permitir solicitudes "withCredentials".

Ejemplo:

{
  "IsEncrypted": false,
  "Values": {
    // values
  },
  "Host": {
    "CORS": "http://localhost:8080",
    "CORSCredentials": true
  }
}

Nube - CORS en Azure Functions

Para habilitar CORS en una aplicación de función de Azure, vaya a la pantalla de configuración de CORS en la pestaña Características de la plataforma de la aplicación de función en Azure Portal.

Nota:

La configuración de CORS todavía no está disponible en el plan de Consumo para Linux de Azure Functions. Use Azure API Management para habilitar CORS.

Debe estar habilitado CORS con Access-Control-Allow-Credentials para que el cliente de SignalR llame a la función de negociación. Active la casilla para habilitarlo.

En la sección Orígenes permitidos, agregue una entrada con la dirección URL base de origen de la aplicación web.

Configuración de CORS

Nube: Azure API Management

Azure API Management proporciona una puerta de enlace de API que agrega funcionalidades a los servicios back-end existentes. Puede usarlo para agregar CORS a la aplicación de función. Ofrece un nivel de consumo con precios de pago por acción y una concesión gratuita mensual.

Consulte la documentación de API Management para obtener información sobre cómo importar una aplicación de Azure Function. Una vez importado, puede agregar una directiva de entrada para habilitar CORS con compatibilidad 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 los clientes SignalR para usar la dirección URL de API Management.

Uso de la autenticación de App Service

Azure Functions cuenta con autenticación integrada y admite proveedores conocidos, como Facebook, Twitter, Google, Cuenta Microsoft y Microsoft Entra ID. Esta característica se puede integrar con el enlace SignalRConnectionInfo para crear conexiones a Azure SignalR Service autenticado con un identificador de usuario. La aplicación puede enviar mensajes mediante el enlace de salida SignalR que se destinan a ese identificador de usuario.

En Azure Portal, en la pestaña Características de la plataforma de la aplicación de función, abra la ventana de configuración Autenticación/autorización. Siga la documentación de Autenticación de App Service para configurar la autenticación mediante un proveedor de identidades de su elección.

Una vez configurada, las solicitudes HTTP autenticadas incluyen los encabezados x-ms-client-principal-name y x-ms-client-principal-id que contienen, respectivamente, el nombre de usuario y el identificador de usuario de la identidad autenticada.

Puede usar estos encabezados en la configuración de enlace SignalRConnectionInfo para crear las conexiones autenticadas. Este es un ejemplo de la función de negociación de C# que usa el encabezado 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;
}

A continuación, puede enviar mensajes a ese usuario si establece la propiedad UserId de un mensaje de 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 información sobre otros lenguajes, consulte los enlaces de Azure SignalR Service para hacer referencia a Azure Functions.

Pasos siguientes

En este artículo, aprenderá a desarrollar y configurar aplicaciones de SignalR Service sin servidor con Azure Functions. Intente crear una aplicación por su cuenta mediante uno de los inicios rápidos o tutoriales de la página de información general de SignalR Service.