Usare hub in ASP.NET Core SignalR

Di Rachel Appel e Kevin Griffin

L'API SignalR Hubs consente ai client connessi di chiamare metodi sul server, semplificando la comunicazione in tempo reale. Il server definisce i metodi chiamati dal client e il client definisce i metodi chiamati dal server. SignalR consente inoltre la comunicazione indiretta da client a client, in cui l'Hub fornisce la mediazione SignalR. Questo approccio consente di inviare messaggi tra singoli client, gruppi o a tutti i client connessi. SignalR si occupa di tutto ciò che è necessario per rendere possibile la comunicazione da client a server e da server a client in tempo reale.

Questo articolo descrive come configurare hub, inviare messaggi ai client e consentire ai server di gestire i risultati dai client.

Configurare SignalR hub

Registrare i servizi richiesti dagli SignalR hub chiamando il AddSignalR metodo nel file Program.cs :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

Configurate gli endpoint SignalR chiamando il metodo MapHub nel file Program.cs:

app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");

app.Run();

Note

Assembly lato server di ASP.NET Core SignalR sono ora installati con .NET Core SDK. Per altre informazioni, vedere SignalR Assembly nel framework condiviso.

Creare e usare hub

Creare un hub dichiarando una classe che eredita da Hub. Aggiungere public metodi alla classe per renderli chiamabili dai client:

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.SendAsync("ReceiveMessage", user, message);
}

Note

Gli hub sono temporanei:

  • Non archiviare lo stato in una proprietà della classe dell'hub. Ogni chiamata al metodo hub viene eseguita in una nuova istanza dell'hub.
  • Non creare un'istanza di un hub direttamente tramite l'inserimento delle dipendenze. Per inviare messaggi a un client da un'altra posizione nell'applicazione, usare un IHubContext.
  • Usare await quando si chiamano metodi asincroni che dipendono dal mantenere l'hub operativo. Ad esempio, se si chiama un metodo come Clients.All.SendAsync(...) senza usare await, la chiamata può non riuscire e il metodo dell'hub viene completato prima che SendAsync termini.

Note

I parametri del metodo hub, i valori restituiti e gli elementi del flusso possono essere tipi di unione C# solo con il valore predefinito JsonHubProtocol. I protocolli hub MessagePack e Newtonsoft.Json non supportano le unioni.

Utilizzare le proprietà e i metodi dell'oggetto 'Context'

La Hub classe include una Context proprietà che contiene le proprietà seguenti con informazioni sulla connessione:

Property Description
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Esiste un ID di connessione per ogni connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa la ClaimTypes.NameIdentifier proprietà dell'oggetto ClaimsPrincipal associato alla connessione come identificatore utente.
User Ottiene l'elemento ClaimsPrincipal associato all'utente corrente.
Items Ottiene una raccolta chiave/valore che può essere utilizzata per condividere i dati nell'ambito di questa connessione. I dati possono essere archiviati in questa raccolta e vengono mantenuti per la connessione tra diverse chiamate al metodo hub.
Features Ottiene la raccolta di funzionalità disponibili nella connessione. Questa raccolta non è attualmente necessaria nella maggior parte degli scenari, quindi la documentazione dettagliata non è ancora disponibile.
ConnectionAborted Ottiene un oggetto CancellationToken che notifica quando la connessione viene interrotta.

La Hub.Context proprietà contiene anche i metodi seguenti:

Method Description
GetHttpContext Restituisce HttpContext per la connessione, oppure null se la connessione non è associata a una richiesta HTTP. Per le connessioni HTTP, usare questo metodo per ottenere informazioni quali intestazioni HTTP e stringhe di query.
Abort Interrompe la connessione.

Utilizzare le proprietà e i metodi dell'oggetto 'Clients'

La Hub classe include una Clients proprietà che contiene le proprietà seguenti per la comunicazione tra server e client:

Property Description
All Chiama un metodo su tutti i client connessi.
Caller Chiama un metodo sul client che ha richiamato il metodo hub.
Others Chiama un metodo su tutti i client connessi, ad eccezione del client che ha richiamato il metodo .

La Hub.Clients proprietà contiene anche i metodi seguenti:

Method Description
AllExcept Chiama un metodo su tutti i client connessi, ad eccezione delle connessioni specificate.
Client Chiama un metodo su un client connesso specifico.
Clients Chiama un metodo su client connessi specifici.
Group Chiama un metodo su tutte le connessioni nel gruppo specificato.
GroupExcept Chiama un metodo su tutte le connessioni nel gruppo specificato, ad eccezione delle connessioni specificate.
Groups Chiama un metodo per più gruppi di connessioni.
OthersInGroup Chiama un metodo su un gruppo di connessioni, escluso il client che ha richiamato il metodo hub.
User Chiama un metodo su tutte le connessioni associate a un utente specifico.
Users Chiama un metodo su tutte le connessioni associate agli utenti specificati.

Ogni proprietà o metodo restituisce un oggetto con un SendAsync metodo . Il SendAsync metodo riceve il nome del metodo client da chiamare ed eventuali parametri.

L'oggetto restituito dai Client metodi e Caller contiene anche un InvokeAsync metodo, che può essere utilizzato per attendere un risultato dal client.

Inviare messaggi ai client

Per effettuare chiamate a client specifici, utilizzare le proprietà dell'oggetto Clients . Nell'esempio seguente sono disponibili tre metodi hub:

  • Il SendMessage metodo invia un messaggio a tutti i client connessi utilizzando la Clients.All proprietà .
  • Il SendMessageToCaller metodo invia un messaggio al chiamante usando la Clients.Caller proprietà .
  • Il SendMessageToGroup metodo invia un messaggio a tutti i client del SignalR Users gruppo.
public async Task SendMessage(string user, string message)
    => await Clients.All.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToCaller(string user, string message)
    => await Clients.Caller.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToGroup(string user, string message)
    => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);

Usare hub fortemente tipizzati

Uno svantaggio dell'uso del SendAsync metodo consiste nel fatto che si basa su una stringa per specificare il metodo client da chiamare. Questa progettazione lascia aperto il codice agli errori di runtime se il nome del metodo è digitato in modo non corretto o mancante dal client.

Un'alternativa all'uso del metodo SendAsync è tipizzare in modo forte la classe Hub con Hub<T>. Nell'esempio seguente il ChatHub metodo client viene estratto in un'interfaccia denominata IChatClient:

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

L'interfaccia può essere utilizzata per eseguire il refactoring dell'esempio precedente ChatHub per renderlo fortemente tipizzato:

public class StronglyTypedChatHub : Hub<IChatClient>
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.ReceiveMessage(user, message);

    public async Task SendMessageToCaller(string user, string message)
        => await Clients.Caller.ReceiveMessage(user, message);

    public async Task SendMessageToGroup(string user, string message)
        => await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}

L'uso di Hub<IChatClient> consente il controllo in fase di compilazione dei metodi client. Questo approccio impedisce problemi causati dall'uso di stringhe perché Hub<T> può fornire l'accesso solo ai metodi definiti nell'interfaccia. L'uso di un elemento con tipizzazione forte Hub<T> impedisce di usare il metodo SendAsync.

Note

Il Async suffisso non viene rimosso dai nomi dei metodi. A meno che un metodo client non sia definito con .on('MyMethodAsync'), non usare MyMethodAsync come nome.

