Usare hub in SignalR per ASP.NET Core

Di Rachel Appel e Kevin Griffin

L'API SignalR Hubs consente ai client connessi di chiamare i metodi nel server. Il server definisce i metodi chiamati dal client e il client definisce i metodi chiamati dal server. 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 MapHubanche in Program.cs:

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

app.Run();

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

Nota

Gli hub sono temporanei:

  • Non archiviare lo stato in una proprietà della classe hub. Ogni chiamata al metodo hub viene eseguita in una nuova istanza dell'hub.
  • Usare await quando si chiamano metodi asincroni che dipendono dall'hub che rimangono attivi. Ad esempio, un metodo come Clients.All.SendAsync(...) può avere esito negativo se viene chiamato senza await e il metodo hub viene completato prima SendAsync del completamento.

Oggetto Context

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

Proprietà Descrizione
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Per ogni connessione è presente un ID connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa l'oggetto ClaimTypes.NameIdentifier dell'oggetto ClaimsPrincipal associato alla connessione come identificatore utente.
User Ottiene l'oggetto 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 CancellationToken oggetto che notifica quando la connessione viene interrotta.

Hub.Context contiene anche i metodi seguenti:

Metodo Descrizione
GetHttpContext Restituisce l'oggetto HttpContext per la connessione o 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:

Proprietà Descrizione
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:

Metodo Descrizione
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 consiste nel fatto che si basa su una stringa per specificare il metodo client da chiamare. In questo modo il codice viene aperto agli errori di runtime se il nome del metodo non è corretto o risulta mancante dal 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 essere fortemente tipizzata:

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 Hub<IChatClient> di abilita il controllo in fase di compilazione dei metodi client. Ciò impedisce i problemi causati dall'uso di stringhe, perché 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.

Nota

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 è 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.

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 javaScript Promise. I client possono collegare un catch gestore alla promessa restituita o usare/catchtrycon 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, SignalR restituisce 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, senza modifiche:

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

Nota

SignalR invia solo la Message proprietà dell'eccezione al client. L'analisi 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(come scaricare)

Che cos'è un SignalR hub

L'API SignalR Hubs consente di chiamare i metodi sui client connessi dal server. Nel codice del server si definiscono i metodi chiamati dal client. Nel codice client si definiscono i metodi chiamati dal server. SignalR si occupa di tutto ciò che è dietro le quinte che rende possibili comunicazioni 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 funzionalità a un'app ASP.NET Core, configurare SignalR le route chiamando MapHub nel Startup.Configure callback del UseEndpoints metodo:

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

Creare e usare hub

Creare un hub dichiarando una classe che eredita da Hube aggiungervi 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 complessi e matrici nei parametri e nei valori restituiti.

Nota

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.
  • Usare await quando si chiamano metodi asincroni che dipendono dall'hub che rimangono attivi. Ad esempio, un metodo come Clients.All.SendAsync(...) può avere esito negativo se viene chiamato senza await e il metodo hub viene completato prima SendAsync del completamento.

Oggetto Context

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

Proprietà Descrizione
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Per ogni connessione è presente un ID connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa l'oggetto ClaimTypes.NameIdentifier dell'oggetto ClaimsPrincipal associato alla connessione come identificatore utente.
User Ottiene l'oggetto 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 CancellationToken oggetto che notifica quando la connessione viene interrotta.

Hub.Context contiene anche i metodi seguenti:

Metodo Descrizione
GetHttpContext Restituisce l'oggetto HttpContext per la connessione o 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:

Proprietà Descrizione
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:

Metodo Descrizione
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 viene aperto agli errori di runtime se il nome del metodo non è corretto o risulta mancante dal client.

Un'alternativa all'uso SendAsync consiste nel digitare fortemente con HubHub<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 Hub<IChatClient> di abilita il controllo in fase di compilazione dei metodi client. In questo modo si evitano i 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 realtà, ognuno di questi metodi deve restituire un oggetto Task. Poiché si tratta di un'interfaccia, non usare la async parola chiave . Ad esempio:

public interface IClient
{
    Task ClientMethod();
}

Nota

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 viene 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.

