Sunteți pe pagina 1din 30

Curs 7

Aplicatii Real-time cu
SignalR
Lect. dr. Florina Covaci
SignalR
• Creata in 2011 de David Fowler si Damian Edwards
• Adaugata in ASP.NET 2013, rescrisa in ASP.NET Core
• Librarie open-source care simplifica adaugarea de functionalitati in
timp real pentru aplicatiile web
• Functionalitatile in timp real permit codului de tip server-side sa
trimita continut catre clienti instant
• Scenarii de utilizare:
• Aplicatii care necesita actualizari frecvente de la server (ex. jocuri, retele
sociale, sistem de votare, licitatii, harti , aplicatii gps etc)
• Dashboard-uri, aplicatii de monitorizare (ex. actualizari pentru instant sale )
• Aplicatii colaborative (ex. whiteboard, software pentru team-meeting)
• Aplicatii care necesita notificari (ex. retele sociale, email, chat-uri)
Facilitati SignalR
• Realizeaza gestiunea automata a conexiunilor
• Trimite mesaje catre toti clientii conectati simultan (ex. un chat room)
• Trimite mesaje catre un client specificat sau un grup de clienti
• Suport pentru scalare(in situatii cu trafic crescut) incorporat – ex. SQL
Server as a backplane
• Un backplane permite instantelor diferite ale aceleiasi aplicatii SignalR sa
comunice una cu cealalta si sa trimita mesaje clientilor indiferent de
instanta la care sunt conectati client
• ASP.NET Core SignalR a fost rescris cu un model mai simplu pentru scalare –
nu mai permite unui client sa se conecteze la instante diferite pentru cereri
successive (sticky sessions)
Hubs
• SignalR utilizeaza hubs pentru a comunica intre client si server
• Hub– un pipeline de nivel inalt care permite unui client si unui server
sa apeleze metodele celulalt
• In codul server definim metode care sunt apelate de client
• In codul client definim metode care sunt apelate de server
• SignalR gestioneaza mecanismul care face posibila comunicarea in
timp real client-server si server-client
Configurare Hubs
• Configurarea serviciilor necesare prin apelul in metoda
ConfigureServices din Startup.cs
services.AddSignalR();

• Setarea modului de rutare

app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chathub"); })
Crearea si utilizarea unui hub
• cream o clasa care mostenste clasa de baza Hub
• adaugam metode publice – clientii vor apela metodele publice

public class ChatHub : Hub


{
public async Task SendMessage(string user, string
message)
{
await Clients.All.SendAsync("ReceiveMessage",
user, message);
}
}
Hub.Context - proprietati
• Clasa Hub - proprietate Context care contine urmatoarele atribute
care ofera informarii despre conexiune:
1. ConnectionId – ID unic pentru fiecare conexiune
2. UserIdentifier – Un utilizator poate avea mai multe conexiuni in aplicatie.
De ex. Un utilizator poate fi conectat de pe laptop si telefon. Fiecare device
are o conexiune SignalR separata, dar sunt asociate cu acelasi utilizator.
Daca un mesaj se trimite la utilizator, toate conexiunile asociate cu acel
utilizator primesc acel mesaj
3. Items – ofera o colectie de tipul cheie/valoare care poate fi folosita pentru
a persista date pentru conexiunea curenta
4. ConnectionAbort – utilizeaza un CancellationToken care trimite o notificare
cand conexiunea este intrerupta
Hub.Context - metode

• GetHttpContextReturns – returneaza HTTPContext sau null daca


conexiunea nu este asociata cu o cerere HTTP.
Utilizam metoda pentru a prelua informatii legate de headere HTTP
si query strings

• Abort - intrerupe conexiunea


Hub.Clients - proprietati
• All –apeleaza o metoda pentru toti clientii conectati

• Caller – apleaza o metoda a clientului care a invocat metoda hub-ul

• Others –apeleaza o metoda catre toti clientii conectati cu exceptia