Richiedere i risultati del client

Oltre a effettuare chiamate ai client, il server può richiedere un risultato da un client. In questo scenario, il server usa il ISingleClientProxy.InvokeAsync metodo e il client restituisce un risultato dal relativo .On gestore.

Esistono due modi per usare l'API nel server.

È possibile chiamare Client(...) o Caller sulla proprietà Clients in un metodo Hub:

public class ChatHub : Hub
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        var message = await Clients.Client(connectionId).InvokeAsync<string>(
            "GetMessage");
        return message;
    }
}

In alternativa, è possibile chiamare Client(...) su un'istanza di IHubContext<T>:

async Task SomeMethod(IHubContext<MyHub> context)
{
    string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
        "GetMessage");
}

Gli hub fortemente tipizzati possono anche restituire valori dai metodi di interfaccia:

public interface IClient
{
    Task<string> GetMessage();
}

public class ChatHub : Hub<IClient>
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        string message = await Clients.Client(connectionId).GetMessage();
        return message;
    }
}

I client restituiscono i risultati nei rispettivi handler .On(...), come illustrato nelle sezioni seguenti.

Client .NET

hubConnection.On("GetMessage", async () =>
{
    Console.WriteLine("Enter message:");
    var message = await Console.In.ReadLineAsync();
    return message;
});

Client TypeScript

hubConnection.on("GetMessage", async () => {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("message");
        }, 100);
    });
    return promise;
});

Client Java

hubConnection.onWithResult("GetMessage", () -> {
    return Single.just("message");
});

Modificare il nome di un metodo hub

Per impostazione predefinita, il nome di un metodo hub server è il nome del metodo .NET. Per modificare questo comportamento predefinito per un metodo specifico, usare l'attributo HubMethodName . Il client deve usare questo nome anziché il nome del metodo .NET quando si richiama il metodo :