Avviso

Avviso di sicurezza: ConnectionId l'esposizione può causare la rappresentazione dannosa se il server o la SignalR versione client è ASP.NET Core 2.2 o versioni 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 javaScript Promise. Quando il client riceve un errore con un gestore collegato 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. Ad esempio:

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

Le eccezioni impreviste spesso contengono informazioni riservate, ad esempio il nome di un server di database in un'eccezione attivata quando la connessione del database ha esito negativo. 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 si ha una condizione eccezionale che si vuole propagare al client, è possibile usare la HubException classe . Se si genera un HubException oggetto dal metodo hub, SignalRverrà inviato l'intero messaggio al client, non modificato:

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

Nota

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 codice di esempio(come scaricare)

Che cos'è un SignalR hub

L'API SignalR Hubs consente di chiamare i metodi nei client connessi dal server. Nel codice del server si definiscono i metodi chiamati dal client. Nel codice client si definiscono i metodi chiamati dal server. SignalR si occupa di tutto ciò che è dietro le quinte che rende possibili comunicazioni client-to-server e server-to-client.

Configurare SignalR hub

Il SignalR middleware richiede alcuni servizi, configurati chiamando AddSignalR:

services.AddSignalR();

Quando si aggiungono SignalR funzionalità a un'app ASP.NET Core, configurare SignalR le route chiamando UseSignalR nel Startup.Configure metodo:

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

Creare e usare hub

Creare un hub dichiarando una classe che eredita da Hube aggiungere metodi pubblici. I client possono chiamare i 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 si farebbe in qualsiasi metodo C#. SignalR gestisce la serializzazione e la deserializzazione di oggetti complessi e matrici nei parametri e nei valori restituiti.

Nota

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.
  • Usare await quando si chiamano metodi asincroni che dipendono dall'hub che rimangono attivi. Ad esempio, un metodo come Clients.All.SendAsync(...) può non riuscire se viene chiamato senza await e il metodo hub viene completato prima SendAsync del completamento.

Oggetto Context

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

Proprietà Descrizione
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Per ogni connessione è presente un ID di connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa l'oggetto ClaimTypes.NameIdentifierClaimsPrincipal da associato alla connessione come identificatore utente.
User Ottiene l'oggetto ClaimsPrincipal associato all'utente corrente.
Items Ottiene una raccolta chiave/valore che può essere usata per condividere i dati nell'ambito di questa connessione. I dati possono essere archiviati in questa raccolta e verranno mantenuti per la connessione tra chiamate al metodo hub diverse.
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:

Metodo Descrizione
GetHttpContext Restituisce l'oggetto HttpContext per la connessione o null se la connessione non è associata a una richiesta HTTP. Per le connessioni HTTP, è possibile usare questo metodo per ottenere informazioni come intestazioni HTTP e stringhe di query.
Abort Interrompe la connessione.

Oggetto Client

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

Proprietà Descrizione
All Chiama un metodo in tutti i client connessi
Caller Chiama un metodo nel 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:

Metodo Descrizione
AllExcept Chiama un metodo in tutti i client connessi, ad eccezione delle connessioni specificate
Client Chiama un metodo in un client connesso specifico
Clients Chiama un metodo in 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, usare 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 magica per specificare il metodo client da chiamare. Ciò lascia aperto il codice agli errori di runtime se il nome del metodo viene eliminato o mancante dal client.

Un'alternativa all'uso SendAsync consiste nel digitare fortemente l'oggetto 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 eseguire 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 Hub<IChatClient> abilita il controllo in fase di compilazione dei metodi client. Ciò impedisce i problemi causati dall'uso di stringhe magic, poiché Hub<T> può fornire solo l'accesso ai metodi definiti nell'interfaccia.

L'uso di un tipo fortemente digitato 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 Taskoggetto . Poiché è un'interfaccia, non usare la async parola chiave. Ad esempio:

public interface IClient
{
    Task ClientMethod();
}

Nota

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, un nome del 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 Hub fornisce i metodi virtuali e OnDisconnectedAsync per gestire e tenere traccia delle OnConnectedAsync 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 viene 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.

Avviso

