API SignalRCore

SignalRCore

 

ASP.NET Core SignalR è una libreria open source che semplifica l'aggiunta di funzionalità web in tempo reale alle app. La funzionalità web in tempo reale consente al codice lato server di inviare contenuti ai client istantaneamente.

 

Buoni candidati per SignalR:

 

 

Il componente SignalRCore di sgcWebSockets utilizza WebSocket come trasporto per connettersi a un server SignalRCore; se questo trasporto non è supportato, verrà generato un errore.

 

Hub

SignalRCore utilizza hub per la comunicazione tra client e server. SignalRCore fornisce 2 protocolli hub: il protocollo testuale basato su JSON e il protocollo binario basato su MessagePack. Il componente sgcWebSockets implementa solo il protocollo testuale JSON per comunicare con i server SignalRCore.

Per configurare quale Hub il client utilizzerà, impostare nella proprietà SignalRCore/Hub il nome dell'Hub prima che il client si connetta al server.

 

Connessione

Quando un client apre una nuova connessione al server, invia un messaggio di richiesta contenente il protocollo e la versione del formato. sgcWebSockets invia sempre il protocollo di formato come JSON. Il server risponderà con un errore se il protocollo non è supportato; questo errore può essere gestito tramite l'evento OnSignalRCoreError, e se la connessione ha successo, verrà chiamato l'evento OnSignalRCoreConnect.

 

Quando un client si connette a un server SignalRCore, può inviare un ConnectionId che identifica il client tra le sessioni, in modo che in caso di disconnessione il client possa riconnettersi al server passando lo stesso ConnectionId precedente. Per ottenere un nuovo ConnectionId, basta connettersi normalmente al server e si può conoscere il ConnectionId tramite OnBeforeConnectEvent. Se si desidera riconnettersi al server e passare un ConnectionId precedente, utilizzare il metodo ReConnect e passare ConnectionId come parametro.

 

 

Protocollo SignalRCore

Il protocollo SignalR è un protocollo per RPC bidirezionale su qualsiasi trasporto basato su messaggi. Entrambe le parti nella connessione possono invocare procedure sull'altra parte, e le procedure possono restituire zero o più risultati oppure un errore. Esempio: il client può richiedere un metodo al server e il server può richiedere un metodo al client. I seguenti messaggi vengono scambiati tra server e client:

 

 

 

SignalRCore Encoding

 

SignalRCore Le consente di utilizzare le seguenti codifiche:

 

 

Attualmente è supportato solo JSON, sebbene sia possibile utilizzare MessagePack codificando i messaggi inviati tramite una libreria messagepack esterna. Veda la sezione MessagePack di seguito per maggiori informazioni.

 

La configurazione del protocollo di codifica è definita nella proprietà SignalRCore.Protocol. Per impostazione predefinita il valore è srcpJSON.

 

Authorization

L'autenticazione può essere abilitata per associare un utente a ogni connessione e filtrare quali utenti possono accedere alle risorse. L'autenticazione viene implementata tramite Bearer Token: il client fornisce un token di accesso e il server convalida questo token e lo utilizza per identificare l'utente.

Nelle API Web standard, i bearer token vengono inviati in un Header HTTP, ma quando si utilizzano i websocket, il token viene trasmesso come parametro della query string.

Sono supportati i seguenti metodi:

 

srcaRequestToken

 

Se l'autenticazione è abilitata, il flusso è:

 

1. Prima tenta di ottenere un token valido dal server. Apre una connessione HTTP verso Authentication.RequestToken.URL ed esegue un POST utilizzando i dati di utente e password.

2. Se l'operazione precedente ha avuto successo, viene restituito un token. In caso contrario, viene restituito un errore.

3. Se viene restituito un token, allora apre una nuova connessione HTTP per negoziare una nuova connessione. Qui, il token viene passato come header HTTP.

4. Se l'operazione precedente ha esito positivo, apre una connessione WebSocket e passa il token come parametro della query string.

 

 

srcaSetToken

 

Qui, si passa il token direttamente al server SignalRCore (perché il token è stato ottenuto da un altro server).

 

 

 

L'Access token può essere inviato come parametro di query (questa è l'opzione predefinita) oppure inviato come HTTP Header come Bearer Token. Utilizzi la proprietà Authentication.TokenParam per configurare questo comportamento.

 

 

 

srcaBasic

 

Questa opzione utilizza l'autenticazione di base; questo metodo di autenticazione richiede la configurazione del componente SignalRCore e del TsgcWebSocketClient.

 

Esempio: se il server richiede l'autenticazione di base e il nome utente è "user" e la password è "secret", configurare i componenti come mostrato di seguito.

 