[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
    => await Clients.User(user).SendAsync("ReceiveMessage", user, message);

Inserire i servizi in un hub

I costruttori dell'hub possono accettare come parametri i servizi forniti dall'iniezione delle dipendenze, che possono essere memorizzati nelle proprietà della classe per essere utilizzati in un metodo dell'hub.

Quando si iniettano più servizi per diversi metodi dell'hub o come modo alternativo di scrivere il codice, i metodi dell'hub possono anche accettare servizi dall'iniezione delle dipendenze. Per impostazione predefinita, i parametri del metodo dell'hub vengono analizzati e risolti tramite l'iniezione delle dipendenze, se possibile.

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message, IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Se la risoluzione implicita dei parametri dai servizi non è desiderata, è possibile disabilitare il comportamento con l'opzione del server DisableImplicitFromServicesParameters .

Per specificare in modo esplicito i parametri risolti dall'inserimento delle dipendenze nei metodi hub, usare la proprietà DisableImplicitFromServicesParameters . Specificare l'attributo [FromServices] o un attributo personalizzato che implementa IFromServiceMetadata nei parametri del metodo dell'hub che devono essere risolti tramite l'iniezione delle dipendenze.

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message,
        [FromServices] IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Note

Questa funzionalità usa IServiceProviderIsService, che è facoltativamente implementata nelle configurazioni di inserimento delle dipendenze. Se il contenitore di inserimento delle dipendenze dell'applicazione non supporta questa funzionalità, l'inserimento di servizi nei metodi hub non è supportato.

Supporto per i servizi con chiave nell'iniezione delle dipendenze

Il meccanismo dei servizi con chiavi consente di registrare e recuperare i servizi di dependency injection tramite chiavi. Un servizio è associato a una chiave chiamando il AddKeyedSingleton metodo per registrarlo. In alternativa, è possibile chiamare il metodo AddKeyedScoped o AddKeyedTransient.

Per accedere a un servizio registrato, specificare la chiave con l'attributo [FromKeyedServices]. Il codice seguente illustra come usare i servizi con chiave:

using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

var app = builder.Build();

app.MapRazorPages();
app.MapHub<MyHub>("/myHub");

app.Run();

public interface ICache
{
    object Get(string key);
}
public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

public class MyHub : Hub
{
    public void SmallCacheMethod([FromKeyedServices("small")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }

    public void BigCacheMethod([FromKeyedServices("big")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }
}

Limitare le chiamate di streaming per connessione

MaximumParallelInvocationsPerClient controlla il numero di invocazioni di metodi hub non di streaming che un client può eseguire contemporaneamente prima che vengano accodate. Non si applica alle invocazioni dello streaming hub. Le chiamate di streaming vengono escluse intenzionalmente perché devono essere a esecuzione prolungata e simultanee, in modo che un client possa avviare qualsiasi numero di flussi simultanei indipendentemente da tale impostazione.

Per applicare un limite per ogni connessione alle chiamate di streaming, racchiudere il flusso all'interno del metodo dell'hub stesso usando una funzione di supporto privata che incrementa un contatore prima di restituire gli elementi e lo decrementa in un blocco finally:

using System.Collections.Concurrent;
using System.Runtime.CompilerServices;

public class StreamingHub : Hub
{
    private static readonly ConcurrentDictionary<string, int> _activeStreams = new();
    private const int MaxConcurrentStreams = 2;

    public IAsyncEnumerable<int> Counter(
        int count,
        int delay,
        CancellationToken cancellationToken)
    {
        return WithLimit(Context.ConnectionId, GetCounter(count, delay, cancellationToken));
    }

    private async IAsyncEnumerable<int> GetCounter(
        int count,
        int delay,
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        for (var i = 0; i < count; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();
            yield return i;
            await Task.Delay(delay, cancellationToken);
        }
    }

    private async IAsyncEnumerable<T> WithLimit(
        string connectionId,
        IAsyncEnumerable<T> stream,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        var current = _activeStreams.AddOrUpdate(
            connectionId,
            addValue: 1,
            updateValueFactory: (_, count) => count + 1);

        if (current > MaxConcurrentStreams)
        {
            Decrement(connectionId);
            throw new HubException(
                $"The connection is limited to {MaxConcurrentStreams} concurrent streaming invocations.");
        }

        try
        {
            await foreach (var item in stream.WithCancellation(cancellationToken))
            {
                yield return item;
            }
        }
        finally
        {
            Decrement(connectionId);
        }
    }

    private static void Decrement(string connectionId)
    {
        while (_activeStreams.TryGetValue(connectionId, out var current))
        {
            if (current <= 1)
            {
                if (_activeStreams.TryRemove(new KeyValuePair<string, int>(connectionId, current)))
                {
                    return;
                }
            }
            else if (_activeStreams.TryUpdate(connectionId, current - 1, current))
            {
                return;
            }
        }
    }
}

Il punto chiave è che WithLimit incapsula l'originale IAsyncEnumerable<T> e mantiene alto il contatore per l'intero ciclo di vita del flusso, non solo fino a quando non viene prodotto il primo elemento. Il finally blocco viene eseguito solo quando il client termina l'utilizzo del flusso, lo annulla o la connessione viene eliminata.

Se i metodi dell'hub di streaming restituiscono ChannelReader<T> invece di IAsyncEnumerable<T>, è possibile applicare un wrapper simile. Deve usare lo stesso dizionario _activeStreams in modo che entrambi i tipi di flusso condividano un singolo limite a livello di connessione invece di mantenere il proprio conteggio indipendente.

Note

Il _activeStreams dizionario è static quindi condiviso tra tutte le istanze dell'hub. Se si preferisce uno stato gestito tramite la dependency injection, registrare un servizio singleton che contiene il dizionario e iniettarlo nel costruttore dell'hub.

Gestire gli eventi per una connessione

L'API SignalR Hubs fornisce i OnConnectedAsync metodi virtuali e OnDisconnectedAsync per gestire e tenere traccia delle connessioni. Eseguire l'override del OnConnectedAsync metodo virtuale per eseguire azioni quando un client si connette all'hub, ad esempio aggiungendolo a un gruppo:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Eseguire l'override del OnDisconnectedAsync metodo virtuale per eseguire azioni quando un client si disconnette. Se il client si disconnette intenzionalmente, ad esempio chiamando connection.stop(), il exception parametro viene impostato su null. Tuttavia, se il client si disconnette a causa di un errore, ad esempio un errore di rete, il exception parametro contiene un'eccezione che descrive l'errore:

public override async Task OnDisconnectedAsync(Exception? exception)
{
    await base.OnDisconnectedAsync(exception);
}

Non è necessario chiamare il RemoveFromGroupAsync metodo all'interno del OnDisconnectedAsync metodo perché viene gestito automaticamente.

Gestire gli errori

Le eccezioni generate nei metodi hub vengono inviate al client che ha richiamato il metodo . Nel client JavaScript il invoke metodo restituisce un oggetto JavaScript 'Promise'. I client possono collegare un catch gestore alla promessa restituita o usarla try/catchcon async/await per gestire le eccezioni:

try {
  await connection.invoke("SendMessage", user, message);
} catch (err) {
  console.error(err);
}

Le connessioni non vengono chiuse quando un hub genera un'eccezione. Per impostazione predefinita, restituisce SignalR un messaggio di errore generico al client, come illustrato nell'esempio seguente:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.

Le eccezioni impreviste contengono spesso informazioni riservate, ad esempio il nome di un server di database in un'eccezione attivata quando la connessione al database non riesce. Come misura di sicurezza, SignalR non espone questi messaggi di errore dettagliati per impostazione predefinita. Per altre informazioni sul motivo per cui i dettagli delle eccezioni vengono eliminati, vedere Considerazioni sulla sicurezza in ASP.NET Core SignalR.

Se è necessario propagare una condizione eccezionale al client, usare la HubException classe . Se viene generata un'eccezione HubException in un metodo hub, SignalRinvia l'intero messaggio di eccezione al client in un formato non modificato:

public Task ThrowException()
    => throw new HubException("This error will be sent to the client!");

Note

SignalR invia solo la Message proprietà dell'eccezione al client. La traccia dello stack e altre proprietà dell'eccezione non sono disponibili per il client.

Di Rachel Appel e Kevin Griffin

L'API SignalR Hubs consente ai client connessi di chiamare metodi sul server, semplificando la comunicazione in tempo reale. Il server definisce i metodi chiamati dal client e il client definisce i metodi chiamati dal server. SignalR consente anche la comunicazione indiretta da client a client, sempre mediata dall'hub, consentendo l'invio SignalR di messaggi tra singoli client, gruppi o a tutti i client connessi. SignalR si occupa di tutto ciò che è necessario per rendere possibile la comunicazione da client a server e da server a client in tempo reale.

Configurare SignalR hub

Per registrare i servizi richiesti dagli SignalR hub, chiamare AddSignalR in Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

Per configurare SignalR gli endpoint, chiamare MapHub, anche in Program.cs:

app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");

app.Run();

Note

Assembly lato server di ASP.NET Core SignalR sono ora installati con .NET Core SDK. Per altre informazioni, vedere SignalR Assembly nel framework condiviso.

Creare e usare hub

Creare un hub dichiarando una classe che eredita da Hub. Aggiungere public metodi alla classe per renderli chiamabili dai client:

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.SendAsync("ReceiveMessage", user, message);
}

Note

Gli hub sono temporanei:

  • Non archiviare lo stato in una proprietà della classe dell'hub. Ogni chiamata al metodo hub viene eseguita in una nuova istanza dell'hub.
  • Non creare un'istanza di un hub direttamente tramite l'inserimento delle dipendenze. Per inviare messaggi a un client da un'altra posizione nell'applicazione, usare un oggetto IHubContext.
  • Usare await quando si chiamano metodi asincroni che dipendono dal mantenere l'hub operativo. Ad esempio, un metodo come Clients.All.SendAsync(...) può fallire se viene chiamato senza await e il metodo hub si completa prima che SendAsync finisca.

Oggetto Context

La Hub classe include una Context proprietà che contiene le proprietà seguenti con informazioni sulla connessione:

Property Description
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Esiste un ID di connessione per ogni connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa ClaimTypes.NameIdentifier da ClaimsPrincipal associato alla connessione come identificatore utente.
User Ottiene l'elemento ClaimsPrincipal associato all'utente corrente.
Items Ottiene una raccolta chiave/valore che può essere utilizzata per condividere i dati nell'ambito di questa connessione. I dati possono essere archiviati in questa raccolta e verranno mantenuti per la connessione tra diverse chiamate al metodo hub.
Features Ottiene la raccolta di funzionalità disponibili nella connessione. Per il momento, questa raccolta non è necessaria nella maggior parte degli scenari, quindi non è ancora documentata in dettaglio.
ConnectionAborted Ottiene un oggetto CancellationToken che notifica quando la connessione viene interrotta.

Hub.Context contiene anche i metodi seguenti:

Method Description
GetHttpContext Restituisce HttpContext per la connessione, oppure null se la connessione non è associata a una richiesta HTTP. Per le connessioni HTTP, usare questo metodo per ottenere informazioni quali intestazioni HTTP e stringhe di query.
Abort Interrompe la connessione.

Oggetto "Clients"

La Hub classe include una Clients proprietà che contiene le proprietà seguenti per la comunicazione tra server e client:

Property Description
All Chiama un metodo su tutti i client connessi
Caller Chiama un metodo sul client che ha richiamato il metodo hub
Others Chiama un metodo su tutti i client connessi, ad eccezione del client che ha richiamato il metodo

Hub.Clients contiene anche i metodi seguenti:

Method Description
AllExcept Chiama un metodo su tutti i client connessi, ad eccezione delle connessioni specificate
Client Chiama un metodo in un client connesso specifico
Clients Chiama un metodo su client connessi specifici
Group Chiama un metodo su tutte le connessioni nel gruppo specificato
GroupExcept Chiama un metodo su tutte le connessioni nel gruppo specificato, ad eccezione delle connessioni specificate
Groups Chiama un metodo su più gruppi di connessioni
OthersInGroup Chiama un metodo su un gruppo di connessioni, escluso il client che ha richiamato il metodo hub
User Chiama un metodo su tutte le connessioni associate a un utente specifico
Users Chiama un metodo su tutte le connessioni associate agli utenti specificati

Ogni proprietà o metodo nelle tabelle precedenti restituisce un oggetto con un SendAsync metodo . Il SendAsync metodo riceve il nome del metodo client da chiamare ed eventuali parametri.

L'oggetto restituito dai Client metodi e Caller contiene anche un InvokeAsync metodo, che può essere utilizzato per attendere un risultato dal client.

Inviare messaggi ai client

Per effettuare chiamate a client specifici, utilizzare le proprietà dell'oggetto Clients . Nell'esempio seguente sono disponibili tre metodi hub:

  • SendMessage invia un messaggio a tutti i client connessi usando Clients.All.
  • SendMessageToCaller invia un messaggio al chiamante usando Clients.Caller.
  • SendMessageToGroup invia un messaggio a tutti i client del SignalR Users gruppo.
public async Task SendMessage(string user, string message)
    => await Clients.All.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToCaller(string user, string message)
    => await Clients.Caller.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToGroup(string user, string message)
    => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);

Hub fortemente tipizzato

Uno svantaggio dell'uso SendAsync è che si basa su una stringa per specificare il metodo client da chiamare. In questo modo, il codice diventa suscettibile a errori di runtime se il nome del metodo è stato digitato in modo non corretto o non presente nel client.

Un'alternativa all'uso SendAsync consiste nel digitare fortemente la Hub classe con Hub<T>. Nell'esempio seguente il ChatHub metodo client è stato estratto in un'interfaccia denominata IChatClient:

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Questa interfaccia può essere usata per effettuare il refactoring dell'esempio precedente ChatHub per renderlo fortemente tipizzato.

public class StronglyTypedChatHub : Hub<IChatClient>
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.ReceiveMessage(user, message);

    public async Task SendMessageToCaller(string user, string message)
        => await Clients.Caller.ReceiveMessage(user, message);

    public async Task SendMessageToGroup(string user, string message)
        => await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}