clientului care a invocat metoda
Hub.Clients - metode
• AllExcept – apleaza o metoda pe toti clientii conectati cu exceptia
conexiunilor specificate
• Group – apeleaza o metoda pe toate conexiunile din grupul specificat
• GroupExcept – apeleaza o metoda pe toate conexiunile din grup in
afara celor specificate
• Groups – apleaza o metoda pe grupuri multiple de conexiuni
• OthersInGroup – apeleaza o metoda pe un grup de conexiuni,
excluzand clientul care a invocat metoda hub
• User/Users – apeleaza o metoda pe toate conexiunile asociate cu un
utilizator/utilizatori specificati
Trimiterea de mesaje la clienti
• Trimite un mesaj catre toti clientii conectati
public Task SendMessage(string user, string message) { return
Clients.All.SendAsync("ReceiveMessage", user, message); }
• Trimite un mesaj catre apelant
public Task SendMessageToCaller(string user, string message) { return
Clients.Caller.SendAsync("ReceiveMessage", user, message); }
• Trimite un mesaj catre toti clientii din grupul “SignalR Users”
public Task SendMessageToGroup(string user, string message) { return
Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user,
message); }
Hub puternic tipizat
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}

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); }
}
Modificarea numelui unei metode Hub
• Implicit numele unei metode este cel din .NET
• Atributul HubMethodName

[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{ return Clients.User(user).SendAsync("ReceiveMessage", user,
message);
}
Evenimente la nivel de conexiune
• OnConnectedAsync
• Supracriem metoda pentru a realiza actiuni cand un client se
conecteaza la un hub – ex. adaugam un client la un grup

public override async Task OnConnectedAsync()


{ await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR
Users");
await base.OnConnectedAsync();
}
Evenimente la nivel de conexiune
• OnDisconnectedAsync
• Suprascriem metoda pentru a realiza actiuni cand un client se
deconecteaza – ex. eliminam clientul din grup

public override async Task OnDisconnectedAsync(Exception


exception)
{ await Groups.RemoveFromGroupAsync(Context.ConnectionId,
"SignalR Users");
await base.OnDisconnectedAsync(exception);
}
Grupuri in SignalR
• Grupurile reprezinta o colectie de conexiuni care au asociat un nume
• Mesajele se pot trimite catre toate conexiunile dintr-un grup
• O conexiune poate fi membra in mai multe grupuri
• Grupurile sunt utile pentru aplicatii de tipul chat-urilor unde fiecare
room poate fi reprezentat ca un grup
• Conexiunile pot fi adaugate sau inlaturate din grup prin metodele
AddToGroupAsync si RemoveFromGroupAsync
Grupuri - exemplu
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has
joined the group {groupName}.");
}

public async Task RemoveFromGroup(string groupName)


{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has
left the group {groupName}.");
}
• Apartenenta la grup nu este pastrata cand o conexiune se reconecteaza. Conexiunea trebuie sa se
realature grupului cand se restabileste.
Autentificare
• SignalR poate fi utilizat cu mecanismul de autentificare ASP.NET Core
pentru a asocia un utilizator cu fiecare conexiune

• Intr-un hub, datele de autentificare pot fi accesate din proprietatea


HubConnectionContext.User

• Autentificarea permite sa apelam metode pentru toate conexiunile


asociate cu un utilizator
Metode de autentificare
• Cookie
• Intr-o aplicatie bazata pe browser, autentificarea cu cookie permite
credentialelor existente sa fie introduse in conexiunile SignalR
• Nu sunt necesare configurari aditionale – daca utilizatorul este logat in
aplicatie, conexiunea SignalR mosteneste automat aceasta autentificare

• Token
• Clientul poate trimite un token. Serverul valideaza token-ul si il utilzeaza
pentru a identifica utilizatorul
• Validarea se realizeaza cand se realizeaza conexiunea, iar pe durata de viata a
conexiunii, serverul nu revalideaza automat tokenul pentru a verifica daca e
necesata revocarea acestuia
Cookie vs. token
• Cookie-urile sunt specifice browserelor
• Trimiterea de cookie-uri din alte aplicatii adauga complexitate sporita
aplicatiei spre deosebire de trimiterea de token-uri

