ASP.NET Core SignalR ist eine Bibliothek von Microsoft, die insbesondere Entwicklern die Möglichkeit bietet, Echtzeitfunktionen in Webanwendungen einzubinden. Obwohl man bei dem Begriff „Echtzeit“ natürlich zuerst an Chats denkt, lässt sich SignalR auf viele Anwendungsfälle wie Spiele, Online-Umfragen, Echtzeit-Dashboards, kollaborative Anwendungen und so weiter anwenden. Wir bei Redwerk haben viel Erfahrung mit SignalR und können mit Sicherheit sagen, dass es sich um eine wirklich leistungsstarke und einfach zu bedienende Bibliothek handelt.

Stellen wir uns vor, Ihr Kunde hat ein Scheduler-Dashboard für ein großes Team. Wie würden Sie den Endbenutzern aktuelle Informationen zur Verfügung stellen, ohne dass sie die Seite ständig aktualisieren müssen? Oder wenn Sie zum Beispiel Azure Media Services verwenden und Ihre Nutzer darüber informieren müssen, dass ein Video erfolgreich verarbeitet wurde? Der „schlechte“ Weg wäre die Verwendung von Polling. Tatsächlich ist Polling an sich nichts Schlechtes, da es sich dabei nur um eine Implementierung eines Endpunktaufrufs handelt. Allerdings ist ständiges Polling eine Verschwendung, vor allem wenn man einen Endpunkt abfragen muss, der jedes Mal etwas aus der Datenbank abruft. Das ist eine Verschwendung von Ressourcen, Datenverkehr und Aufwand, und zweifelsohne werden die meisten Anfragen nicht nützlich sein. An dieser Stelle kommt SignalR ins Spiel.

Wie in der offiziellen Dokumentation erwähnt, unterstützt SignalR mehrere Techniken zur Handhabung von Echtzeitkommunikation:

  • WebSockets: Dies ist der optimalste Weg, da es der einzige Transport ist, der eine echte dauerhafte Zwei-Wege-Verbindung herstellt. Wenn sowohl Client als auch Server einen solchen Mechanismus unterstützen, wird er verwendet.
  • Server-Sent Events wird verwendet, wenn WebSockets nicht unterstützt werden. Dies ist ein Mechanismus, der es dem Server ermöglicht, die Daten asynchron an den Client zu senden, sobald eine Pipe vom Server zum Client erstellt wurde (durch Erstellen eines Objekts namens EventSource).
  • Long Polling wird verwendet, wenn sowohl WebSockets als auch Server-Sent Events nicht unterstützt werden. Es basiert auf dem Comet-Webanwendungsmodell, bei dem der Client wie beim normalen Polling Informationen vom Server anfordert, aber wenn der Server keine neuen Informationen für den Client hat, hält er die Anforderung offen und wartet auf zu sendende Informationen, anstatt eine leere Antwort zu senden. Sobald er diese hat, schließt der Server die Anfrage ab, indem er eine Antwort an den Client sendet. Normalerweise fordert der Client danach sofort wieder Informationen vom Server an, so dass der Server fast immer eine verfügbare, wartende Anfrage hat.

Hinter den Kulissen wählt SignalR automatisch die beste Methode, die im Rahmen der Möglichkeiten von Server und Client liegt.

In diesem Artikel werden wir ASP.NET Core SignalR verwenden und einige Leute können zwischen ihm und ASP.NET SignalR verwirrt sein, also lassen Sie uns diesen Punkt klären. Es gibt zwei Implementierungen von SignalR:

  1. ASP.NET SignalR – vertreten durch das NuGet-Paket Microsoft.AspNet.SignalR und läuft auf Anwendungen, die das .NET Framework und System.Web verwenden;
  2. ASP.NET Core SignalR ist Teil der ASP.NET Core-Plattform, die sowohl auf .NET Core als auch auf .NET Framework läuft und das NuGet-Paket Microsoft.AspNetCore.App verwendet. Es ist nicht mit Clients oder Servern für ASP.NET SignalR kompatibel.