// websocket client
WSClient := TsgcWebSocketClient.Create(nil);
WSClient.Authentication.Enabled := True;
WSClient.Authentication.Basic.Enabled := True;
WSClient.Authentication.URL.Enabled := False;
WSClient.Authentication.Session.Enabled := False;
WSClient.Authentication.Token.Enabled := False;
WSClient.Authentication.User := 'user';
WSClient.Authentication.Password := 'secret';
// signalrcore
Signal := TsgcWSAPI_SignalRCore.Create(nil);
Signal.SignalRCore.Authentication.Enabled := True;
Signal.SignalRCore.Authentication.Authentication := srcaBasic;
Signal.SignalRCore.Authentication.Username := 'user';
Signal.SignalRCore.Authentication.Password := 'secret';
Signal.Client := WSClient;

 

Comunicazione tra Client e Server

Esistono tre tipi di interazioni tra server e client:

 

Invocazioni

Il Caller invia un messaggio al Callee e si aspetta un messaggio che indichi che l'invocazione è stata completata e, facoltativamente, un risultato dell'invocazione

Esempio: il client invoca il metodo SendMessage e passa come parametri il nome utente e il messaggio di testo. Invia un ID invocazione per
ottenere un messaggio di risultato dal server.


SignalRCore.Invoke('SendMessage', ['John', 'Hello All.'], 'id-000001');
 
procedure OnSignalRCoreCompletion(Sender: TObject; Completion: TSignalRCore_Completion);
begin
  if Completion.Error <> '' then
    ShowMessage('Something goes wrong.')
  else
    ShowMessage('Invocation Successful!');
end;

Invocazioni non bloccanti

Il Caller invia un messaggio al Callee e non si aspetta ulteriori messaggi per questa invocazione. Le invocazioni possono essere inviate senza un valore di Invocation ID, il che indica che l'invocazione è "non bloccante".

Esempio: il client invoca il metodo SendMessage e passa come parametri il nome utente e il messaggio di testo. Il client non si aspetta alcuna risposta dal server riguardo al risultato dell'invocazione.

 


SignalRCore.Invoke('SendMessage', ['John', 'Hello All.']);

Invocazioni in streaming

Il Caller invia un messaggio al Callee e si aspetta uno o più risultati restituiti dal Callee seguiti da un messaggio che indica la fine dell'invocazione.

Esempio: il client invoca il metodo Counter e richiede 10 numeri con un intervallo di 500 millisecondi.


SignalRCore.InvokeStream('Counter', [10, 500], 'id-000002');
 
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: TSignalRCore_StreamItem; var Cancel: Boolean);
begin
  DoLog('#stream item: ' + StreamItem.Item);
end;
 
procedure OnSignalRCoreCompletion(Sender: TObject; Completion: TSignalRCore_Completion);
begin
  if Completion.Error  '' then
    ShowMessage('Something goes wrong.')
  else
    ShowMessage('Invocation Successful!');
end;

Invocazioni

Per eseguire una singola invocazione, il Caller segue il seguente flusso di base:


procedure Invoke(const aTarget: String; const aArguments: Array of Const; const aInvocationId: String = '');
procedure InvokeStream(const aTarget: String; const aArguments: Array of Const; const aInvocationId: String);

Allocare un valore univoco di ID invocazione (stringa arbitraria, scelta dal chiamante) per rappresentare l'invocazione. Chiamare il metodo Invoke o InvokeStream specificando il Target invocato, gli Arguments e l'InvocationId (se non si invia l'InvocationId, non si riceverà il risultato di completamento).

 

Se l'invocazione è contrassegnata come non bloccante (vedere "Invocazioni Non Bloccanti" di seguito), ci si fermi qui e si restituisca immediatamente il controllo all'applicazione. Gestire il messaggio StreamItem o Completion con un ID Invocazione corrispondente.


SignalRCore.InvokeStream('Counter', [10, 500], 'id-000002');
 
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: TSignalRCore_StreamItem; var Cancel: Boolean);
begin
  if StreamItem.InvocationId = 'id-000002' then
    DoLog('#stream item: ' + StreamItem.Item);
end;
 
procedure OnSignalRCoreCompletion(Sender: TObject; Completion: TSignalRCore_Completion);
begin
  if StreamItem.InvocationId = 'id-000002' then
  begin
    if Completion.Error  '' then
      ShowMessage('Something goes wrong.')
    else
      ShowMessage('Invocation Successful!');
  end;
end;

È possibile effettuare una singola chiamata e attendere il completamento.

 