• Autentificarea cu cookie-uri nu este recomandata pentru aplicatii care


nu autentifica utilizatorul pe baza browserului
• Autentificarea cu token este abordarea recomandata pentru clienti de
alte tipuri decat browserul
Autorizare
• Autorizarea utilizatorilor pentru accesul la hub-uri si metodele din
hub-uri

• Implicit toate metodele din hub pot fi apelate de un utilizator


neautentificat

• Pentru a solicita autentificarea aplicam atributul [Authorize] la hub


Autorizare la nivel hub
[Authorize]
public class ChatHub: Hub { }

• Pentru a restictiona accesul doar pentru acei utilizatori care respecta


o anumita politica de autorizare. Ex. Avem o politica custom
MyAuthorizationPolicy
[Authorize("MyAuthorizationPolicy")]
public class ChatHub : Hub { }
Autorizare la nivel metoda
[Authorize]
public class ChatHub : Hub { public async Task Send(string message)
{ // ... send a message to all users ... }
[Authorize("Administrators")]
public void BanUser(string userName)
{
// ... ban a user from the chat room (something only Administrators can
do) ...
}
}
Handleri pentru autorizare
• Customizarea autorizarii metodei

[Authorize]
public class ChatHub : Hub
{
public void SendMessage(string message) { }

[Authorize("DomainRestricted")]
public void ViewUserHistory(string username) { }

}
public class DomainRestrictedRequirement :
AuthorizationHandler<DomainRestrictedRequirement, HubInvocationContext>,
IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext
context, DomainRestrictedRequirement requirement, HubInvocationContext
resource)
{ if (IsUserAllowedToDoThis(resource.HubMethodName, context.User.Identity.Name))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
private bool IsUserAllowedToDoThis(string hubMethodName, string
currentUsername)
{ return !(currentUsername.Equals(“asdf123@ubbcluj.ro") &&
hubMethodName.Equals("ViewUserHistory", StringComparison.OrdinalIgnoreCase)); }
}
Adaugarea noii politici
• Startup.cs – cream politica DomainRestricted
public void ConfigureServices(IServiceCollection services)
{ // ... other services ...
services
.AddAuthorization(options =>
{ options.AddPolicy("DomainRestricted", policy =>
{ policy.Requirements.Add(new
DomainRestrictedRequirement());
});
});
}
Tracking pentru utilizatori online

• Un utilizator poate avea mai multe conexiuni intr-un hub, trebuie sa


facem tracking pentru numarul de conexiuni/utilizator
public class PresenceTracker {
private static readonly Dictionary<string, int> onlineUsers = new
Dictionary<string, int>();
public Task<ConnectionOpenedResult> ConnectionOpened(string
userId)
{ var joined = false;
if (onlineUsers.ContainsKey(userId)) { onlineUsers[userId] += 1; }
else { onlineUsers.Add(userId, 1); joined = true; }
}
return Task.FromResult(new ConnectionOpenedResult { UserJoined =
joined });
}
public Task<ConnectionClosedResult> ConnectionClosed (string userId)
{ var left = false;
if (onlineUsers.ContainsKey(userId))
{ onlineUsers[userId] -= 1;
if (onlineUsers[userId] <= 0)
{ onlineUsers.Remove(userId); left = true; }
}

return Task.FromResult(new ConnectionClosedResult { UserLeft = left


});
}
public Task<string[]> GetOnlineUsers()
{
{
return Task.FromResult(onlineUsers.Keys.ToArray());

public class ConnectionOpenedResult


{
public bool UserJoined { get; set; }
}

public class ConnectionClosedResult


{
public bool UserLeft { get; set; }
}

S-ar putea să vă placă și