Insgesamt behält ASP.NET Core SignalR viele der Kernkonzepte und -funktionen von SignalR bei. Hier sind die Hauptunterschiede zwischen ihnen:

  • Automatische Wiederverbindungen. Sie werden in ASP.NET Core SignalR nicht unterstützt. Die Client-Anwendung muss explizit eine neue Verbindung starten, wenn sie benötigt wird. Als ASP.NET SignalR zum ersten Mal erschien, schien die automatische Wiederverbindung eine gute Idee zu sein, aber dies erwies sich als fehlerhaft und ineffizient. Dasselbe gilt für die Zwischenspeicherung von nicht gesendeten Nachrichten auf dem Server, es ist Sache des Servers, dies zu implementieren oder nicht.
  • Unterschiede auf dem Client. Der ASP.NET Core SignalR-Client ist in TypeScript geschrieben. Sie können JavaScript oder die TypeScript-Client-Bibliothek verwenden, ohne zwingend auf jQuery verweisen zu müssen. Außerdem wurde die Art der Beschaffung geändert: In früheren Versionen wurde der JavaScript-Client über ein NuGet-Paket in Visual Studio bezogen, aber jetzt können Sie npm verwenden, um das Paket zu beziehen und zu installieren.
  • Skalierung. Es gibt jetzt einen kleinen Unterschied bei der Skalierung, da der Azure SignalR Service nun ASP.NET unterstützt. Grundsätzlich wurde SignalR mit eingebauter Unterstützung für Scaleout mit Redis und SQL Server ausgeliefert. ASP.NET Core SignalR wurde mit einem einfacheren und erweiterbaren Scale-Out-Modell überarbeitet: Es unterstützt Redis und Azure SignalR Service.
  • Protokoll-Unterstützung. ASP.NET Core SignalR unterstützt JSON, ein neues binäres Protokoll, das auf MessagePack basiert, und ermöglicht auch die Erstellung von benutzerdefinierten Protokollen.
  • Injektion von Abhängigkeiten (DI). ASP.NET Core wird mit DI ausgeliefert, die in das Framework integriert ist. Dienste können damit auf den HubContext zugreifen (wir werden diesen Punkt später in diesem Artikel besprechen).

In diesem Artikel werden wir den Prozess der Erstellung eines einfachen Chats überprüfen. Obwohl es sich um ein sehr „klassisches“ Beispiel handelt, ist es einfach, den allgemeinen Prozess der Arbeit mit SignalR zu zeigen, und es ist überhaupt nicht zeitaufwändig. Als Frontend-Framework werden wir Vue.js verwenden – ein Open-Source-JavaScript-Framework zur Erstellung von Benutzeroberflächen. Momentan unterstützt ASP.NET Core nur Angular, React und React mit Redux out-of-the-box. Im nächsten Abschnitt gehen wir ein wenig darauf ein, wie Sie mit dieser Situation umgehen können und geben Ihnen einen Überblick darüber, womit wir im Praxisteil arbeiten werden.

Da unser Hauptziel darin besteht, ein gewisses Wissen über die Arbeit mit ASP.NET Core SignalR zu erlangen, werden wir nicht im Detail auf Vue.js eingehen. Sicherlich werden wir einige Details liefern, die es den Lesern ermöglichen, ein allgemeines Verständnis dafür zu bekommen, wie alles funktioniert, aber nicht in einer zu detaillierten Weise.

ASP.NET Core mit Vue.js

