August 2018

Jahrgang 33, Nummer 8

Cutting Edge: Benachrichtigungen im Stil sozialer Netzwerke mit ASP.NET Core SignalR

Durch Dino Esposito | August 2018

Dino EspositoSoziale Netzwerke und mobile Betriebssysteme Popup vorgenommen, sprechblasenförmig Benachrichtigungen unglaublich beliebten und grundlegende, aber Microsoft Windows 2000 war wahrscheinlich das erste Software, die sie häufig verwenden werden. Sprechblasen mit Benachrichtigungen können sie wichtige Ereignisse, die dem Benutzer zu kommunizieren, ohne Sie zu, die sofortiges Eingreifen und Ihre Aufmerksamkeit erfordern, im Gegensatz zu herkömmlichen Popup-Fenster. Taste, um diese Benachrichtigungen sprechblasenförmig ist die zugrunde liegende Infrastruktur, die die Nachricht in Echtzeit direkt auf das geöffnete Fenster bereitstellt, in denen der Benutzer arbeitet.

In diesem Artikel sehen Sie, wie Sie ASP.NET Core SignalR zu verwenden, um Popupbenachrichtigungen zu erzeugen. Der Artikel enthält eine beispielanwendung, die verfolgt angemeldeten Benutzer und jede bietet die Möglichkeit zum Erstellen und verwalten ein Netzwerk von Freunden. Wie in einem Szenario mit sozialen Netzwerk jeder protokollierte Benutzer möglicherweise werden hinzugefügt oder daraus eine Friend-Liste zu einem beliebigen Zeitpunkt. In diesem Fall in der beispielanwendung empfängt der angemeldeten Benutzer eine kontextbezogene Benachrichtigung.

Authentifizierung von Anwendungsbenutzern

Sprechblasenförmig Benachrichtigungen sind keine einfachen übertragungsbenachrichtigungen gesendet, um Personen SignalR-Nachrichten in einem Web-Socket-Kanal überwacht. Stattdessen sind sie an bestimmten Benutzern die Anmeldung bei der Anwendung gesendet. Öffnen und das Lauschen auf eine einfache Web-Socket-Kanal ist eine gute Möglichkeit, zu dem Problem, aber ASP.NET Core SignalR ist einfach eine weitere abstrakte Programmierschnittstelle bereitstellt und bietet Unterstützung für alternative von Netzwerkprotokollen über WebSockets.

Der erste Schritt bei der Entwicklung der Anwendung ist eine Ebene für die Benutzerauthentifizierung hinzufügen. Der Benutzer erhält eine kanonische Anmeldeformular und seiner Anmeldeinformationen enthält. Nachdem Sie als gültiger Benutzer des Systems ordnungsgemäß erkannt wird, erhält sie ein Authentifizierungscookie mit einer Reihe von Ansprüchen, verpackt, wie hier gezeigt:

var claims = new[]
{
  new Claim(ClaimTypes.Name, input.UserName),
  new Claim(ClaimTypes.Role, actualRole)
};
var identity = new ClaimsIdentity(claims,
  CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
  CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(identity));

Der Beispielcode veranschaulicht das Erstellen eines ad-hoc-IPrincipal-Objekts in ASP.NET Core rund um einen Benutzernamen ein, sobald Anmeldeinformationen erfolgreich überprüft wurden. Sie dessen Schlüssel zu beachten, dass für die Authentifizierung ordnungsgemäß in ASP.NET Core funktioniert in Kombination mit der neuesten SignalR-Bibliothek, Sie Authentifizierung in der Configure-Methode der Startup-Klasse sehr früh aktiviert werden sollte (vor abschließen und in jedem Fall die SignalR Route Initialisierung). Ich werde diesen Punkt gleich zurückgegeben wird. in der Zwischenzeit betrachten wir die Infrastruktur von Friendship-Klasse in der beispielanwendung.

Definieren von Freunden in der Anwendung

Im Rahmen der beispielanwendung ist eine Friend-Beziehung nur einen Link zwischen zwei Benutzern des Systems. Beachten Sie, dass nicht die demoanwendung eine beliebige Datenbank verwendet, um Benutzer und Beziehungen beizubehalten. Einige Benutzer und Friend-Beziehungen sind hartcodiert und zurücksetzen, wenn die Anwendung neu gestartet wird oder die aktuelle Ansicht wird erneut geladen. Dies ist die Klasse, die eine Friend-Beziehung darstellt:

public class FriendRelationship
{
  public FriendRelationship(string friend1, string friend2)
  {
    UserName1 = friend1;
    UserName2 = friend2;
  }
  public string UserName1 { get; set; }
  public string UserName2 { get; set; }
}

Wie der Benutzer anmeldet, hat er eine Indexseite gearbeitet, die die Liste von Freunden enthält. Über die Benutzeroberfläche der Seite der Benutzer kann sowohl neue Freunde hinzufügen, und vorhandene entfernen (siehe Abbildung1).

Auf der Startseite eines angemeldeten Benutzers
Abbildung 1: die Startseite eines angemeldeten Benutzers

Wenn der Benutzer den Namen eines neuen Freunds eingibt, stellt ein HTML-Formular, und eine neue Friend-Beziehung wird im Arbeitsspeicher erstellt. Wenn der eingegebene Name nicht mit einen vorhandenen Benutzer übereinstimmt, um ein neuer Benutzer ein Objekt erstellt und der in-Memory-Liste hinzugefügt wie folgt:

[HttpPost]
public IActionResult Add(string friend)
{
  var currentUser = User.Identity.Name;
  UserRepository.AddFriend(currentUser, friend);
  // More code here
  ...
  return new EmptyResult();
}

Wie Sie feststellen können, gibt die Controllermethode eine leere Aktion-Ergebnis zurück. Es wird in der Tat vorausgesetzt, dass das HTML-Formular mit seinen Inhalt über JavaScript sendet. Klicken Sie deshalb eine JavaScript Handler angefügt ist die Schaltfläche "Senden" des Formulars programmgesteuert, wie hier gezeigt:

<form class="form-horizontal" id="add-form" method="post"
      action="@Url.Action("add", "friend")">
      <!-- More input controls here -->
      <!-- SUBMIT button -->
  <button id="add-form-submit-button"
       class="btn btn-danger" type="button">
       SAVE  </button></form>

Die JavaScript-Code veröffentlichen den serverseitigen Vorgang wird ausgelöst, und gibt zurück. Die neue Liste mit Freunden, kann in das aktuelle Seite Document Object Model den gleichen Code der JavaScript-Aufrufer als ein JSON-Array oder eine HTML-Zeichenfolge, von der Controllermethode zurückgegeben und integriert werden. Es funktioniert gut, aber es gibt eine Störung in Betracht ziehen, die möglicherweise ein Problem in einigen Szenarien können.

Stellen Sie sich vor, dass der Benutzer mehrere Verbindungen zur selben Serverseite enthält. Beispielsweise wird der Benutzer verfügt über mehrere Browserfenster geöffnet, auf derselben Seite und interagiert mit einer dieser Seiten. In einer Situation, in dem der Aufruf ist wieder eine direkte Antwort (, ob JSON oder HTML), letztendlich, dass nur die Seite aus, in dem die Anforderung stammt aktualisiert wird. Alle anderen geöffneten Browser-Fenster bleibt die statischen und nicht betroffen. Um das Problem zu umgehen, können Sie ein Feature von ASP.NET Core SignalR nutzen, die Sie per broadcast an alle Verbindungen, die im Zusammenhang mit dem gleichen Benutzerkonto anmelden können.

Übertragungen auf Benutzerverbindungen

Die ASP.NET MVC-Controller-Klasse, die Aufrufe zum Hinzufügen oder Entfernen von Freunden empfängt enthält einen Verweis auf den SignalR-Hub-Kontext. Dieser Code zeigt, wie dies erfolgt ist:

[Authorize]
public class FriendController : Controller
{
  private readonly IHubContext<FriendHub> _friendHubContext;
  public FriendController(IHubContext<FriendHub> friendHubContext)
  {
    _friendHubContext = friendHubContext;
  }
  // Methods here
  ...
}

Wie üblich in SignalR-Programmierung der Friend-Hub wird in der Startup-Klasse, entsprechend definiert abbildung2.

Abbildung 2: definieren den Hub

public void Configure(IApplicationBuilder app)
{
  // Enable security
  app.UseAuthentication();
  // Add MVC
  app.UseStaticFiles();
  app.UseMvcWithDefaultRoute();
  // SignalR (must go AFTER authentication)
  app.UseSignalR(routes =>
  {
    routes.MapHub<FriendHub>("/friendDemo");
  });
}