Avviso di sicurezza: ConnectionId l'esposizione può causare la rappresentazione dannosa se il server o la SignalR versione client è ASP.NET Core 2.2 o versioni 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 javaScript Promise. Quando il client riceve un errore con un gestore collegato 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. Ad esempio:

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

Le eccezioni impreviste spesso contengono informazioni riservate, ad esempio il nome di un server di database in un'eccezione attivata quando la connessione del database ha esito negativo. 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 si ha una condizione eccezionale che si vuole propagare al client, è possibile usare la HubException classe . Se si genera un HubException oggetto dal metodo hub, SignalRverrà inviato l'intero messaggio al client, non modificato:

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

Nota

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 i metodi nel server. Il server definisce i metodi chiamati dal client e il client definisce i metodi chiamati dal server. SignalR si occupa di tutto ciò che è necessario per rendere possibile la comunicazione da client a server in tempo reale e da server a client.

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 MapHubanche in Program.cs:

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

app.Run();

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

Nota

Gli hub sono temporanei:

  • Non archiviare lo stato in una proprietà della classe hub. Ogni chiamata al metodo hub viene eseguita in una nuova istanza dell'hub.
  • Usare await quando si chiamano metodi asincroni che dipendono dall'hub che rimangono attivi. Ad esempio, un metodo come Clients.All.SendAsync(...) può non riuscire se viene chiamato senza await e il metodo hub viene completato prima SendAsync del completamento.

Oggetto Context

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

Proprietà Descrizione
ConnectionId Ottiene l'ID univoco per la connessione, assegnato da SignalR. Per ogni connessione è presente un ID di connessione.
UserIdentifier Ottiene l'identificatore utente. Per impostazione predefinita, SignalR usa l'oggetto ClaimTypes.NameIdentifierClaimsPrincipal da associato alla connessione come identificatore utente.
User Ottiene l'oggetto ClaimsPrincipal associato all'utente corrente.
Items Ottiene una raccolta chiave/valore che può essere usata per condividere i dati nell'ambito di questa connessione. I dati possono essere archiviati in questa raccolta e verranno mantenuti per la connessione tra chiamate al metodo hub diverse.
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:

Metodo Descrizione
GetHttpContext Restituisce l'oggetto HttpContext per la connessione o 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 Client

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

Proprietà Descrizione
All Chiama un metodo in tutti i client connessi
Caller Chiama un metodo nel 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:

Metodo Descrizione
AllExcept Chiama un metodo in tutti i client connessi, ad eccezione delle connessioni specificate
Client Chiama un metodo in un client connesso specifico
Clients Chiama un metodo in 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 per chiamare e tutti i parametri.

L'oggetto restituito dai Client metodi e Caller contiene anche un InvokeAsync metodo, che può essere usato 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 consiste nel fatto che si basa su una stringa per specificare il metodo client da chiamare. In questo modo il codice viene aperto agli errori di runtime se il nome del metodo non è corretto o risulta mancante dal 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 essere fortemente tipizzata:

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 Hub<IChatClient> di abilita il controllo in fase di compilazione dei metodi client. Ciò impedisce i problemi causati dall'uso di stringhe, perché 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.

Nota

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 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 dall'inserimento delle dipendenze come parametri, che possono essere archiviati nelle proprietà della classe da usare in un metodo hub.

Quando si inseriscono più servizi per metodi hub diversi o come modo alternativo di scrivere codice, i metodi hub possono anche accettare servizi dall'inserimento delle dipendenze. Per impostazione predefinita, i parametri del metodo hub vengono controllati 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 [FromServices] o un attributo personalizzato che implementa IFromServiceMetadata nei parametri del metodo hub che devono essere risolti dall'inserimento 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);
    }
}

Nota

Questa funzionalità usa IServiceProviderIsService, che è facoltativamente implementato dalle implementazioni di inserimento delle dipendenze. Se il contenitore di inserimento 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 è 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.

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 javaScript Promise. I client possono collegare un catch gestore alla promessa restituita o usare/catchtrycon 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, SignalR restituisce 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, senza modifiche:

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

Nota

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

Risorse aggiuntive