Die Diskussion darüber, was besser zu verwenden ist – ReactJS, Angular, Vue.js oder etwas anderes – kann ein Thema sein, das einen eigenen Artikel verdient. In diesem Artikel haben wir uns für das Vue.js-Framework entschieden, da es recht einfach, aber leistungsstark ist und über eine gute Dokumentation verfügt (was vor allem für Anfänger wichtig ist). Vue wurde zum beliebtesten Frontend-Projekt auf GitHub im Jahr 2018, zum Zeitpunkt des Schreibens hat es 148 Tausend Sterne und mehr als 21 Tausend Forks auf GitHub. Außerdem hat Vue.js keine offizielle Vorlage und diejenigen, die es verwenden müssen, können ein wenig verwirrt sein, was zu tun ist. All diese Gründe dienten als Anstoß, sich für Vue.js zu entscheiden und den Lesern einige der verfügbaren Lösungen zu zeigen.

Das Problem mit den fehlenden Vorlagen trat für Entwickler mit ASP.NET Core 2.1 Version auf. Zuvor waren die SPA-Templates über das Microsoft.AspNetCore.SpaTemplates-Paket verfügbar und hatten Vue.js-Unterstützung. Da alle SPA-Templates in das Core-Repository verschoben wurden, wurde die Auswahl eingeschränkt. Die gute Nachricht ist, dass es ein paar Möglichkeiten gibt, dieses Problem zu lösen.

Die erste Variante ist die Erstellung eines separaten Projekts für das Frontend unter Verwendung des Vue CLI. Dies ist eine weit verbreitete Lösung, für die man viele Anleitungen finden kann. Diese Variante ist auch komplizierter, hat aber ihre eigenen Vorteile, wie z.B. die unabhängige Skalierung und die Möglichkeit, geeignete Hosting-Optionen füreinander zu nutzen.
Der zweite Ansatz ist das manuelle Ersetzen von React durch Vue.js in der React SPA-Vorlage. Tatsächlich wurden einige Open-Source-Vorlagen genau auf diese Weise erstellt, so dass Sie jetzt ihre Grundlage und ihr Wesen verstehen können.

Um der Kürze willen werden wir nicht alle diese Lösungen aufzählen. Stattdessen beschränken wir uns auf die letzte hier aufgeführte Lösung: die Verwendung der bereits erstellten Vorlagen (die wir weiter oben bereits erwähnt haben). Es gibt eine ganze Reihe von Open-Source-Vorlagen, die Sie verwenden können. Wir haben uns für die recht beliebte Vorlage von TrilonIO entschieden. Sie können aber auch jede andere Vorlage ausprobieren und entscheiden, welche Ihnen am besten gefällt. Wir haben uns für die Vorlage von TrilonIO entschieden, weil wir gute Erfahrungen mit dieser Vorlage in einem unserer Projekte gemacht haben.

Wenn Sie nun eine Vorstellung davon haben, wofür SignalR geeignet ist, welche Anforderungen wir für unsere Demo benötigen und was uns zu dieser Wahl veranlasst hat, können wir damit beginnen, den gesamten Prozess gezielt zu überprüfen.

Erste Schritte mit Vue.js und ASP.NET Core SignalR

Einrichten des Projekts

In diesem Abschnitt wird der Prozess der Erstellung eines einfachen Chats im Detail besprochen, so dass diejenigen, die bereits in gewisser Weise damit vertraut sind, die Erklärung überspringen können und nur die Art und Weise der Arbeit mit SignalR besprechen. Für Anfänger ist es jedoch sicher nützlich und interessant, den Prozess von Grund auf zu sehen. Wir besprechen eine Variante der Erstellung einer neuen Lösung zusammen mit dem Projekt unter Verwendung der Windows PowerShell. Sie sollten Node.js und .NET Core installiert haben und natürlich über eine IDE verfügen.

Also, fangen wir an. Öffnen Sie die Konsole, navigieren Sie zu Ihrem Ordner und führen Sie den folgenden Befehl aus:

$ dotnet new sln -n RealTimeApp