L'uso di Hub<IChatClient> consente il controllo in fase di compilazione dei metodi client. In questo modo si evitano problemi causati dall'uso di stringhe, poiché Hub<T> può fornire l'accesso solo ai metodi definiti nell'interfaccia . L'uso di un oggetto fortemente tipizzato Hub<T> disabilita la possibilità di usare SendAsync.

Note

Il Async suffisso non viene rimosso dai nomi dei metodi. A meno che un metodo client non sia definito con .on('MyMethodAsync'), non usare MyMethodAsync come nome.

Risultati del client

Oltre a effettuare chiamate ai client, il server può richiedere un risultato da un client. Ciò richiede che il server usi ISingleClientProxy.InvokeAsync e che il client restituisca un risultato dal relativo .On gestore.

Esistono due modi per usare l'API nel server, la prima consiste nel chiamare Client(...) o Caller nella Clients proprietà in un metodo hub:

public class ChatHub : Hub
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        var message = await Clients.Client(connectionId).InvokeAsync<string>(
            "GetMessage");
        return message;
    }
}

Il secondo modo consiste nel chiamare Client(...) su un'istanza di IHubContext<T>:

async Task SomeMethod(IHubContext<MyHub> context)
{
    string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
        "GetMessage");
}

Gli hub fortemente tipizzati possono anche restituire valori dai metodi di interfaccia:

public interface IClient
{
    Task<string> GetMessage();
}

public class ChatHub : Hub<IClient>
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        string message = await Clients.Client(connectionId).GetMessage();
        return message;
    }
}

I client restituiscono i risultati nei relativi .On(...) gestori, come illustrato di seguito:

Client .NET

hubConnection.On("GetMessage", async () =>
{
    Console.WriteLine("Enter message:");
    var message = await Console.In.ReadLineAsync();
    return message;
});

Client Typescript

hubConnection.on("GetMessage", async () => {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("message");
        }, 100);
    });
    return promise;
});

Client Java

hubConnection.onWithResult("GetMessage", () -> {
    return Single.just("message");
});

Modificare il nome di un metodo hub

Per impostazione predefinita, il nome di un metodo hub server è il nome del metodo .NET. Per modificare questo comportamento predefinito per un metodo specifico, usare l'attributo HubMethodName . Il client deve usare questo nome anziché il nome del metodo .NET quando si richiama il metodo :