Der Schlüssel, den der UseSignalR aufrufen resultiert aus der UseAuthentication-Aufruf. Dadurch wird sichergestellt, wenn auf die angegebene Route eine SignalR-Verbindung hergestellt wird, Informationen zu den angemeldeten Benutzer und die Ansprüche verfügbar ist. Abbildung 3 bietet einen tieferen Einblick in den Code, der die Formular-Post behandelt, wenn der Benutzer einen neuen Freund zur Liste hinzugefügt.

Abbildung 3 die Formular-Post behandeln

[HttpPost]
public IActionResult Add(string friend)
{
  var currentUser = User.Identity.Name;
  UserRepository.AddFriend(currentUser, friend);
  // Broadcast changes to all user-related windows
  _friendHubContext.Clients.User(currentUser).SendAsync("refreshUI");
  // More code here  ...
  return new EmptyResult();
}

Der Clients-Eigenschaft, der den hubkontext verfügt über eine Eigenschaft namens des Benutzers, der eine Benutzer-ID verwendet. Wenn für die User-Objekt aufgerufen wird, benachrichtigt die SendAsync-Methode die angegebene Meldung an alle aktuell verbundenen Browserfenster unter dem gleichen Benutzernamen an. SignalR hat also die Möglichkeit, automatisch alle Verbindungen vom gleichen authentifizierten Benutzer mit einem einzelnen Pool zu gruppieren. Daher hat SendAsync aufgerufen, die vom Benutzer die Möglichkeit, die die Nachricht an alle Fenster, die im Zusammenhang mit der Benutzer übertragen wird, ob sie mehrere Desktopbrowsern, in-app-Web-Ansichten, mobilen Browsern, Desktopclients oder was auch immer stammen. Hier ist der Code:

_friendHubContext.Clients.User(currentUser).SendAsync("refreshUI");

Senden der RefreshUI-Nachricht an alle Verbindungen unter dem gleichen Benutzernamen wird sichergestellt, dass alle geöffneten Fenster auf eine Weise, die mit der hinzufügen-Friend-Funktion synchronisiert sind. In den Quellcode des Beispiels sehen Sie sich, dass das gleiche passiert, wenn der aktuell angemeldeten Benutzer einen Freund aus seiner Liste entfernt.

Konfigurieren den Benutzer-Clientproxy

In SignalR hat das Benutzerobjekt nichts mit dem Benutzerobjekt der HTTP-Kontext zugeordnet ist. Trotz der gleiche Name ist das SignalR-User-Objekt, einen Clientproxy und einen Container von Ansprüchen. Noch vorhanden ist eine geringfügige Beziehung, zwischen den SignalR-Clientproxy benutzerspezifische und das Objekt, das den authentifizierten Benutzer darstellt. Wie Sie möglicherweise bemerkt haben, erfordert der Clientproxy Benutzer einen Zeichenfolgenparameter an. Im Code in Abbildung 3, der Parameter ist der Name des aktuell angemeldeten Benutzers. Das ist einfach ein Zeichenfolgenbezeichner, allerdings bis hin Sie ihn konfigurieren.

Standardmäßig ist die Benutzer-ID erkannt, die vom Clientproxy Benutzer den Wert des NameIdentifier-Anspruch. Wenn die Liste von Ansprüchen des authentifizierten Benutzers NameIdentifier enthalten nicht, besteht keine Möglichkeit, die die Übertragung funktioniert. Daher haben Sie zwei Möglichkeiten: Eine besteht darin, den NameIdentifier-Anspruch hinzufügen, wenn das Authentifizierungscookie zu erstellen, und die andere besteht darin, Ihre eigenen SignalR-Benutzer-ID-Anbieter zu schreiben. Um den NameIdentifier-Anspruch hinzuzufügen, benötigen Sie den folgenden Code in den Anmeldevorgang ab:

var claims = new[]
{
  new Claim(ClaimTypes.Name, input.UserName),
  new Claim(ClaimTypes.NameIdentifier, input.UserName),
  new Claim(ClaimTypes.Role, actualRole),
};

Der Wert des NameIdentifier-Anspruchs zugewiesen spielt keine Rolle, solange sie für jeden Benutzer eindeutig ist. Intern SignalR verwendet wurden die nutzungsbeschränkungen entfernt eine IUserIdProvider-Komponente für eine Benutzer-ID, die den Verbindungsgruppen entsprechen, der derzeit angemeldete Benutzer wie folgt:

public interface IUserIdProvider
{
  string GetUserId(HubConnectionContext connection);
}

Die IUserIdProvider-Schnittstelle verfügt über eine Standardimplementierung in der DI-Infrastruktur. Die Klasse ist DefaultUserIdProvider und wird wie folgt codiert:

public class DefaultUserIdProvider : IUserIdProvider
{
  public string GetUserId(HubConnectionContext connection)
  {
    var first = connection.User.FindFirst(ClaimTypes.NameIdentifier);
    if (first == null)
      return  null;
    return first.Value;
  }
}

Wie Sie sehen können, verwendet die DefaultUserIdProvider-Klasse den Wert des NameIdentifier-Anspruch, um benutzerdefinierte Verbindungs-IDs zu gruppieren. Die Name-Anspruch ist vorgesehen, um den Namen des Benutzers anzugeben, aber nicht notwendigerweise um den eindeutigen Bezeichner bereitzustellen, über dem ein Benutzer im System identifiziert wird. Beim NameIdentifier-Anspruch dient stattdessen, die einen eindeutigen Wert enthalten soll, eine GUID fest, ob eine Zeichenfolge oder eine ganze Zahl. Wenn Sie Benutzer statt NameIdentifer wechseln, stellen Sie sicher, dass es sich bei jeder zugewiesenen Wert pro Benutzer eindeutig ist.

Alle Verbindungen, die von einem Konto mit einem übereinstimmenden Namen-Bezeichner werden gruppiert, und es werden automatisch benachrichtigt, wenn der Benutzer-Clientproxy verwendet wird. Um mit der kanonische Name-Anspruch zu wechseln, benötigen Sie eine benutzerdefinierte IUserIdProvider, wie folgt:

public class MyUserIdProvider : IUserIdProvider
{
  public string GetUserId(HubConnectionContext connection)
  {
    return connection.User.Identity.Name;
  }
}

Diese Komponente muss natürlich die DI-Infrastruktur während der Startphase registriert werden. Hier ist der Code in der ConfigureServices-Methode der Startup-Klasse eingeschlossen werden sollen:

services.AddSignalR();
services.AddSingleton(typeof(IUserIdProvider), typeof(MyUserIdProvider));

An diesem Punkt ist alles, was haben alle übereinstimmenden Benutzer Windows synchronisiert werden, auf die denselben Zustand und die Ansicht eingerichtet. Wie sieht die sprechblasenförmig Benachrichtigungen?

Der letzte Schritt

Hinzufügen und Entfernen von Freunden dazu führen, dass die Notwendigkeit von Benachrichtigungen Aktualisieren der Indexseite gesendet werden, dass der aktuelle Benutzer angezeigt wird. Wenn ein bestimmter Benutzer zwei Browserfenster geöffnet, die auf verschiedenen Seiten der gleichen Anwendung (Index und eine andere Seite) hat, erhalten sie aktualisieren nur Benachrichtigungen für die Indexseite. Allerdings hinzufügen und Entfernen von Freunden auch bewirkt, dass hinzufügen und entfernen die Benachrichtigungen an Benutzer gesendet werden, die hinzugefügt oder entfernt aus der Liste der Friend-Beziehung wurde. Z. B. wenn Benutzer Dino entscheidet sich, Benutzer Mary aus seiner Liste von Freunden zu entfernen, erhalten Benutzer Mary außerdem entfernen Sie eine Benachrichtigung. Im Idealfall sollte eine Benachrichtigung entfernen (oder hinzufügen) den Benutzer unabhängig von der Seite angezeigt wird, gibt an, ob erreichen Index oder einen anderen.

Um dies zu erreichen, stehen Ihnen zwei Optionen:

  • Verwenden Sie einen einzelnen SignalR-Hub, mit dem Einrichten der Verbindung auf der Seite "Layout" verschoben, und klicken Sie dann von allen Seiten, die basierend auf das Layout geerbt.
  • Verwenden Sie zwei unterschiedliche Hubs – eine für das Aktualisieren der Benutzeroberfläche nach dem Hinzufügen oder Entfernen von Freunden und zu benachrichtigen, hinzugefügt oder entfernt Benutzer.