Mit dem Befehl `dotnet new` können Sie ein neues Projekt, eine Konfigurationsdatei oder eine Projektmappe auf der Grundlage der angegebenen Vorlage mit allen erforderlichen Abhängigkeiten erstellen. Hier geben wir an, dass wir eine Projektmappe (mit der Option `sln`) mit dem Namen `RealTimeApp` (mit der Option `-n RealTimeApp`) erstellen wollen. Wenn Sie den Namen nicht angeben, wird er automatisch dem Namen des Ordners entsprechen. Nachdem Sie diesen Befehl ausgeführt haben, sollten Sie eine neu erstellte Projektmappe in Ihrem Ordner sehen. Beziehen Sie sich auf die Standardstruktur des Projekts und erstellen Sie mit dem nächsten Befehl einen neuen Ordner neben der Lösung:

$ md RealTimeAppCore

Wir verwenden weiterhin die TrilonIO-Vorlage, die manuell installiert werden muss, indem der folgende Befehl im erstellten Ordner ausgeführt wird:

$ dotnet new --install "aspnetcore-vuejs"

Als Ergebnis sollten Sie eine Liste aller verfügbaren Vorlagen sehen und in der Lage sein, ein neues Projekt mit einer dieser Vorlagen zu erstellen. Die Vorlage, die wir benötigen, muss einen Namen vuejs haben, und mit dem unten stehenden Befehl können wir ein neues Projekt erstellen:

$ dotnet new vuejs

er Projektname ist derselbe wie der Ordnername, was für uns passend ist. Die obigen Befehle erstellen nur das neue Projekt und die Projektmappe, aber sie sind nicht miteinander verbunden. Um sie miteinander zu verknüpfen, kehren Sie zum Lösungsordner zurück und führen diesen Befehl aus:

$ dotnet sln add .\RealTimeAppCore\RealTimeAppCore.csproj

Wenn Sie nun das Projekt im Visual Studio öffnen, sehen Sie ein ziemlich standardmäßiges Projekt mit ein paar Controllern, Views und Models, sowie ein einfaches Beispiel für die Verwendung. Die Vorlage kann ziemlich interessant für diejenigen sein, die keine Erfahrung mit Vue.js haben. Wir erstellen alles von Grund auf neu, also lassen Sie uns die folgenden unnötigen Dateien entfernen:

  • …\ClientApp\components\about.vue
  • …\ClientApp\components\fetch-data.vue
  • …\ClientApp\components\counter-example.vue
  • whole folder…\Providers\
  • …\Controllers\WeatherController.cs
  • …\Models\WeatherForecast.cs

Nach dem Löschen können einige Fehler auftreten. Um diese zu beheben, müssen Sie nur die StartupClass bereinigen, indem Sie die folgenden Zeilen entfernen:

  1. using RealTimeAppCore.Providers;
  2. services.AddSingleton<IWeatherProvider, WeatherProviderFake>();

Jetzt haben wir ein sauberes Projekt, so dass wir mit der Einrichtung eines Projekts für unsere Bedürfnisse beginnen können. Zunächst benötigen wir ein Model und einen Controller. Klicken Sie mit der rechten Maustaste auf den Ordner „Models“, wählen Sie im Kontextmenü „Add“ und erstellen Sie eine neue Klasse „Message“. In unserem Fall muss sie nur zwei Felder enthalten: „Text“ und ‚Datum‘, wie unten dargestellt:

using System;

  namespace RealTimeAppCore.Models {
      public class Message {
          public string Text { get; set; }

          public DateTime Date { get; set; }
      }
  }

Die Struktur ist einfach genug, deshalb brauchen wir sie nicht zu überprüfen. Der Controller wird ein wenig komplizierter sein. Wie bei den Modellen, klicken Sie mit der rechten Maustaste auf den Ordner „Controllers“, wählen Sie im Kontextmenü „Add“ und erstellen Sie eine neue Klasse „MessagesController“. Hier ist der Inhalt dieser Datei, die wir im Folgenden überprüfen:

using Microsoft.AspNetCore.Mvc;
  using RealTimeAppCore.Models;
  using System.Collections.Generic;

  namespace RealTimeAppCore.Controllers {
      [Route("api/[controller]")]
      public class MessagesController : Controller {
          [HttpGet("[action]")]
          public IActionResult GetMessages() {
              List messages = new List();

              var result = new {
                  messages
              };

              return Ok(result);
          }
      }
  }