[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
    => await Clients.User(user).SendAsync("ReceiveMessage", user, message);

Inserire i servizi in un hub

I costruttori dell'hub possono accettare servizi dalla Dependency Injection come parametri, che possono essere archiviati nelle proprietà della classe da usare in un metodo dell'hub.

Quando si inseriscono più servizi per i diversi metodi hub o come metodo alternativo di scrittura del codice, i metodi del hub possono anche accettare servizi dal Dependency Injection. Per impostazione predefinita, i parametri del metodo hub vengono esaminati e risolti dall'inserimento delle dipendenze, se possibile.

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message, IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Se la risoluzione implicita dei parametri dai servizi non è desiderata, disabilitarla con DisableImplicitFromServicesParameters. Per specificare in modo esplicito i parametri risolti dall'inserimento delle dipendenze nei metodi hub, usare l'opzione DisableImplicitFromServicesParameters e usare l'attributo o un attributo personalizzato che implementa [FromServices] nei parametri del metodo hub che devono essere risolti dall'inserimento IFromServiceMetadata delle dipendenze.

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message,
        [FromServices] IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Note

Questa funzionalità usa IServiceProviderIsService, che è facoltativamente implementata dalle implementazioni di DI. Se il contenitore delle dipendenze dell'app non supporta questa funzionalità, l'inserimento dei servizi nei metodi hub non è supportato.

Gestire gli eventi per una connessione

L'API SignalR Hubs fornisce i OnConnectedAsync metodi virtuali e OnDisconnectedAsync per gestire e tenere traccia delle connessioni. Eseguire l'override del OnConnectedAsync metodo virtuale per eseguire azioni quando un client si connette all'hub, ad esempio aggiungendolo a un gruppo:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Eseguire l'override del OnDisconnectedAsync metodo virtuale per eseguire azioni quando un client si disconnette. Se il client si disconnette intenzionalmente, ad esempio chiamando connection.stop(), il exception parametro viene impostato su null. Tuttavia, se il client si disconnette a causa di un errore, ad esempio un errore di rete, il exception parametro contiene un'eccezione che descrive l'errore:

public override async Task OnDisconnectedAsync(Exception? exception)
{
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync non deve essere chiamato in OnDisconnectedAsync, viene gestito automaticamente per l'utente.

Gestire gli errori

Le eccezioni generate nei metodi hub vengono inviate al client che ha richiamato il metodo . Nel client JavaScript il invoke metodo restituisce un codice JavaScript Promise. I client possono collegare un catch gestore alla promessa restituita o usarla try/catchcon async/await per gestire le eccezioni:

try {
  await connection.invoke("SendMessage", user, message);
} catch (err) {
  console.error(err);
}

Le connessioni non vengono chiuse quando un hub genera un'eccezione. Per impostazione predefinita, restituisce SignalR un messaggio di errore generico al client, come illustrato nell'esempio seguente:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.

Le eccezioni impreviste contengono spesso informazioni riservate, ad esempio il nome di un server di database in un'eccezione attivata quando la connessione al database non riesce. SignalR non espone questi messaggi di errore dettagliati per impostazione predefinita come misura di sicurezza. Per altre informazioni sul motivo per cui i dettagli delle eccezioni vengono eliminati, vedere Considerazioni sulla sicurezza in ASP.NET Core SignalR.

Se è necessario propagare una condizione eccezionale al client, usare la HubException classe . Se viene generata un'eccezione HubException in un metodo hub, SignalRinvia l'intero messaggio di eccezione al client, non modificato:

public Task ThrowException()
    => throw new HubException("This error will be sent to the client!");

Note

SignalR invia solo la Message proprietà dell'eccezione al client. La traccia dello stack e altre proprietà dell'eccezione non sono disponibili per il client.

Risorse aggiuntive

Di Rachel Appel e Kevin Griffin

L'API SignalR Hubs consente ai client connessi di chiamare metodi sul server, semplificando la comunicazione in tempo reale. Il server definisce i metodi chiamati dal client e il client definisce i metodi chiamati dal server. SignalR consente anche la comunicazione indiretta da client a client, sempre mediata dall'hub, consentendo l'invio SignalR di messaggi tra singoli client, gruppi o a tutti i client connessi. SignalR si occupa di tutto ciò che è necessario per rendere possibile la comunicazione da client a server e da server a client in tempo reale.

Configurare SignalR hub

Per registrare i servizi richiesti dagli SignalR hub, chiamare AddSignalR in Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

Per configurare SignalR gli endpoint, chiamare MapHub, anche in Program.cs:

app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");

app.Run();

Note

Assembly lato server di ASP.NET Core SignalR sono ora installati con .NET Core SDK. Per altre informazioni, vedere SignalR Assembly nel framework condiviso.

Creare e usare hub

Creare un hub dichiarando una classe che eredita da Hub. Aggiungere public metodi alla classe per renderli chiamabili dai client:

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.SendAsync("ReceiveMessage", user, message);
}

Note

Gli hub sono temporanei:

  • Non archiviare lo stato in una proprietà della classe dell'hub. Ogni chiamata al metodo hub viene eseguita in una nuova istanza dell'hub.
  • Non creare un'istanza di un hub direttamente tramite l'inserimento delle dipendenze. Per inviare messaggi a un client da un'altra posizione nell'applicazione, usare un oggetto IHubContext.
  • Usare await quando si chiamano metodi asincroni che dipendono dal mantenere l'hub operativo. Ad esempio, un metodo come Clients.All.SendAsync(...) può fallire se viene chiamato senza await e il metodo hub si completa prima che SendAsync finisca.

Oggetto Context

La Hub classe include una Context proprietà che contiene le proprietà seguenti con informazioni sulla connessione:

Property Description
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Esiste un ID di connessione per ogni connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa ClaimTypes.NameIdentifier da ClaimsPrincipal associato alla connessione come identificatore utente.
User Ottiene l'elemento ClaimsPrincipal associato all'utente corrente.
Items Ottiene una raccolta chiave/valore che può essere utilizzata per condividere i dati nell'ambito di questa connessione. I dati possono essere archiviati in questa raccolta e verranno mantenuti per la connessione tra diverse chiamate al metodo hub.
Features Ottiene la raccolta di funzionalità disponibili nella connessione. Per il momento, questa raccolta non è necessaria nella maggior parte degli scenari, quindi non è ancora documentata in dettaglio.
ConnectionAborted Ottiene un oggetto CancellationToken che notifica quando la connessione viene interrotta.

Hub.Context contiene anche i metodi seguenti:

Method Description
GetHttpContext Restituisce HttpContext per la connessione, oppure null se la connessione non è associata a una richiesta HTTP. Per le connessioni HTTP, usare questo metodo per ottenere informazioni quali intestazioni HTTP e stringhe di query.
Abort Interrompe la connessione.

Oggetto "Clients"

La Hub classe include una Clients proprietà che contiene le proprietà seguenti per la comunicazione tra server e client:

Property Description
All Chiama un metodo su tutti i client connessi
Caller Chiama un metodo sul client che ha richiamato il metodo hub
Others Chiama un metodo su tutti i client connessi, ad eccezione del client che ha richiamato il metodo

Hub.Clients contiene anche i metodi seguenti:

Method Description
AllExcept Chiama un metodo su tutti i client connessi, ad eccezione delle connessioni specificate
Client Chiama un metodo in un client connesso specifico
Clients Chiama un metodo su client connessi specifici
Group Chiama un metodo su tutte le connessioni nel gruppo specificato
GroupExcept Chiama un metodo su tutte le connessioni nel gruppo specificato, ad eccezione delle connessioni specificate
Groups Chiama un metodo su più gruppi di connessioni
OthersInGroup Chiama un metodo su un gruppo di connessioni, escluso il client che ha richiamato il metodo hub
User Chiama un metodo su tutte le connessioni associate a un utente specifico
Users Chiama un metodo su tutte le connessioni associate agli utenti specificati

Ogni proprietà o metodo nelle tabelle precedenti restituisce un oggetto con un SendAsync metodo . Il SendAsync metodo riceve il nome del metodo client da chiamare ed eventuali parametri.

Inviare messaggi ai client

Per effettuare chiamate a client specifici, utilizzare le proprietà dell'oggetto Clients . Nell'esempio seguente sono disponibili tre metodi hub:

  • SendMessage invia un messaggio a tutti i client connessi usando Clients.All.
  • SendMessageToCaller invia un messaggio al chiamante usando Clients.Caller.
  • SendMessageToGroup invia un messaggio a tutti i client del SignalR Users gruppo.
public async Task SendMessage(string user, string message)
    => await Clients.All.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToCaller(string user, string message)
    => await Clients.Caller.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToGroup(string user, string message)
    => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);

Hub fortemente tipizzato

Uno svantaggio dell'uso SendAsync è che si basa su una stringa per specificare il metodo client da chiamare. In questo modo, il codice diventa suscettibile a errori di runtime se il nome del metodo è stato digitato in modo non corretto o non presente nel client.

Un'alternativa all'uso SendAsync consiste nel digitare fortemente la Hub classe con Hub<T>. Nell'esempio seguente il ChatHub metodo client è stato estratto in un'interfaccia denominata IChatClient:

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Questa interfaccia può essere usata per effettuare il refactoring dell'esempio precedente ChatHub per renderlo fortemente tipizzato.

public class StronglyTypedChatHub : Hub<IChatClient>
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.ReceiveMessage(user, message);

    public async Task SendMessageToCaller(string user, string message)
        => await Clients.Caller.ReceiveMessage(user, message);

    public async Task SendMessageToGroup(string user, string message)
        => await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}

L'uso di Hub<IChatClient> consente il controllo in fase di compilazione dei metodi client. In questo modo si evitano problemi causati dall'uso di stringhe, poiché Hub<T> può fornire l'accesso solo ai metodi definiti nell'interfaccia . L'uso di un oggetto fortemente tipizzato Hub<T> disabilita la possibilità di usare SendAsync.

Note

Il Async suffisso non viene rimosso dai nomi dei metodi. A meno che un metodo client non sia definito con .on('MyMethodAsync'), non usare MyMethodAsync come nome.

Modificare il nome di un metodo hub

Per impostazione predefinita, il nome di un metodo hub server è il nome del metodo .NET. Per modificare questo comportamento predefinito per un metodo specifico, usare l'attributo HubMethodName . Il client deve usare questo nome anziché il nome del metodo .NET quando si richiama il metodo :