Wenn Sie unterschiedliche Hubs zu entscheiden, die benachrichtigungs-Hub hinzufügen/entfernen muss eingerichtet werden auf allen Seiten, in denen Benachrichtigungen angezeigt werden sollen – wahrscheinlich die Layoutseiten verfügen Sie über in der Anwendung.

Die beispielanwendung verwendet einen einzelnen Hub vollständig in die Seite "Layout" eingerichtet. Beachten Sie, dass das JavaScript-Objekt, das die aktuelle Verbindung verweist auf global freigegeben wird, in dem Client, was bedeutet, dass der SignalR-Initialisierungscode besser am Anfang des Textkörpers Layout und vor dem Abschnitt für die Razor-Syntax RenderBody platziert wird.

Sehen wir uns der endgültige Code der Add-Methode im Controller an. Diese Methode ist, wo das Formular im Abbildung 1 letztlich sendet. Die Methode können Sie alle notwendigen Änderungen vor, in das Back-End zum Speichern von Friend-Beziehungen und stellt dann zwei SignalR-Nachrichten – eine visuell die Liste der Freunde des aktuellen Benutzers zu aktualisieren, die den Vorgang und ein zweites zur Benachrichtigung des Benutzers hinzugefügt (oder entfernt). Der Benutzer ist tatsächlich benachrichtigt, wenn sie derzeit verbunden ist, auf die Anwendung und von einer Seite empfangen und verarbeiten diese bestimmten Benachrichtigungen konfiguriert. Abbildung 4 zeigt dies.

Abbildung 4 die endgültige fügen Methodencode hinzu.

public IActionResult Add(string addedFriend)
{
  var currentUser = User.Identity.Name;
  // Save changes to the backend
  UserRepository.AddFriend(currentUser, addedFriend);
  // Refresh the calling page to reflect changes
  _friendHubContext.Clients.User(currentUser).SendAsync("refreshUI");
  // Notify the added user (if connected)
  _friendHubContext.Clients.User(addedFriend).SendAsync("added", currentUser);
  return new EmptyResult();
}

Klicken Sie auf der Seite Layout zeigt JavaScript-Code an eine Benachrichtigung sprechblasenförmig (oder den Typ der UI-Anpassung, die Sie vornehmen möchten). In der beispielanwendung akzeptiert die Benachrichtigung die Form einer Nachricht, die auf eine Benachrichtigungsleiste für bis zu 10 Sekunden angezeigt, wie hier im Code angegeben:

friendConnection.on("added", (user) => {
  $("#notifications").html("ADDED by <b>" + user + "</b>");
  window.setTimeout(function() {
            $("#notifications").html("NOTIFICATIONS");
    },
    10000);
});

Die Ergebnisse werden angezeigt, Abbildung 5.

Cross-Benutzerbenachrichtigungen
Abbildung 5-Cross-Benutzerbenachrichtigungen

Ein Wort für SignalR-Gruppen

In diesem Artikel ging es um die SignalR-Unterstützung für die ausgewählten Benachrichtigungen, die eine zusammengehörige Gruppe von Benutzern an. SignalR bietet eine Reihe von Methoden – die Clientproxy für Benutzer und Gruppen. Der Unterschied ist die feine: Der Benutzer-Clientproxy generiert implizit eine Anzahl von Gruppen, in dem der Name wird bestimmt, indem Sie die Benutzer-ID und Member sind alle Verbindungen, die durch die Benutzer mit der gleichen Anwendung geöffnet. Einer Gruppe handelt es sich um einen allgemeineren Mechanismus, der programmgesteuert Verbindungen zu einer logischen Gruppe angefügt. Sowohl die Verbindungen als auch die Namen der Gruppe werden programmgesteuert festgelegt.

Sprechblasenförmig Benachrichtigungen wurden in diese beiden Vorgehensweisen implementiert, aber dieses spezielle Szenario des Benutzers Clientproxy war die beste Lösung. Quellcode finden Sie unter bit.ly/2HVyLp5.


Dino Esposito hat in seiner 25-jährigen Karriere mehr als 20 Bücher und 1.000 Artikel verfasst. Als Autor von „The Sabbatical Break“, einer theatralisch angehauchten Show, schreibt Esposito Software für eine grünere Welt als digitaler Stratege bei BaxEnergy. Folgen Sie ihm auf Twitter: @despos.


Diesen Artikel im MSDN Magazine-Forum diskutieren