Mit dem Attribut `[Route(“api/[controller]”)]` legen wir fest, dass alle Methoden innerhalb dieses Controllers unter dem Pfad `[host]/api/[controller name]/[method name]`. ausgeführt werden können. Anfänger müssen bedenken, dass Controller-Name den Teil des Namens ohne das Wort „Controller“ bedeutet, so dass die URL für die Methode `GetMessages [host]/api/Messages/GetMessages` lautet. Zu diesem Zeitpunkt haben wir nur eine Methode, die eine leere Liste von Nachrichten zurückgibt.

Um die Einrichtung unseres Projekts abzuschließen, müssen wir noch einige Änderungen an den Frontend-Dateien vornehmen. Zunächst bereinigen wir unsere Routen, indem wir den Inhalt der Datei `ClientApp\router\routes.js` durch die folgenden Zeilen ersetzen:

import HomePage from 'components/home-page'

  export const routes = [
      { name: 'home', path: '/', component: HomePage, display: 'Home', icon: 'home' }
  ]

Wir brauchen nur ein einfaches Routing, so dass das bereinigte Routing (da wir andere Ansichten gelöscht haben) aus dem Beispiel für uns passt. Zweitens müssen wir unsere Homepage aktualisieren. In diesem Schritt entfernen wir nur alle unnötigen Daten und fügen einen einfachen Loader, einen Titel und eine Methode zum Abrufen der Daten hinzu. Ersetzen Sie den Inhalt der Datei `ClientApp\components\home-page.vue` durch die folgenden Zeilen:


 <template>
      <div>
          <h1>Simple chat</h1>

          <div v-if="!messages" class="text-center">

              <p><em>Loading...</em></p>
              <h1><icon icon="spinner" pulse /></h1>
          </div>

          <template v-if="messages">
              <div v-for="(message, index) in messages" :key="index">
                  <p><em>Message:</em>'{{ message.text }}'</p>
              </div>
          </template>
      </div>
  </template>

  <script>
      export default {
          data() {
              return {
                  messages: null
              }
          },

          methods: {
              async loadMessages() {
                  try {
                      let response = await this.$http.get(`/api/messages/getMessages`)
                      this.messages = response.data.messages
                  } catch (err) {
                      console.log(err)
                  }
              }
        },

        async created() {
            this.loadMessages()
        }
      }
  </script>

  <style>
  </style>

Für diejenigen, die mit Vue.js nicht vertraut sind, wollen wir den Inhalt dieser Datei kurz erläutern. Wenn eine Vue-Instanz erstellt wird, fügt sie alle Eigenschaften, die in ihrem Datenobjekt gefunden werden, dem Reaktivitätssystem von Vue hinzu. In unserem Beispiel stellen wir einfach ein leeres Objekt „messages“ ein, das in der `loadMessages` Methode aktualisiert wird.

Jede Vue-Instanz durchläuft bei der Erstellung eine Reihe von Initialisierungsschritten und führt sogenannte „lifecycle hooks“ Funktionen aus. Mit Hilfe solcher Hooks können Sie Ihren eigenen Code hinzufügen, der in bestimmten Phasen ausgeführt wird. In unserem Beispiel haben wir den „created“-Hook verwendet, um das Message-Objekt mit den Daten aus dem Controller zu versehen, nachdem die Vue-Instanz erstellt wurde.

Diejenigen unter den Lesern, die nur wissen wollen, wie man ein ASP.NET Core Projekt mit Vue.js einrichtet, müssen jetzt eine grundlegende Vorlage haben.

SignalR konfigurieren

Es ist nun an der Zeit, sich mit dem Kernpunkt des Artikels vertraut zu machen – SignalR. Sie benötigen keine zusätzlichen Installationen, da es ein Teil des Microsoft.AspNetCore.App-Metapakets ist, das automatisch installiert werden muss.