function InvokeAndWait(const aTarget: String; aArguments: Array of Const; aInvocationId: String; out Completion: TSignalRCore_Completion; 
  const aTimeout: Integer = 10000): Boolean;
function InvokeStreamAndWait(const aTarget: String; const aArguments: Array of Const; const aInvocationId: String; 
  out Completion: TSignalRCore_Completion; const aTimeout: Integer = 10000): Boolean;

Assegni un valore Invocation ID univoco (stringa arbitraria, scelta dal Caller) per rappresentare l'invocazione. Chiami il metodo InvokeAndWait o InvokeStreamAndWait contenente il Target invocato, gli Arguments e l'InvocationId. Il programma attenderà finché non viene chiamato l'evento di completamento oppure è stato superato il Time out.


var
  oCompletion: TSignalRCore_Completion;
begin
  if SignalRCore.InvokeStreamAndWait('Counter', [10, 500], 'id-000002', oCompletion) then
    DoLog('#invoke stream ok: ' + oCompletion.Result)
  else
    DoLog('#invocke stream error: ' + oCompletion.Error);
  
 
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: TSignalRCore_StreamItem; var Cancel: Boolean);
begin
  if StreamItem.InvocationId = 'id-000002' then
    DoLog('#stream item: ' + StreamItem.Item);
end;

Annullare l'invocazione

Se il client desidera smettere di ricevere messaggi StreamItem prima che il server invii un messaggio Completion, il client può inviare un messaggio CancelInvocation con lo stesso InvocationId utilizzato per il messaggio StreamInvocation che ha avviato lo stream.


procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: SignalRCore_StreamItem; var Cancel: Boolean);
begin
  if StreamItem.InvocationId = 'id-000002' then
    Cancel := True;
end;

Risultati client

 

Un'invocazione è considerata completata solo quando viene ricevuto il messaggio di completamento. Se il client riceve un'invocazione dal server, verrà chiamato l'evento OnSignalRCoreInvocation.


procedure OnSignalRCoreInvocation(Sender: TObject; Invocation: TSignalRCore_Invocation);
begin
  if Invocation.Target = 'SendMessage' then
    ... your code here ...
end;
// Once invocation is completed, call Completion method to inform server invocation is finished.
// If result is successful, then call CompletionResult method:
SignalRCore.CompletionResult('id-000002', 'ok');
 
// If not, then call CompletionError method:
SignalRCore.CompletionError('id-000002', 'Error processing invocation.');

Chiudi connessione

 

Inviato dal client quando una connessione viene chiusa. Contiene un motivo di errore se la connessione è stata chiusa a causa di un errore.


SignalRCore.Close('Unexpected message').
  
// If the server close connection by any reason, OnSignalRCoreClose event will be called. 
procedure OnSignalRCoreClose(Sender: TObject; Close: TSignalRCore_Close);
begin
  DoLog('#closed: ' + Close.Error);
end;

Ping

Il protocollo SignalR Hub supporta messaggi "Keep Alive" utilizzati per garantire che la connessione di trasporto sottostante rimanga attiva. Questi messaggi aiutano a garantire:

I proxy non chiudono la connessione sottostante durante i periodi di inattività (quando vengono inviati pochi messaggi). Se la connessione sottostante viene interrotta senza essere terminata correttamente, l'applicazione viene informata il prima possibile.

Il comportamento keep alive viene ottenuto chiamando il metodo Ping o abilitando HeartBeat sul client WebSocket. Se il server invia un ping al client, il client invierà automaticamente una risposta e l'evento OnSignalRCoreKeepAlive verrà chiamato.


procedure OnSignalRCoreKeepAlive(Sender: TObject);
begin
  DoLog('#keepalive');
end;

MessagePack

Nella codifica MsgPack del protocollo SignalR, ogni messaggio è rappresentato come un singolo array MsgPack contenente elementi che corrispondono alle proprietà del messaggio del protocollo hub specificato. Gli elementi dell'array possono essere valori primitivi, array (ad es. argomenti del metodo) o oggetti (ad es. valore dell'argomento). Il primo elemento dell'array è il tipo di messaggio.

 

Faccia riferimento alla documentazione di MessagePack per vedere come codificare i messaggi inviati.

 

Ogni volta che viene ricevuto un nuovo messaggio, questo viene inviato nell'evento OnSignalRCoreMessagePack. È possibile accedere al messaggio leggendo il parametro Data Stream. Il parametro JSON è vuoto per impostazione predefinita; se si converte il messaggio MessagePack in JSON, il componente elaborerà il messaggio JSON come se la codifica fosse JSON (quindi verranno generati gli eventi OnSignalRCoreCompletion, OnSignalRCoreInvocation...).