[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
    => await Clients.User(user).SendAsync("ReceiveMessage", user, message);

Gestire gli eventi per una connessione

L'API SignalR Hubs fornisce i OnConnectedAsync metodi virtuali e OnDisconnectedAsync per gestire e tenere traccia delle connessioni. Eseguire l'override del OnConnectedAsync metodo virtuale per eseguire azioni quando un client si connette all'hub, ad esempio aggiungendolo a un gruppo:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Eseguire l'override del OnDisconnectedAsync metodo virtuale per eseguire azioni quando un client si disconnette. Se il client si disconnette intenzionalmente, ad esempio chiamando connection.stop(), il exception parametro viene impostato su null. Tuttavia, se il client si disconnette a causa di un errore, ad esempio un errore di rete, il exception parametro contiene un'eccezione che descrive l'errore:

public override async Task OnDisconnectedAsync(Exception? exception)
{
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync non deve essere chiamato in OnDisconnectedAsync, viene gestito automaticamente per l'utente.

Gestire gli errori

Le eccezioni generate nei metodi hub vengono inviate al client che ha richiamato il metodo . Nel client JavaScript il invoke metodo restituisce un codice JavaScript Promise. I client possono collegare un catch gestore alla promessa restituita o usarla try/catchcon async/await per gestire le eccezioni:

try {
  await connection.invoke("SendMessage", user, message);
} catch (err) {
  console.error(err);
}

Le connessioni non vengono chiuse quando un hub genera un'eccezione. Per impostazione predefinita, restituisce SignalR un messaggio di errore generico al client, come illustrato nell'esempio seguente:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.

Le eccezioni impreviste contengono spesso informazioni riservate, ad esempio il nome di un server di database in un'eccezione attivata quando la connessione al database non riesce. SignalR non espone questi messaggi di errore dettagliati per impostazione predefinita come misura di sicurezza. Per altre informazioni sul motivo per cui i dettagli delle eccezioni vengono eliminati, vedere Considerazioni sulla sicurezza in ASP.NET Core SignalR.

Se è necessario propagare una condizione eccezionale al client, usare la HubException classe . Se viene generata un'eccezione HubException in un metodo hub, SignalRinvia l'intero messaggio di eccezione al client, non modificato:

public Task ThrowException()
    => throw new HubException("This error will be sent to the client!");

Note

SignalR invia solo la Message proprietà dell'eccezione al client. La traccia dello stack e altre proprietà dell'eccezione non sono disponibili per il client.

Risorse aggiuntive

Di Rachel Appel e Kevin Griffin

Visualizzare o scaricare il codice di esempio (procedura per il download)

Che cos'è un SignalR hub

L'API SignalR Hubs consente ai client connessi di chiamare metodi sul server, semplificando la comunicazione in tempo reale. Il server definisce i metodi chiamati dal client e il client definisce i metodi chiamati dal server. SignalR consente anche la comunicazione indiretta da client a client, sempre mediata dall'hub, consentendo l'invio SignalR di messaggi tra singoli client, gruppi o a tutti i client connessi. SignalR si occupa di tutto ciò che è necessario per rendere possibile la comunicazione da client a server e da server a client in tempo reale.

Configurare SignalR hub

Il SignalR middleware richiede alcuni servizi, configurati chiamando AddSignalR:

services.AddSignalR();

Quando si aggiungono SignalR funzioni a un'app ASP.NET Core, configurare le SignalR route chiamando MapHub nel metodo di callback Startup.Configure del UseEndpoints.

app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ChatHub>("/chathub");
});

Note

Assembly lato server di ASP.NET Core SignalR sono ora installati con .NET Core SDK. Per altre informazioni, vedere SignalR Assembly nel framework condiviso.

Creare e usare hub