Um SignalR zu aktivieren, müssen Sie einfach Ihre Startup-Klasse wie folgt aktualisieren:

  • Fügen Sie using `Microsoft.AspNetCore.SignalR;` Namespace am Anfang der Datei hinzu;
  • Aktualisieren Sie die ConfigureServices-Methode, indem Sie den Aufruf services.AddSignalR hinzufügen, der die von der SignalR-Middleware benötigten Dienste konfiguriert:
    public void ConfigureServices(IServiceCollection services) {
          services.AddSignalR();
          // Add framework services.
          services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
      }
    
  • SignalR-Routen einrichten, indem Sie app.UseSignalR in der Methode Startup.Configure aufrufen:
    public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
          if (env.IsDevelopment()) {
              app.UseDeveloperExceptionPage();
    
              // Webpack initialization with hot-reload.
              app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
                  HotModuleReplacement = true,
              });
          } else {
              app.UseExceptionHandler("/Home/E
              // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
              app.UseHsts();
          }
    
          app.UseHttpsRedirection();
          app.UseStaticFiles();
    
          app.UseSignalR(route => {
              route.MapHub("/signalr-hub");
          });
    
          app.UseMvc(routes => {
              routes.MapRoute(
              name: "default",
              template: "{controller=Home}/{action=Index}/{id?}");
    
              routes.MapSpaFallbackRoute(
              name: "spa-fallback",
              defaults: new { controller = "Home", action = "Index" });
          });
      }
    

Mit der Zeile `route.MapHub(“/signalr-hub”)` richten wir die `SignalRHub`-Klasse als Request-Handler für den Pfad „signalr-hub“ ein, so dass Clients in der Lage sein sollten, sich mit dem Hub unter `[host]/signalr-hub` zu verbinden.

Erste Schritte mit SignalR Hubs

Mit Hilfe von Hubs ermöglicht ASP.NET Core SignalR die Zwei-Wege-Kommunikation zwischen Server und Client. Wie in der offiziellen Dokumentation beschrieben, rufen Hubs clientseitigen Code auf, indem sie Nachrichten senden, die den Namen und die Parameter der clientseitigen Methode enthalten. Der Client versucht, den Namen mit einer Methode im client-seitigen Code abzugleichen. Wenn eine Übereinstimmung gefunden wird, ruft er die Methode auf und übergibt die deserialisierten Parameterdaten an sie.

Die Erstellung eines Hubs ist ein sehr einfacher Prozess. Erstellen Sie einen neuen Ordner mit dem Namen „Hubs“ innerhalb des Projekts und fügen Sie eine neue `SignalRHub.cs`-Datei hinzu. Unsere neue Klasse sollte von der Hub-Basisklasse geerbt werden:

using Microsoft.AspNetCore.SignalR;

  namespace ResPlanner.Hubs {
      public class SignalRHub : Hub {
      }
  }

Sie können auch öffentliche Methoden innerhalb dieser Klasse hinzufügen, die von Clients aufgerufen werden können. SignalR übernimmt die Serialisierung und Deserialisierung in Parametern und gibt Werte zurück, so dass Sie diese wie in jeder C#-Methode angeben können.

Wir verwenden auch die SignalR JavaScript-Client-Bibliothek, um den Aufbau der Verbindung zu unserem Hub und die Verwaltung von Ereignissen zu verwalten. Sie wird als npm-Paket ausgeliefert, so dass Sie sie einfach installieren können, indem Sie diesen Befehl ausführen:

$ npm install --save-dev @aspnet/signalr

Nun sind wir bereit für den letzten Schritt der Anleitung: Wir erstellen eine neue Methode im Controller, die eine neue Nachricht empfängt, ein Ereignis an alle Clients sendet und unseren Client auf den Empfang dieses Ereignisses vorbereitet.

