ASP.NET Guida api hub SignalR - Server (SignalR 1.x)
di Patrick Fletcher, Tom Dykstra
Avviso
Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.
Questo documento fornisce un'introduzione alla programmazione del lato server dell'API Hub SignalR ASP.NET per SignalR versione 1.1, con esempi di codice che illustrano le opzioni comuni.
L'API SignalR Hubs consente di effettuare chiamate di procedura remota da un server ai client connessi e dai client al server. Nel codice del server si definiscono i metodi che possono essere chiamati dai client e si chiamano i metodi eseguiti nel client. Nel codice client si definiscono i metodi che possono essere chiamati dal server e si chiamano i metodi eseguiti nel server. SignalR si occupa automaticamente di tutte le operazioni di idraulica da client a server.
SignalR offre anche un'API di livello inferiore denominata Connessioni persistenti. Per un'introduzione a SignalR, Hub e Connessioni permanenti o per un'esercitazione che illustra come compilare un'applicazione SignalR completa, vedere SignalR - Introduzione.
Panoramica
Questo documento contiene le seguenti sezioni:
Come registrare la route SignalR e configurare le opzioni di SignalR
Come definire i metodi nella classe Hub che i client possono chiamare
Come gestire gli eventi di durata della connessione nella classe Hub
Come ottenere informazioni sul client dalla proprietà Context
Come chiamare i metodi client e gestire i gruppi dall'esterno della classe Hub
Per la documentazione su come programmare i client, vedere le risorse seguenti:
I collegamenti agli argomenti di riferimento sulle API sono alla versione .NET 4.5 dell'API. Se si usa .NET 4, vedere la versione .NET 4 degli argomenti dell'API.
Come registrare la route SignalR e configurare le opzioni di SignalR
Per definire la route che i client useranno per connettersi all'hub, chiamare il metodo MapHubs all'avvio dell'applicazione. MapHubs
è un metodo di estensione per la System.Web.Routing.RouteCollection
classe . L'esempio seguente illustra come definire la route di Hub SignalR nel file Global.asax .
using System.Web.Routing;
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs();
}
}
Se si aggiunge la funzionalità SignalR a un'applicazione MVC ASP.NET, assicurarsi che la route SignalR venga aggiunta prima delle altre route. Per altre informazioni, vedere Esercitazione: Introduzione con SignalR e MVC 4.
The /signalr URL
Per impostazione predefinita, l'URL di route che i client useranno per connettersi all'hub è "/signalr". (Non confondere questo URL con l'URL "/signalr/hubs", che è per il file JavaScript generato automaticamente. Per altre informazioni sul proxy generato, vedere Guida all'API di SignalR Hubs - Client JavaScript - Proxy generato e cosa fa per l'utente.
Potrebbero esserci circostanze straordinarie che rendono questo URL di base non utilizzabile per SignalR; Ad esempio, si dispone di una cartella nel progetto denominato signalr e non si vuole modificare il nome. In questo caso, è possibile modificare l'URL di base, come illustrato negli esempi seguenti (sostituire "/signalr" nel codice di esempio con l'URL desiderato).
Codice server che specifica l'URL
RouteTable.Routes.MapHubs("/signalr", new HubConfiguration());
Codice client JavaScript che specifica l'URL (con il proxy generato)
$.connection.hub.url = "/signalr"
$.connection.hub.start().done(init);
Codice client JavaScript che specifica l'URL (senza il proxy generato)
var connection = $.hubConnection("/signalr", { useDefaultPath: false });
Codice client .NET che specifica l'URL
var hubConnection = new HubConnection("http://contoso.com/signalr", useDefaultUrl: false);
Configurazione delle opzioni di SignalR
Gli overload del MapHubs
metodo consentono di specificare un URL personalizzato, un resolver di dipendenze personalizzato e le opzioni seguenti:
Abilitare le chiamate tra domini dai client browser.
In genere, se il browser carica una pagina da
http://contoso.com
, la connessione SignalR si trova nello stesso dominio, inhttp://contoso.com/signalr
. Se la pagina dahttp://contoso.com
stabilisce una connessione ahttp://fabrikam.com/signalr
, si tratta di una connessione tra domini. Per motivi di sicurezza, le connessioni tra domini sono disabilitate per impostazione predefinita. Per altre informazioni, vedere ASP.NET Guida api di Hub SignalR - Client JavaScript - Come stabilire una connessione tra domini.Abilitare messaggi di errore dettagliati.
Quando si verificano errori, il comportamento predefinito di SignalR consiste nell'inviare ai client un messaggio di notifica senza dettagli su ciò che è accaduto. L'invio di informazioni dettagliate sugli errori ai client non è consigliato nell'ambiente di produzione, perché gli utenti malintenzionati potrebbero essere in grado di usare le informazioni negli attacchi contro l'applicazione. Per la risoluzione dei problemi, è possibile usare questa opzione per abilitare temporaneamente la segnalazione errori più informativa.
Disabilitare i file proxy JavaScript generati automaticamente.
Per impostazione predefinita, viene generato un file JavaScript con proxy per le classi hub in risposta all'URL "/signalr/hubs". Se non si vogliono usare i proxy JavaScript o se si vuole generare questo file manualmente e fare riferimento a un file fisico nei client, è possibile usare questa opzione per disabilitare la generazione del proxy. Per altre informazioni, vedere SignalR Hubs API Guide - JavaScript Client - How to create a physical file for the SignalR generated proxy .For more information, see SignalR Hubs API Guide - JavaScript Client - How to create a physical file for the SignalR generated proxy.For more information, see SignalR Hubs API Guide - JavaScript Client - How to create a physical file for the SignalR generated proxy.
Nell'esempio seguente viene illustrato come specificare l'URL di connessione SignalR e queste opzioni in una chiamata al MapHubs
metodo . Per specificare un URL personalizzato, sostituire "/signalr" nell'esempio con l'URL che si vuole usare.
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableCrossDomain = true;
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);
Come creare e usare classi hub
Per creare un hub, creare una classe che deriva da Microsoft.Aspnet.Signalr.Hub. L'esempio seguente illustra una semplice classe Hub per un'applicazione di chat.
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
In questo esempio un client connesso può chiamare il NewContosoChatMessage
metodo e, quando lo fa, i dati ricevuti vengono trasmessi a tutti i client connessi.
Durata dell'oggetto hub
Non si crea un'istanza della classe Hub o si chiamano i relativi metodi dal proprio codice nel server; tutto ciò viene eseguito dall'utente dalla pipeline di Hub SignalR. SignalR crea una nuova istanza della classe Hub ogni volta che deve gestire un'operazione hub, ad esempio quando un client si connette, si disconnette o effettua una chiamata al metodo al server.
Poiché le istanze della classe Hub sono temporanee, non è possibile usarle per mantenere lo stato da una chiamata di metodo alla successiva. Ogni volta che il server riceve una chiamata al metodo da un client, una nuova istanza della classe Hub elabora il messaggio. Per mantenere lo stato tramite più connessioni e chiamate al metodo, usare un altro metodo, ad esempio un database, una variabile statica nella classe Hub o una classe diversa che non deriva da Hub
. Se si salvano in modo permanente i dati in memoria, usando un metodo come una variabile statica nella classe Hub, i dati andranno persi quando il dominio dell'app viene riciclato.
Se si vogliono inviare messaggi ai client dal codice che viene eseguito all'esterno della classe Hub, non è possibile crearne un'istanza creando un'istanza della classe Hub, ma è possibile farlo ottenendo un riferimento all'oggetto contesto SignalR per la classe Hub. Per altre informazioni, vedere Come chiamare i metodi client e gestire i gruppi dall'esterno della classe Hub più avanti in questo argomento.
Maiuscole e minuscole dei nomi hub nei client JavaScript
Per impostazione predefinita, i client JavaScript fanno riferimento a Hub usando una versione camel del nome della classe. SignalR apporta automaticamente questa modifica in modo che il codice JavaScript possa essere conforme alle convenzioni JavaScript. L'esempio precedente viene definito come contosoChatHub
nel codice JavaScript.
Server
public class ContosoChatHub : Hub
Client JavaScript con proxy generato
var contosoChatHubProxy = $.connection.contosoChatHub;
Se si vuole specificare un nome diverso per i client da usare, aggiungere l'attributo HubName
. Quando si usa un HubName
attributo, non viene apportata alcuna modifica del nome al case camel nei client JavaScript.
Server
[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub
Client JavaScript con proxy generato
var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;
Più hub
È possibile definire più classi hub in un'applicazione. Quando si esegue questa operazione, la connessione viene condivisa, ma i gruppi sono separati:
Tutti i client useranno lo stesso URL per stabilire una connessione SignalR con il servizio ("/signalr" o l'URL personalizzato, se specificato) e tale connessione viene usata per tutti gli hub definiti dal servizio.
Non esiste alcuna differenza di prestazioni per più hub rispetto alla definizione di tutte le funzionalità dell'hub in una singola classe.
Tutti gli hub ottengono le stesse informazioni di richiesta HTTP.
Poiché tutti gli hub condividono la stessa connessione, le uniche informazioni sulla richiesta HTTP che il server ottiene è ciò che viene fornito nella richiesta HTTP originale che stabilisce la connessione SignalR. Se si usa la richiesta di connessione per passare informazioni dal client al server specificando una stringa di query, non è possibile fornire stringhe di query diverse a hub diversi. Tutti gli hub riceveranno le stesse informazioni.
Il file proxy JavaScript generato conterrà proxy per tutti gli hub in un unico file.
Per informazioni sui proxy JavaScript, vedere Guida all'API di SignalR Hubs - Client JavaScript - Proxy generato e operazioni da eseguire automaticamente.
I gruppi vengono definiti all'interno di Hub.
In SignalR è possibile definire gruppi denominati da trasmettere a subset di client connessi. I gruppi vengono mantenuti separatamente per ogni hub. Ad esempio, un gruppo denominato "Administrators" include un set di client per la
ContosoChatHub
classe e lo stesso nome di gruppo fa riferimento a un set diverso di client per laStockTickerHub
classe.
Come definire i metodi nella classe Hub che i client possono chiamare
Per esporre un metodo nell'hub che si vuole chiamare dal client, dichiarare un metodo pubblico, come illustrato negli esempi seguenti.
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
public class StockTickerHub : Hub
{
public IEnumerable<Stock> GetAllStocks()
{
return _stockTicker.GetAllStocks();
}
}
È possibile specificare un tipo restituito e parametri, inclusi tipi complessi e matrici, come in qualsiasi metodo C#. Tutti i dati ricevuti nei parametri o restituiti al chiamante vengono comunicati tra il client e il server tramite JSON e SignalR gestisce automaticamente l'associazione di oggetti complessi e matrici di oggetti.
Maiuscole e minuscole dei nomi dei metodi nei client JavaScript
Per impostazione predefinita, i client JavaScript fanno riferimento ai metodi hub usando una versione con maiuscole e minuscole camel del nome del metodo. SignalR apporta automaticamente questa modifica in modo che il codice JavaScript possa essere conforme alle convenzioni JavaScript.
Server
public void NewContosoChatMessage(string userName, string message)
Client JavaScript con proxy generato
contosoChatHubProxy.server.newContosoChatMessage($(userName, message);
Se si vuole specificare un nome diverso per i client da usare, aggiungere l'attributo HubMethodName
.
Server
[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)
Client JavaScript con proxy generato
contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);
Quando eseguire in modo asincrono
Se il metodo sarà a esecuzione prolungata o deve eseguire operazioni che comportano l'attesa, ad esempio una ricerca di database o una chiamata al servizio Web, rendere il metodo Hub asincrono restituendo un oggetto Task (al posto di void
return) o Task<T> (al posto del T
tipo restituito). Quando si restituisce un Task
oggetto dal metodo, SignalR attende il completamento dell'oggetto Task
e quindi invia di nuovo il risultato non compresso al client, quindi non esiste alcuna differenza nel modo in cui si codifica la chiamata al metodo nel client.
La creazione di un metodo Hub consente di evitare di bloccare la connessione quando usa il trasporto WebSocket. Quando un metodo Hub viene eseguito in modo sincrono e il trasporto è WebSocket, le chiamate successive dei metodi nell'hub dallo stesso client vengono bloccate fino al completamento del metodo Hub.
L'esempio seguente mostra lo stesso metodo codificato per l'esecuzione in modo sincrono o asincrono, seguito dal codice client JavaScript che funziona per chiamare una delle due versioni.
Sincrono
public IEnumerable<Stock> GetAllStocks()
{
// Returns data from memory.
return _stockTicker.GetAllStocks();
}
Asincrono - ASP.NET 4.5
public async Task<IEnumerable<Stock>> GetAllStocks()
{
// Returns data from a web service.
var uri = Util.getServiceUri("Stocks");
using (HttpClient httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(uri);
return (await response.Content.ReadAsAsync<IEnumerable<Stock>>());
}
}
Client JavaScript con proxy generato
stockTickerHubProxy.server.getAllStocks().done(function (stocks) {
$.each(stocks, function () {
alert(this.Symbol + ' ' + this.Price);
});
});
Per altre informazioni su come usare metodi asincroni in ASP.NET 4.5, vedere Uso di metodi asincroni in ASP.NET MVC 4.
Definizione di overload
Se si desidera definire gli overload per un metodo, il numero di parametri in ogni overload deve essere diverso. Se si differenzia un overload specificando solo tipi di parametro diversi, la classe Hub verrà compilata, ma il servizio SignalR genererà un'eccezione in fase di esecuzione quando i client tentano di chiamare uno degli overload.
Come chiamare i metodi client dalla classe Hub
Per chiamare i metodi client dal server, usare la Clients
proprietà in un metodo nella classe Hub. L'esempio seguente mostra il codice server che chiama addNewMessageToPage
su tutti i client connessi e il codice client che definisce il metodo in un client JavaScript.
Server
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
Client JavaScript con proxy generato
contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '<li>');
};
Non è possibile ottenere un valore restituito da un metodo client; sintassi, ad int x = Clients.All.add(1,1)
esempio, non funziona.
È possibile specificare tipi e matrici complessi per i parametri. Nell'esempio seguente viene passato un tipo complesso al client in un parametro del metodo.
Codice server che chiama un metodo client usando un oggetto complesso
public void SendMessage(string name, string message)
{
Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}
Codice server che definisce l'oggetto complesso
public class ContosoChatMessage
{
public string UserName { get; set; }
public string Message { get; set; }
}
Client JavaScript con proxy generato
var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
console.log(message.UserName + ' ' + message.Message);
});
Selezione dei client che riceveranno rpc
La proprietà Clients restituisce un oggetto HubConnectionContext che fornisce diverse opzioni per specificare quali client riceveranno rpc:
Tutti i client connessi.
Clients.All.addContosoChatMessageToPage(name, message);
Solo il client chiamante.
Clients.Caller.addContosoChatMessageToPage(name, message);
Tutti i client ad eccezione del client chiamante.
Clients.Others.addContosoChatMessageToPage(name, message);
Un client specifico identificato dall'ID connessione.
Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
Questo esempio chiama
addContosoChatMessageToPage
il client chiamante e ha lo stesso effetto dell'usoClients.Caller
di .Tutti i client connessi, ad eccezione dei client specificati, identificati dall'ID connessione.
Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
Tutti i client connessi in un gruppo specificato.
Clients.Group(groupName).addContosoChatMessageToPage(name, message);
Tutti i client connessi in un gruppo specificato, ad eccezione dei client specificati, identificati dall'ID connessione.
Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
Tutti i client connessi in un gruppo specificato, ad eccezione del client chiamante.
Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);
Nessuna convalida in fase di compilazione per i nomi dei metodi
Il nome del metodo specificato viene interpretato come oggetto dinamico, il che significa che non è presente alcuna convalida in fase di compilazione o in fase di compilazione. L'espressione viene valutata in fase di esecuzione. Quando viene eseguita la chiamata al metodo, SignalR invia il nome del metodo e i valori dei parametri al client e, se il client ha un metodo che corrisponde al nome, tale metodo viene chiamato e i valori dei parametri vengono passati. Se nel client non viene trovato alcun metodo corrispondente, non viene generato alcun errore. Per informazioni sul formato dei dati trasmessi da SignalR al client in background quando si chiama un metodo client, vedere Introduzione a SignalR.
Corrispondenza del nome del metodo senza distinzione tra maiuscole e minuscole
La corrispondenza dei nomi dei metodi non fa distinzione tra maiuscole e minuscole. Ad esempio, Clients.All.addContosoChatMessageToPage
nel server verrà eseguito AddContosoChatMessageToPage
, addcontosochatmessagetopage
o addContosoChatMessageToPage
nel client.
Esecuzione asincrona
Il metodo chiamato viene eseguito in modo asincrono. Qualsiasi codice riportato dopo una chiamata al metodo a un client verrà eseguito immediatamente senza attendere che SignalR finisca di trasmettere i dati ai client, a meno che non si specifichi che le righe di codice successive devono attendere il completamento del metodo. Gli esempi di codice seguenti illustrano come eseguire due metodi client in sequenza, uno usando il codice che funziona in .NET 4.5 e uno usando il codice che funziona in .NET 4.
Esempio di .NET 4.5
async public Task NewContosoChatMessage(string name, string message)
{
await Clients.Others.addContosoChatMessageToPage(data);
Clients.Caller.notifyMessageSent();
}
Esempio di .NET 4
public void NewContosoChatMessage(string name, string message)
{
(Clients.Others.addContosoChatMessageToPage(data) as Task).ContinueWith(antecedent =>
Clients.Caller.notifyMessageSent());
}
Se si usa await
o ContinueWith
per attendere il completamento di un metodo client prima dell'esecuzione della riga di codice successiva, ciò non significa che i client riceveranno effettivamente il messaggio prima dell'esecuzione della riga di codice successiva. "Completamento" di una chiamata al metodo client significa solo che SignalR ha eseguito tutto il necessario per inviare il messaggio. Se è necessaria la verifica che i client abbiano ricevuto il messaggio, è necessario programmare manualmente tale meccanismo. Ad esempio, è possibile scrivere il codice di un MessageReceived
metodo nell'hub e nel addContosoChatMessageToPage
metodo nel client che è possibile chiamare MessageReceived
dopo aver eseguito qualsiasi operazione da eseguire nel client. Nell'hub MessageReceived
è possibile eseguire qualsiasi operazione dipende dalla ricezione effettiva del client e dall'elaborazione della chiamata al metodo originale.
Come usare una variabile stringa come nome del metodo
Per richiamare un metodo client usando una variabile stringa come nome del metodo, eseguire il cast Clients.All
(o Clients.Others
, Clients.Caller
e così via) a IClientProxy
e quindi chiamare Invoke(methodName, args...).
public void NewContosoChatMessage(string name, string message)
{
string methodToCall = "addContosoChatMessageToPage";
IClientProxy proxy = Clients.All;
proxy.Invoke(methodToCall, name, message);
}
Come gestire l'appartenenza ai gruppi dalla classe Hub
I gruppi in SignalR forniscono un metodo per la trasmissione di messaggi ai subset specificati di client connessi. Un gruppo può avere un numero qualsiasi di client e un client può essere membro di un numero qualsiasi di gruppi.
Per gestire l'appartenenza ai gruppi, usare i metodi Add e Remove forniti dalla Groups
proprietà della classe Hub. L'esempio seguente illustra i Groups.Add
metodi e Groups.Remove
usati nei metodi Hub chiamati dal codice client, seguiti dal codice client JavaScript che li chiama.
Server
public class ContosoChatHub : Hub
{
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
}
Client JavaScript con proxy generato
contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);
Non è necessario creare in modo esplicito i gruppi. In effetti, un gruppo viene creato automaticamente la prima volta che si specifica il nome in una chiamata a Groups.Add
e viene eliminato quando si rimuove l'ultima connessione dall'appartenenza.
Non esiste alcuna API per ottenere un elenco di appartenenze a gruppi o un elenco di gruppi. SignalR invia messaggi a client e gruppi basati su un modello pub/sub e il server non gestisce elenchi di gruppi o appartenenze a gruppi. Ciò consente di ottimizzare la scalabilità, perché ogni volta che si aggiunge un nodo a una Web farm, qualsiasi stato gestito da SignalR deve essere propagato al nuovo nodo.
Esecuzione asincrona dei metodi Add e Remove
I Groups.Add
metodi e Groups.Remove
vengono eseguiti in modo asincrono. Se si vuole aggiungere un client a un gruppo e inviare immediatamente un messaggio al client usando il gruppo, è necessario assicurarsi che il Groups.Add
metodo venga completato per primo. Gli esempi di codice seguenti illustrano come eseguire questa operazione, una usando il codice che funziona in .NET 4.5 e una usando il codice che funziona in .NET 4
Esempio di .NET 4.5
async public Task JoinGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
}
Esempio di .NET 4
public void JoinGroup(string groupName)
{
(Groups.Add(Context.ConnectionId, groupName) as Task).ContinueWith(antecedent =>
Clients.Group(groupName).addContosoChatMessageToPage(Context.ConnectionId + " added to group"));
}
Persistenza dell'appartenenza a gruppi
SignalR tiene traccia delle connessioni, non degli utenti, quindi se si vuole che un utente si trovi nello stesso gruppo ogni volta che l'utente stabilisce una connessione, è necessario chiamare Groups.Add
ogni volta che l'utente stabilisce una nuova connessione.
Dopo una perdita temporanea di connettività, a volte SignalR può ripristinare automaticamente la connessione. In tal caso, SignalR ripristina la stessa connessione, non stabilisce una nuova connessione e quindi viene ripristinata automaticamente l'appartenenza al gruppo del client. Ciò è possibile anche quando l'interruzione temporanea è il risultato di un riavvio o di un errore del server, perché lo stato della connessione per ogni client, incluse le appartenenze ai gruppi, viene eseguito il round-tripped al client. Se un server si arresta e viene sostituito da un nuovo server prima del timeout della connessione, un client può riconnettersi automaticamente al nuovo server e registrarlo nuovamente nei gruppi di cui è membro.
Quando una connessione non può essere ripristinata automaticamente dopo una perdita di connettività o quando si verifica il timeout della connessione o quando il client si disconnette (ad esempio, quando un browser passa a una nuova pagina), le appartenenze ai gruppi vengono perse. La volta successiva che l'utente si connette sarà una nuova connessione. Per mantenere le appartenenze ai gruppi quando lo stesso utente stabilisce una nuova connessione, l'applicazione deve tenere traccia delle associazioni tra utenti e gruppi e ripristinare le appartenenze ai gruppi ogni volta che un utente stabilisce una nuova connessione.
Per altre informazioni sulle connessioni e le riconnessioni, vedere Come gestire gli eventi di durata della connessione nella classe Hub più avanti in questo argomento.
Gruppi di utenti singoli
Le applicazioni che usano SignalR in genere devono tenere traccia delle associazioni tra utenti e connessioni per sapere quale utente ha inviato un messaggio e quali utenti devono ricevere un messaggio. I gruppi vengono usati in uno dei due modelli comunemente usati per eseguire questa operazione.
Gruppi di utenti singoli.
È possibile specificare il nome utente come nome del gruppo e aggiungere l'ID connessione corrente al gruppo ogni volta che l'utente si connette o si riconnette. Per inviare messaggi all'utente inviato al gruppo. Uno svantaggio di questo metodo è che il gruppo non fornisce un modo per scoprire se l'utente è online o offline.
Tenere traccia delle associazioni tra i nomi utente e gli ID di connessione.
È possibile archiviare un'associazione tra ogni nome utente e uno o più ID di connessione in un dizionario o un database e aggiornare i dati archiviati ogni volta che l'utente si connette o si disconnette. Per inviare messaggi all'utente, specificare gli ID di connessione. Uno svantaggio di questo metodo è che richiede più memoria.
Come gestire gli eventi di durata della connessione nella classe Hub
I motivi tipici per la gestione degli eventi di durata della connessione sono tenere traccia del fatto che un utente sia connesso o meno e tenere traccia dell'associazione tra i nomi utente e gli ID connessione. Per eseguire codice personalizzato quando i client si connettono o si disconnettono, eseguire l'override dei OnConnected
metodi virtuali , OnDisconnected
e OnReconnected
della classe Hub, come illustrato nell'esempio seguente.
public class ContosoChatHub : Hub
{
public override Task OnConnected()
{
// Add your own code here.
// For example: in a chat application, record the association between
// the current connection ID and user name, and mark the user as online.
// After the code in this method completes, the client is informed that
// the connection is established; for example, in a JavaScript client,
// the start().done callback is executed.
return base.OnConnected();
}
public override Task OnDisconnected()
{
// Add your own code here.
// For example: in a chat application, mark the user as offline,
// delete the association between the current connection id and user name.
return base.OnDisconnected();
}
public override Task OnReconnected()
{
// Add your own code here.
// For example: in a chat application, you might have marked the
// user as offline after a period of inactivity; in that case
// mark the user as online again.
return base.OnReconnected();
}
}
Quando OnConnected, OnDisconnected e OnReconnected vengono chiamati
Ogni volta che un browser passa a una nuova pagina, deve essere stabilita una nuova connessione, il che significa che SignalR eseguirà il OnDisconnected
metodo seguito dal OnConnected
metodo . SignalR crea sempre un nuovo ID di connessione quando viene stabilita una nuova connessione.
Il OnReconnected
metodo viene chiamato quando si è verificata un'interruzione temporanea della connettività da cui SignalR può eseguire automaticamente il ripristino, ad esempio quando un cavo viene temporaneamente disconnesso e riconnesso prima del timeout della connessione. Il OnDisconnected
metodo viene chiamato quando il client viene disconnesso e SignalR non può riconnettersi automaticamente, ad esempio quando un browser passa a una nuova pagina. Pertanto, una possibile sequenza di eventi per un determinato client è OnConnected
, , OnReconnected
OnDisconnected
; o . OnDisconnected
OnConnected
La sequenza OnConnected
, OnDisconnected
, OnReconnected
non verrà visualizzata per una determinata connessione.
Il OnDisconnected
metodo non viene chiamato in alcuni scenari, ad esempio quando un server si arresta o il dominio app viene riciclato. Quando un altro server viene attivato in linea o il dominio app completa il riciclo, alcuni client potrebbero essere in grado di riconnettersi e generare l'evento OnReconnected
.
Per altre informazioni, vedere Understanding and Handling Connection Lifetime Events in SignalR.For more information, see Understanding and Handling Connection Lifetime Events in SignalR.
Stato del chiamante non popolato
I metodi del gestore eventi della durata della connessione vengono chiamati dal server, il che significa che qualsiasi stato inserito nell'oggetto state
nel client non verrà popolato nella Caller
proprietà nel server. Per informazioni sull'oggetto state
e sulla Caller
proprietà, vedere Come passare lo stato tra i client e la classe Hub più avanti in questo argomento.
Come ottenere informazioni sul client dalla proprietà Context
Per ottenere informazioni sul client, usare la Context
proprietà della classe Hub. La Context
proprietà restituisce un oggetto HubCallerContext che fornisce l'accesso alle informazioni seguenti:
ID connessione del client chiamante.
string connectionID = Context.ConnectionId;
L'ID connessione è un GUID assegnato da SignalR (non è possibile specificare il valore nel proprio codice). Esiste un ID di connessione per ogni connessione e lo stesso ID di connessione viene usato da tutti gli hub se nell'applicazione sono presenti più hub.
Dati di intestazione HTTP.
System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
È anche possibile ottenere intestazioni HTTP da
Context.Headers
. Il motivo di più riferimenti alla stessa cosa è cheContext.Headers
è stato creato per primo, laContext.Request
proprietà è stata aggiunta in un secondo momento edContext.Headers
è stata mantenuta per la compatibilità con le versioni precedenti.Eseguire query sui dati della stringa.
System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString; string parameterValue = queryString["parametername"]
È anche possibile ottenere dati di stringa di query da
Context.QueryString
.La stringa di query che si ottiene in questa proprietà è quella usata con la richiesta HTTP che ha stabilito la connessione SignalR. È possibile aggiungere parametri di stringa di query nel client configurando la connessione, che è un modo pratico per passare i dati sul client dal client al server. Nell'esempio seguente viene illustrato un modo per aggiungere una stringa di query in un client JavaScript quando si usa il proxy generato.
$.connection.hub.qs = { "version" : "1.0" };
Per altre informazioni sull'impostazione dei parametri della stringa di query, vedere le guide API per i client JavaScript e .NET .
È possibile trovare il metodo di trasporto usato per la connessione nei dati della stringa di query, insieme ad altri valori usati internamente da SignalR:
string transportMethod = queryString["transport"];
Il valore di
transportMethod
sarà "webSockets", "serverSentEvents", "foreverFrame" o "longPolling". Si noti che se si controlla questo valore nel metodo delOnConnected
gestore eventi, in alcuni scenari è possibile ottenere inizialmente un valore di trasporto che non è il metodo di trasporto negoziato finale per la connessione. In tal caso, il metodo genererà un'eccezione e verrà chiamato di nuovo in un secondo momento quando viene stabilito il metodo di trasporto finale.Biscotti.
System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
È anche possibile ottenere i cookie da
Context.RequestCookies
.informazioni utente.
System.Security.Principal.IPrincipal user = Context.User;
Oggetto HttpContext per la richiesta :
System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();
Usare questo metodo invece di ottenere
HttpContext.Current
l'oggettoHttpContext
per la connessione SignalR.
Come passare lo stato tra i client e la classe Hub
Il proxy client fornisce un state
oggetto in cui è possibile archiviare i dati da trasmettere al server con ogni chiamata al metodo. Nel server è possibile accedere a questi dati nella Clients.Caller
proprietà nei metodi hub chiamati dai client. La Clients.Caller
proprietà non viene popolata per i metodi OnConnected
del gestore eventi della durata della connessione , OnDisconnected
e OnReconnected
.
La creazione o l'aggiornamento dei dati nell'oggetto state
e la Clients.Caller
proprietà funziona in entrambe le direzioni. È possibile aggiornare i valori nel server e vengono passati di nuovo al client.
L'esempio seguente mostra il codice client JavaScript che archivia lo stato per la trasmissione al server con ogni chiamata al metodo.
contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";
L'esempio seguente illustra il codice equivalente in un client .NET.
contosoChatHubProxy["userName"] = "Fadi Fakhouri";
chatHubProxy["computerName"] = "fadivm1";
Nella classe Hub è possibile accedere a questi dati nella Clients.Caller
proprietà . Nell'esempio seguente viene illustrato il codice che recupera lo stato a cui fa riferimento nell'esempio precedente.
public void NewContosoChatMessage(string data)
{
string userName = Clients.Caller.userName;
string computerName = Clients.Caller.computerName;
Clients.Others.addContosoChatMessageToPage(message, userName, computerName);
}
Nota
Questo meccanismo per la persistenza dello stato non è destinato a grandi quantità di dati, poiché tutti gli elementi inseriti nella state
proprietà o Clients.Caller
vengono arrotondati con ogni chiamata al metodo. È utile per elementi più piccoli, ad esempio nomi utente o contatori.
Come gestire gli errori nella classe Hub
Per gestire gli errori che si verificano nei metodi della classe Hub, usare uno o entrambi i metodi seguenti:
Eseguire il wrapping del codice del metodo in blocchi try-catch e registrare l'oggetto eccezione. Ai fini del debug è possibile inviare l'eccezione al client, ma per motivi di sicurezza non è consigliabile inviare informazioni dettagliate ai client nell'ambiente di produzione.
Creare un modulo della pipeline hubs che gestisce il metodo OnIncomingError . L'esempio seguente illustra un modulo della pipeline che registra gli errori, seguito dal codice in Global.asax che inserisce il modulo nella pipeline di Hubs.
public class ErrorHandlingPipelineModule : HubPipelineModule { protected override void OnIncomingError(Exception ex, IHubIncomingInvokerContext context) { Debug.WriteLine("=> Exception " + ex.Message); if (ex.InnerException != null) { Debug.WriteLine("=> Inner Exception " + ex.InnerException.Message); } base.OnIncomingError(ex, context); } }
protected void Application_Start(object sender, EventArgs e) { GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); RouteTable.Routes.MapHubs(); }
Per altre informazioni sui moduli della pipeline hub, vedere Come personalizzare la pipeline hub più avanti in questo argomento.
Come abilitare la traccia
Per abilitare la traccia lato server, aggiungere un elemento system.diagnostics al file Web.config, come illustrato in questo esempio:
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit https://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="SignalRSamples" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;" />
<add name="SignalRSamplesWithMARS" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;MultipleActiveResultSets=true;" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
<system.diagnostics>
<sources>
<source name="SignalR.SqlMessageBus">
<listeners>
<add name="SignalR-Bus" />
</listeners>
</source>
<source name="SignalR.ServiceBusMessageBus">
<listeners>
<add name="SignalR-Bus" />
</listeners>
</source>
<source name="SignalR.ScaleoutMessageBus">
<listeners>
<add name="SignalR-Bus" />
</listeners>
</source>
<source name="SignalR.Transports.WebSocketTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.ServerSentEventsTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.ForeverFrameTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.LongPollingTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.TransportHeartBeat">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
</sources>
<switches>
<add name="SignalRSwitch" value="Verbose" />
</switches>
<sharedListeners>
<add name="SignalR-Transports"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="transports.log.txt" />
<add name="SignalR-Bus"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="bus.log.txt" />
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
</configuration>
Quando si esegue l'applicazione in Visual Studio, è possibile visualizzare i log nella finestra Output .
Come chiamare i metodi client e gestire i gruppi dall'esterno della classe Hub
Per chiamare i metodi client da una classe diversa dalla classe Hub, ottenere un riferimento all'oggetto contesto SignalR per l'hub e usarlo per chiamare i metodi nel client o gestire i gruppi.
La classe di esempio StockTicker
seguente ottiene l'oggetto contesto, la archivia in un'istanza della classe , archivia l'istanza della classe in una proprietà statica e usa il contesto dell'istanza della classe singleton per chiamare il updateStockPrice
metodo sui client connessi a un hub denominato StockTickerHub
.
// For the complete example, go to
// http://www.asp.net/signalr/overview/signalr-1x/getting-started/tutorial-server-broadcast-with-aspnet-signalr
// This sample only shows code related to getting and using the SignalR context.
public class StockTicker
{
// Singleton instance
private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>()));
private IHubContext _context;
private StockTicker(IHubContext context)
{
_context = context;
}
// This method is invoked by a Timer object.
private void UpdateStockPrices(object state)
{
foreach (var stock in _stocks.Values)
{
if (TryUpdateStockPrice(stock))
{
_context.Clients.All.updateStockPrice(stock);
}
}
}
Se è necessario usare il contesto più volte in un oggetto di lunga durata, ottenere il riferimento una sola volta e salvarlo invece di recuperarlo di nuovo ogni volta. Ottenere il contesto una volta garantisce che SignalR invii messaggi ai client nella stessa sequenza in cui i metodi hub effettuano chiamate al metodo client. Per un'esercitazione che illustra come usare il contesto SignalR per un hub, vedere Trasmissione del server con ASP.NET SignalR.
Chiamata dei metodi client
È possibile specificare quali client riceveranno RPC, ma sono disponibili meno opzioni rispetto a quando si chiama da una classe Hub. Il motivo è che il contesto non è associato a una chiamata specifica da un client, pertanto tutti i metodi che richiedono conoscenza dell'ID connessione corrente, ad esempio Clients.Others
, o Clients.Caller
o Clients.OthersInGroup
, non sono disponibili. Sono disponibili le opzioni seguenti:
Tutti i client connessi.
context.Clients.All.addContosoChatMessageToPage(name, message);
Un client specifico identificato dall'ID connessione.
context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);
Tutti i client connessi, ad eccezione dei client specificati, identificati dall'ID connessione.
context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
Tutti i client connessi in un gruppo specificato.
context.Clients.Group(groupName).addContosoChatMessageToPage(name, message);
Tutti i client connessi in un gruppo specificato, ad eccezione dei client specificati, identificati dall'ID connessione.
Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
Se si sta chiamando nella classe non hub dai metodi nella classe Hub, è possibile passare l'ID connessione corrente e usarlo con Clients.Client
, o per simulare Clients.Caller
, Clients.Others
o Clients.Group
Clients.OthersInGroup
. Clients.AllExcept
Nell'esempio seguente la MoveShapeHub
classe passa l'ID connessione alla Broadcaster
classe in modo che la Broadcaster
classe possa simulare Clients.Others
.
// For the complete example, see
// http://www.asp.net/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr
// This sample only shows code that passes connection ID to the non-Hub class,
// in order to simulate Clients.Others.
public class MoveShapeHub : Hub
{
// Code not shown puts a singleton instance of Broadcaster in this variable.
private Broadcaster _broadcaster;
public void UpdateModel(ShapeModel clientModel)
{
clientModel.LastUpdatedBy = Context.ConnectionId;
// Update the shape model within our broadcaster
_broadcaster.UpdateShape(clientModel);
}
}
public class Broadcaster
{
public Broadcaster()
{
_hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
}
public void UpdateShape(ShapeModel clientModel)
{
_model = clientModel;
_modelUpdated = true;
}
// Called by a Timer object.
public void BroadcastShape(object state)
{
if (_modelUpdated)
{
_hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
_modelUpdated = false;
}
}
}
Gestione dell'appartenenza a gruppi
Per la gestione dei gruppi sono disponibili le stesse opzioni eseguite in una classe Hub.
Aggiungere un client a un gruppo
context.Groups.Add(connectionID, groupName);
Rimuovere un client da un gruppo
context.Groups.Remove(connectionID, groupName);
Come personalizzare la pipeline hub
SignalR consente di inserire il proprio codice nella pipeline dell'hub. Nell'esempio seguente viene illustrato un modulo della pipeline hub personalizzato che registra ogni chiamata al metodo in ingresso ricevuta dal client e dalla chiamata al metodo in uscita richiamata nel client:
public class LoggingPipelineModule : HubPipelineModule
{
protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context)
{
Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name);
return base.OnBeforeIncoming(context);
}
protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context)
{
Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub);
return base.OnBeforeOutgoing(context);
}
}
Il codice seguente nel file Global.asax registra il modulo da eseguire nella pipeline hub:
protected void Application_Start(object sender, EventArgs e)
{
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
RouteTable.Routes.MapHubs();
}
Esistono molti metodi diversi che è possibile eseguire l'override. Per un elenco completo, vedere Metodi HubPipelineModule.