Creare un hub dichiarando una classe che eredita da Hube aggiungendo metodi pubblici. I client possono chiamare metodi definiti come public:

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message)
    {
        return Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

È possibile specificare un tipo restituito e parametri, inclusi tipi complessi e matrici, come in qualsiasi metodo C#. SignalR gestisce la serializzazione e la deserializzazione di oggetti e matrici complessi nei parametri e nei valori restituiti.

Note

Gli hub sono temporanei:

  • Non archiviare lo stato in una proprietà nella classe hub. Ogni chiamata al metodo hub viene eseguita in una nuova istanza dell'hub.
  • Non creare un'istanza di un hub direttamente tramite l'inserimento delle dipendenze. Per inviare messaggi a un client da un'altra posizione nell'applicazione, usare un oggetto IHubContext.
  • Usare await quando si chiamano metodi asincroni che dipendono dal mantenere l'hub operativo. Ad esempio, un metodo come Clients.All.SendAsync(...) può fallire se viene chiamato senza await e il metodo hub si completa prima che SendAsync finisca.

Oggetto Context

La Hub classe ha una Context proprietà che contiene le proprietà seguenti con informazioni sulla connessione:

Property Description
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Esiste un ID di connessione per ogni connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa ClaimTypes.NameIdentifier da ClaimsPrincipal associato alla connessione come identificatore utente.
User Ottiene l'elemento ClaimsPrincipal associato all'utente corrente.
Items Ottiene una raccolta chiave/valore che può essere utilizzata per condividere i dati nell'ambito di questa connessione. I dati possono essere archiviati in questa raccolta e verranno mantenuti per la connessione tra diverse chiamate al metodo hub.
Features Ottiene la raccolta di funzionalità disponibili nella connessione. Per il momento, questa raccolta non è necessaria nella maggior parte degli scenari, quindi non è ancora documentata in dettaglio.
ConnectionAborted Ottiene un oggetto CancellationToken che notifica quando la connessione viene interrotta.

Hub.Context contiene anche i metodi seguenti:

Method Description
GetHttpContext Restituisce HttpContext per la connessione, oppure null se la connessione non è associata a una richiesta HTTP. Per le connessioni HTTP, è possibile usare questo metodo per ottenere informazioni quali intestazioni HTTP e stringhe di query.
Abort Interrompe la connessione.

Oggetto "Clients"

La Hub classe ha una Clients proprietà che contiene le proprietà seguenti per la comunicazione tra server e client:

Property Description
All Chiama un metodo su tutti i client connessi
Caller Chiama un metodo sul client che ha richiamato il metodo hub
Others Chiama un metodo su tutti i client connessi, ad eccezione del client che ha richiamato il metodo

Hub.Clients contiene anche i metodi seguenti:

Method Description
AllExcept Chiama un metodo su tutti i client connessi, ad eccezione delle connessioni specificate
Client Chiama un metodo in un client connesso specifico
Clients Chiama un metodo su client connessi specifici
Group Chiama un metodo su tutte le connessioni nel gruppo specificato
GroupExcept Chiama un metodo su tutte le connessioni nel gruppo specificato, ad eccezione delle connessioni specificate
Groups Chiama un metodo su più gruppi di connessioni
OthersInGroup Chiama un metodo su un gruppo di connessioni, escluso il client che ha richiamato il metodo hub
User Chiama un metodo su tutte le connessioni associate a un utente specifico
Users Chiama un metodo su tutte le connessioni associate agli utenti specificati

Ogni proprietà o metodo nelle tabelle precedenti restituisce un oggetto con un SendAsync metodo . Il SendAsync metodo consente di specificare il nome e i parametri del metodo client da chiamare.

Inviare messaggi ai client

Per effettuare chiamate a client specifici, utilizzare le proprietà dell'oggetto Clients . Nell'esempio seguente sono disponibili tre metodi hub:

  • SendMessage invia un messaggio a tutti i client connessi usando Clients.All.
  • SendMessageToCaller invia un messaggio al chiamante usando Clients.Caller.
  • SendMessageToGroup invia un messaggio a tutti i client del SignalR Users gruppo.
public Task SendMessage(string user, string message)
{
    return Clients.All.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToCaller(string user, string message)
{
    return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToGroup(string user, string message)
{
    return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}

Hub fortemente tipizzato

Uno svantaggio dell'uso SendAsync è che si basa su una stringa magic per specificare il metodo client da chiamare. In questo modo, il codice diventa suscettibile a errori di runtime se il nome del metodo è stato digitato in modo non corretto o non presente nel client.

Un'alternativa all'uso di SendAsync è tipizzare fortemente Hub con Hub<T>. Nell'esempio seguente i ChatHub metodi client sono stati estratti in un'interfaccia denominata IChatClient.

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Questa interfaccia può essere usata per effettuare il refactoring dell'esempio precedente ChatHub :

    public class StronglyTypedChatHub : Hub<IChatClient>
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.ReceiveMessage(user, message);
        }

        public Task SendMessageToCaller(string user, string message)
        {
            return Clients.Caller.ReceiveMessage(user, message);
        }
}

L'uso di Hub<IChatClient> consente il controllo in fase di compilazione dei metodi client. In questo modo si evitano problemi causati dall'uso di stringhe magic, poiché Hub<T> può fornire l'accesso solo ai metodi definiti nell'interfaccia.

L'uso di un oggetto fortemente tipizzato Hub<T> disabilita la possibilità di usare SendAsync. Tutti i metodi definiti nell'interfaccia possono comunque essere definiti come asincroni. In effetti, ognuno di questi metodi deve restituire un oggetto Task. Poiché si tratta di un'interfaccia, non usare la async parola chiave . Per esempio:

public interface IClient
{
    Task ClientMethod();
}

Note

Il Async suffisso non viene rimosso dal nome del metodo. A meno che il metodo client non sia definito con .on('MyMethodAsync'), non è consigliabile usare MyMethodAsync come nome.

Modificare il nome di un metodo hub

Per impostazione predefinita, il nome di un metodo hub server è il nome del metodo .NET. Tuttavia, è possibile usare l'attributo HubMethodName per modificare questo valore predefinito e specificare manualmente un nome per il metodo. Il client deve usare questo nome, anziché il nome del metodo .NET, quando si richiama il metodo :

[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
    return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}

Gestire gli eventi per una connessione

L'API SignalR Hubs fornisce i OnConnectedAsync metodi virtuali e OnDisconnectedAsync per gestire e tenere traccia delle connessioni. Eseguire l'override del OnConnectedAsync metodo virtuale per eseguire azioni quando un client si connette all'hub, ad esempio aggiungendolo a un gruppo:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Eseguire l'override del OnDisconnectedAsync metodo virtuale per eseguire azioni quando un client si disconnette. Se il client si disconnette intenzionalmente (chiamando connection.stop(), ad esempio), il exception parametro sarà null. Tuttavia, se il client è disconnesso a causa di un errore (ad esempio un errore di rete), il exception parametro conterrà un'eccezione che descrive l'errore:

public override async Task OnDisconnectedAsync(Exception exception)
{
    await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync non deve essere chiamato in OnDisconnectedAsync, viene gestito automaticamente per l'utente.

Warning

Avviso di sicurezza: L'esposizione di ConnectionId può causare un'imitazione dannosa se la versione del server o del client SignalR è ASP.NET Core 2.2 o precedenti.

Gestire gli errori

Le eccezioni generate nei metodi hub vengono inviate al client che ha richiamato il metodo. Nel client JavaScript il invoke metodo restituisce un codice JavaScript Promise. Quando il client riceve un errore con un gestore associato alla promessa usando catch, viene richiamato e passato come oggetto JavaScript Error :

connection.invoke("SendMessage", user, message).catch(err => console.error(err));

Se l'hub genera un'eccezione, le connessioni non vengono chiuse. Per impostazione predefinita, SignalR restituisce un messaggio di errore generico al client. Per esempio:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.

Le eccezioni impreviste contengono spesso informazioni riservate, ad esempio il nome di un server di database in un'eccezione attivata quando la connessione al database non riesce. SignalR non espone questi messaggi di errore dettagliati per impostazione predefinita come misura di sicurezza. Per altre informazioni sul motivo per cui i dettagli delle eccezioni vengono eliminati, vedere Considerazioni sulla sicurezza in ASP.NET Core SignalR.

Se avete un'eccezione che davvero desiderate propagare al client, potete utilizzare la classe HubException. Se si genera un oggetto HubException dal metodo hub, SignalRinvierà l'intero messaggio al client, senza modifiche:

public Task ThrowException()
{
    throw new HubException("This error will be sent to the client!");
}

Note

SignalR invia solo la Message proprietà dell'eccezione al client. La traccia dello stack e altre proprietà dell'eccezione non sono disponibili per il client.

Risorse aggiuntive

Di Rachel Appel e Kevin Griffin

Visualizzare o scaricare il codice di esempio (procedura per il download)

Che cos'è un SignalR hub

L'API SignalR Hubs consente ai client connessi di chiamare metodi sul server, semplificando la comunicazione in tempo reale. Il server definisce i metodi chiamati dal client e il client definisce i metodi chiamati dal server. SignalR consente anche la comunicazione indiretta da client a client, sempre mediata dall'hub, consentendo l'invio SignalR di messaggi tra singoli client, gruppi o a tutti i client connessi. SignalR si occupa di tutto ciò che è necessario per rendere possibile la comunicazione da client a server e da server a client in tempo reale.

Configurare SignalR hub

Il SignalR middleware richiede alcuni servizi, configurati chiamando AddSignalR:

services.AddSignalR();

Quando aggiungi SignalR funzionalità a un'app ASP.NET Core, configura SignalR le rotte chiamando UseSignalR nel metodo Startup.Configure.

app.UseSignalR(route =>
{
    route.MapHub<ChatHub>("/chathub");
});

Creare e usare hub

Creare un hub dichiarando una classe che eredita da Hube aggiungendo metodi pubblici. I client possono chiamare metodi definiti come public:

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message)
    {
        return Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

È possibile specificare un tipo restituito e parametri, inclusi tipi complessi e matrici, come in qualsiasi metodo C#. SignalR gestisce la serializzazione e la deserializzazione di oggetti e matrici complessi nei parametri e nei valori restituiti.

Note

Gli hub sono temporanei:

  • Non archiviare lo stato in una proprietà nella classe hub. Ogni chiamata al metodo hub viene eseguita in una nuova istanza dell'hub.
  • Non creare un'istanza di un hub direttamente tramite l'inserimento delle dipendenze. Per inviare messaggi a un client da un'altra posizione nell'applicazione, usare un oggetto IHubContext.
  • Usare await quando si chiamano metodi asincroni che dipendono dal mantenere l'hub operativo. Ad esempio, un metodo come Clients.All.SendAsync(...) può fallire se viene chiamato senza await e il metodo hub si completa prima che SendAsync finisca.

Oggetto Context

La Hub classe ha una Context proprietà che contiene le proprietà seguenti con informazioni sulla connessione:

Property Description
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Esiste un ID di connessione per ogni connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa ClaimTypes.NameIdentifier da ClaimsPrincipal associato alla connessione come identificatore utente.
User Ottiene l'elemento ClaimsPrincipal associato all'utente corrente.
Items Ottiene una raccolta chiave/valore che può essere utilizzata per condividere i dati nell'ambito di questa connessione. I dati possono essere archiviati in questa raccolta e verranno mantenuti per la connessione tra diverse chiamate al metodo hub.
Features Ottiene la raccolta di funzionalità disponibili nella connessione. Per il momento, questa raccolta non è necessaria nella maggior parte degli scenari, quindi non è ancora documentata in dettaglio.
ConnectionAborted Ottiene un oggetto CancellationToken che notifica quando la connessione viene interrotta.

Hub.Context contiene anche i metodi seguenti:

Method Description
GetHttpContext Restituisce HttpContext per la connessione, oppure null se la connessione non è associata a una richiesta HTTP. Per le connessioni HTTP, è possibile usare questo metodo per ottenere informazioni quali intestazioni HTTP e stringhe di query.
Abort Interrompe la connessione.

Oggetto "Clients"

La Hub classe ha una Clients proprietà che contiene le proprietà seguenti per la comunicazione tra server e client:

Property Description
All Chiama un metodo su tutti i client connessi
Caller Chiama un metodo sul client che ha richiamato il metodo hub
Others Chiama un metodo su tutti i client connessi, ad eccezione del client che ha richiamato il metodo

Hub.Clients contiene anche i metodi seguenti:

Method Description
AllExcept Chiama un metodo su tutti i client connessi, ad eccezione delle connessioni specificate
Client Chiama un metodo in un client connesso specifico
Clients Chiama un metodo su client connessi specifici
Group Chiama un metodo su tutte le connessioni nel gruppo specificato
GroupExcept Chiama un metodo su tutte le connessioni nel gruppo specificato, ad eccezione delle connessioni specificate
Groups Chiama un metodo su più gruppi di connessioni
OthersInGroup Chiama un metodo su un gruppo di connessioni, escluso il client che ha richiamato il metodo hub
User Chiama un metodo su tutte le connessioni associate a un utente specifico
Users Chiama un metodo su tutte le connessioni associate agli utenti specificati

Ogni proprietà o metodo nelle tabelle precedenti restituisce un oggetto con un SendAsync metodo . Il SendAsync metodo consente di specificare il nome e i parametri del metodo client da chiamare.

Inviare messaggi ai client

Per effettuare chiamate a client specifici, utilizzare le proprietà dell'oggetto Clients . Nell'esempio seguente sono disponibili tre metodi hub:

  • SendMessage invia un messaggio a tutti i client connessi usando Clients.All.
  • SendMessageToCaller invia un messaggio al chiamante usando Clients.Caller.
  • SendMessageToGroup invia un messaggio a tutti i client del SignalR Users gruppo.
public Task SendMessage(string user, string message)
{
    return Clients.All.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToCaller(string user, string message)
{
    return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToGroup(string user, string message)
{
    return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}

Hub fortemente tipizzato

Uno svantaggio dell'uso SendAsync è che si basa su una stringa magic per specificare il metodo client da chiamare. In questo modo, il codice diventa suscettibile a errori di runtime se il nome del metodo è stato digitato in modo non corretto o non presente nel client.

Un'alternativa all'uso di SendAsync è tipizzare fortemente Hub con Hub<T>. Nell'esempio seguente i ChatHub metodi client sono stati estratti in un'interfaccia denominata IChatClient.

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Questa interfaccia può essere usata per effettuare il refactoring dell'esempio precedente ChatHub :

    public class StronglyTypedChatHub : Hub<IChatClient>
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.ReceiveMessage(user, message);
        }

        public Task SendMessageToCaller(string user, string message)
        {
            return Clients.Caller.ReceiveMessage(user, message);
        }
}

L'uso di Hub<IChatClient> consente il controllo in fase di compilazione dei metodi client. In questo modo si evitano problemi causati dall'uso di stringhe magic, poiché Hub<T> può fornire l'accesso solo ai metodi definiti nell'interfaccia.

L'uso di un oggetto fortemente tipizzato Hub<T> disabilita la possibilità di usare SendAsync. Tutti i metodi definiti nell'interfaccia possono comunque essere definiti come asincroni. In effetti, ognuno di questi metodi deve restituire un oggetto Task. Poiché si tratta di un'interfaccia, non usare la async parola chiave . Per esempio:

public interface IClient
{
    Task ClientMethod();
}

Note

Il Async suffisso non viene rimosso dal nome del metodo. A meno che il metodo client non sia definito con .on('MyMethodAsync'), non è consigliabile usare MyMethodAsync come nome.

Modificare il nome di un metodo hub

Per impostazione predefinita, il nome di un metodo hub server è il nome del metodo .NET. Tuttavia, è possibile usare l'attributo HubMethodName per modificare questo valore predefinito e specificare manualmente un nome per il metodo. Il client deve usare questo nome, anziché il nome del metodo .NET, quando si richiama il metodo :

[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
    return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}

Gestire gli eventi per una connessione

L'API SignalR Hubs fornisce i OnConnectedAsync metodi virtuali e OnDisconnectedAsync per gestire e tenere traccia delle connessioni. Eseguire l'override del OnConnectedAsync metodo virtuale per eseguire azioni quando un client si connette all'hub, ad esempio aggiungendolo a un gruppo:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Eseguire l'override del OnDisconnectedAsync metodo virtuale per eseguire azioni quando un client si disconnette. Se il client si disconnette intenzionalmente (chiamando connection.stop(), ad esempio), il exception parametro sarà null. Tuttavia, se il client è disconnesso a causa di un errore (ad esempio un errore di rete), il exception parametro conterrà un'eccezione che descrive l'errore:

public override async Task OnDisconnectedAsync(Exception exception)
{
    await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync non deve essere chiamato in OnDisconnectedAsync, viene gestito automaticamente per l'utente.

Warning

Avviso di sicurezza: L'esposizione di ConnectionId può causare un'imitazione dannosa se la versione del server o del client SignalR è ASP.NET Core 2.2 o precedenti.

Gestire gli errori

Le eccezioni generate nei metodi hub vengono inviate al client che ha richiamato il metodo. Nel client JavaScript il invoke metodo restituisce un codice JavaScript Promise. Quando il client riceve un errore con un gestore associato alla promessa usando catch, viene richiamato e passato come oggetto JavaScript Error :

connection.invoke("SendMessage", user, message).catch(err => console.error(err));

Se l'hub genera un'eccezione, le connessioni non vengono chiuse. Per impostazione predefinita, SignalR restituisce un messaggio di errore generico al client. Per esempio:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.

Le eccezioni impreviste contengono spesso informazioni riservate, ad esempio il nome di un server di database in un'eccezione attivata quando la connessione al database non riesce. SignalR non espone questi messaggi di errore dettagliati per impostazione predefinita come misura di sicurezza. Per altre informazioni sul motivo per cui i dettagli delle eccezioni vengono eliminati, vedere Considerazioni sulla sicurezza in ASP.NET Core SignalR.

Se avete un'eccezione che davvero desiderate propagare al client, potete utilizzare la classe HubException. Se si genera un oggetto HubException dal metodo hub, SignalRinvierà l'intero messaggio al client, senza modifiche:

public Task ThrowException()
{
    throw new HubException("This error will be sent to the client!");
}

Note

SignalR invia solo la Message proprietà dell'eccezione al client. La traccia dello stack e altre proprietà dell'eccezione non sono disponibili per il client.

Risorse aggiuntive