Da wir ein Ereignis nicht vom Hub selbst, sondern vom Controller aus senden müssen, müssen wir einen `IHubContext` in den Controller injizieren, indem wir ihn zum Konstruktor hinzufügen, wie Sie im Listing unten sehen können. Wir fügen auch eine neue Methode hinzu, die ein zuvor erstelltes Modell empfängt und ein Ereignis an alle verbundenen Clients sendet, indem wir `Clients.All` verwenden. So muss Ihr Controller jetzt aussehen:

using Microsoft.AspNetCore.Mvc;
  using Microsoft.AspNetCore.SignalR;
  using RealTimeAppCore.Models;
  using ResPlanner.Hubs;
  using System;
  using System.Collections.Generic;
  using System.Threading.Tasks;

  namespace RealTimeAppCore.Controllers {
      [Route("api/[controller]")]
      public class MessagesController : Controller {
          ///
          /// The context
          ///

          private readonly IHubContext _hubContext;
          public MessagesController(IHubContext hubContext) {
              _hubContext = hubContext;
          }

          [HttpGet("[action]")]
          public IActionResult GetMessages() {
              List messages = new List();

              var result = new {
                  messages
              };

              return Ok(result);
          }
          [HttpPost("[action]")]
          public async Task SendMessage([FromBody]Message message) {
              await _hubContext.Clients.All.SendAsync("RefreshEvent", Json(new {
                  text = message.Text,
                  date = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss")
              }));
          }
      }
  }

Im Grunde sind dies alles Änderungen für die Serverseite. Um eine Verbindung herzustellen und auf die Server-Ereignisse zu hören, sollten wir auf der Client-Seite folgendes tun:

  1. innerhalb der `initSignalR()` Methode:
    1. Erstellen Sie eine neue Verbindung zum Hub mit den folgenden Zeilen:
      this.connection = new HubConnectionBuilder()
        .withUrl(window.location.origin + '/signalr-hub')
        .configureLogging(LogLevel.Information)
        .build();
        …
        this.connectToSignalR();
      
    2. konfiguriert die Wiederverbindung nach 5 Sekunden, wenn die Hub-Verbindung geschlossen wird:
      this.connection.onclose(() => {
            this.connectToSignalR();
        })
      
    3. auf das `RefreshEvent`-Ereignis des Servers warten. Sobald es empfangen wird, sollte die Liste der Nachrichten mit neuen Daten aktualisiert werden:
      this.connection.on('RefreshEvent', (data) => {
            this.messages.push({ text: data.value.text, date: data.value.date })
            this.$store.getters.needReloadPage
        })
      
  2. fügen Sie die Methode `connectToSignalR` hinzu, in der wir tatsächlich eine Verbindung herstellen:
    connectToSignalR() {
          this.connection.start().catch(err => {
              console.error('Failed to connect with hub', err)
              return new Promise((resolve, reject) =>
              setTimeout(() => this.connectToSignalR().then(resolve).catch(reject), 5000))
          })
      }
    

Außerdem sollten wir eine neue Methode hinzufügen, um eine Nachricht an den Server zu senden. Zunächst fügen wir ein Formular für die Benutzereingabe nach der Vorlage mit einem Eingabefeld und einer Schaltfläche hinzu, etwa so:


  <section class="form">
      <div class="field">
          <div class="control">
              <input v-model="form.textMessage" class="message-input" type="text" placeholder="Type a message here">
              <button class="dark-bg text-white submit-button" @click.prevent="sendMessage">Submit</button>
          </div>
      <</div>
  </section>

Dann binden wir den Wert des Formulareingabeelements an die Vue-Instanzdaten, indem wir die v-model-Direktive verwenden, so dass die Daten wie folgt aussehen sollten:

data() {
      return {
          messages: [],
          form: {
              textMessage: ''
          }
      }
  },

Schließlich fügen wir die `sendMessage()`-Methode hinzu, die aufgerufen werden soll, nachdem der Benutzer auf die Schaltfläche geklickt hat, und die die `SendMessage`-Methode des Controllers aufrufen soll. Nach all diesen Änderungen sollte Ihre „home-page.vue“-Datei also Folgendes enthalten:


  <template>
      <div>
          <h1>Simple chat</h1>

          <div v-if="!messages" class="text-center">
              <p><em>Loading...</em></p>
              <h1><icon icon="spinner" pulse /></h1>
          </div>

          <template v-if="messages">
              <div v-for="(message, index) in messages" :key="index">
                  <p><em>Message at {{ message.date }}:</em> '{{ message.text }}'</p>
              </div>
          </template>
          <section class="form">
              <div class="field">
                  <div class="control">
                      <input v-model="form.textMessage" class="message-input" type="text" placeholder="Type a message here">
                     <button class="dark-bg text-white submit-button" @click.prevent="sendMessage">Submit</button>
                  </div>
              <</div>
          </section>
      </div>
  </template>

  <script>
      import { HubConnectionBuilder, LogLevel } from '@aspnet/signalr'
      export default { 
          data() {
              return {
                  messages: [],
                  form: {
                      textMessage: ''
                  }
              }
          },

          methods: {
              async loadMessages() {
                  try {
                      let response = await this.$http.get(`/api/messages/getMessages`)
                      console.log(response.data.messages)
                      this.messages = response.data.messages
                  } catch (err) {
                      window.alert(err)
                      console.log(err)
                  }
              },
              initSignalR() {
                  this.connection = new HubConnectionBuilder()
                  .withUrl(window.location.origin + '/signalr-hub')
                  .configureLogging(LogLevel.Information)
                  .build();
                  this.connection.onclose(() => {
                      this.connectToSignalR();
                  })
                  this.connectToSignalR();
                  this.connection.on('RefreshEvent', (data) => {
                      this.messages.push({ text: data.value.text, date: data.value.date })
                      this.$store.getters.needReloadPage
                  })
              },
              connectToSignalR() {
                  this.connection.start().catch(err => {
                      console.error('Failed to connect with hub', err)
                      return new Promise((resolve, reject) =>
                      setTimeout(() => this.connectToSignalR().then(resolve).catch(reject), 5000))
                  })
              },
              closeConnectionSR() {
                  if (!this.connection) return;
                  this.connection.off('RefreshEvent');
                  this.connection = null;
              },
              sendMessage() {
                  this.$http.post('api/Messages/SendMessage', {
                      text: this.form.textMessage
                  })
                  .then(request => {
                      console.log(request)
                      if (request.status = 200) {
                          this.form.textMessage = '';
                      }
                  });
              }
          },

          async created() {
              this.initSignalR()
          }
      }
  </script>

  <style> 
      .submit-button {
          padding: .5rem .75rem;
          margin-left: 10px;
          border: 1px solid #dee2e6;
          border-radius: 10px;
          width: 150px;
      }
      .message-input {
          padding: .5rem .75rem;
          color: #007bff;
          border: 1px solid #dee2e6;
          border-radius: 10px;
          width: 700px;
          height: 50px;
      }
  </style>

Jetzt können Sie unseren Demo-Chat testen! Versuchen Sie, zwei verschiedene Browserfenster zu öffnen und den Chat zu starten - Sie werden sehen, dass alle verfügbaren Clients die aktualisierten Daten anzeigen:

Fazit

In diesem Artikel haben wir versucht, alle allgemeinen Aspekte zu behandeln, wie man mit ASP.NET Core, Vue.js und SignalR arbeiten kann. Hier finden Sie auch einige Details darüber, was SignalR ist, welche Vorteile es bietet, einen kurzen Vergleich von ASP.NET SignalR und ASP.NET Core SignalR, und so weiter. Wir hoffen, dass dieser Artikel einige Ängste von Anfängern zerstreut und erfahreneren Entwicklern einen anderen Weg zur Arbeit mit diesen Technologien aufgezeigt hat. Wir glauben also, dass jeder hier etwas Interessantes und Neues finden kann.