Je ne peux tout simplement pas comprendre comment établir une connexion signal à partir d'Angular.
Utilisation du didacticiel suivant sur https://docs.Microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc
J'ai ajouté un nouveau projet SignalR
2.4.0 à une solution .Net 4.6 existante dans vs2017.
J'ai également une application Angular 7
À laquelle j'ai ajouté le package SignalR
via npm install @aspnet/signalr
Maintenant, j'essaie de connecter une simple connexion entre le client et le serveur, mais je ne sais pas comment établir la connexion initiale.
Mon front end continue de lancer une exception:
core.js:15714 ERROR Error: Uncaught (in promise): Error: Cannot send data if the connection is not in the 'Connected' State.
Erreur: impossible d'envoyer des données si la connexion n'est pas à l'état "connecté".
Dans mon composant de recherche frontal, j'ai ajouté quelques champs pour les tests:
<mat-form-field>
<input matInput placeholder="message" [(ngModel)]="message">
</mat-form-field>
<button mat-button type="button" (click)="sendMessageToServer()"><span>Send</span></button>
<p *ngFor="let m of messages">{{m}}</p>
Et dans mon fichier ts:
// import other components/services here..
import { HubConnection, HubConnectionBuilder} from '@aspnet/signalr';
@Component({
selector: 'app-my-search',
templateUrl: './my-search.component.html',
styleUrls: ['./my-search.component.scss']
})
export class MySearchComponent implements OnInit {
public hubConnection: HubConnection;
public messages: string[] = [];
public message: string;
constructor() { }
ngOnInit() {
// SIGNALR MESSAGE HUB
let builder = new HubConnectionBuilder();
this.hubConnection = builder.withUrl('/SynBroadcastHub/BroadcastMessage').build(); // see startup.cs
this.hubConnection.on('notifyUser', (message) => {
this.messages.Push(message);
console.log(message);
});
this.hubConnection.start();
}
// signalr, send msg from client
sendMessageToServer() {
this.hubConnection.invoke('MessageToServer', this.message);
this.message = '';
}
}
et du côté c #, j'ai ajouté un fichier SignalR Hub Class (v2)
, BroadcastHub.cs
using Microsoft.AspNet.SignalR;
namespace SynBroadcastHub
{
public class BroadcastHub : Hub
{
/// Message to client
public void BroadcastMessage(string data)
{
Clients.Caller.notifyUser(data);
}
/// Message from client application; broadcast to all clients if requested.
public void MessageToServer(string data, bool notifyAllClients = false)
{
if (notifyAllClients)
{
Clients.All.NotifyAllClients(data);
}
}
}
}
ainsi qu'un fichier Startup.cs
:
using Microsoft.Owin;
using Microsoft.AspNet.SignalR;
using Owin;
[Assembly: OwinStartup(typeof(SynBroadcastHub.Startup))]
namespace SynBroadcastHub
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HubConfiguration cfg = new HubConfiguration();
app.MapSignalR<PersistentConnection>("BroadcastHub/BroadcastMessage");
app.MapSignalR(cfg);
app.MapSignalR();
//app.MapSignalR<NotifyHub>("notify"); ???
}
public override Task OnDisconnected(bool stopCalled)
{
return Clients.All.leave(Context.ConnectionId, System.DateTime.Now.ToString());
}
public override Task OnConnected()
{
return Clients.All.joined(Context.ConnectionId, DateTime.Now.ToString());
}
public override Task OnReconnected()
{
return Clients.All.rejoined(Context.ConnectionId, DateTime.Now.ToString());
}
}
}
Je viens de passer deux jours à essayer de comprendre cette même chose. Je l'ai finalement fait fonctionner et voici les quelques choses que j'ai dû faire:
1) Vous avez remarqué que l'utilisation du package @aspnet/signalr
Était incorrecte pour le framework .Net, et c'est correct. Vous avez besoin du package signalr
(npm install signalr
).
2) Il s'agit de la partie la plus critique de l'ensemble du processus. SignalR
a une dépendance sur jQuery
. Vous avez pour inclure jQuery avant y compris le script signalr. Dans le fichier angular.json
, Sous la section scripts
, vous devez inclure:
"./node_modules/jquery/dist/jquery.js", "./node_modules/signalr/jquery.signalR.js"
dans cet ordre exact. Au démarrage de votre projet, il chargera d'abord jQuery, puis le script signalR.
De nombreuses autres réponses de flux de stackover répondent à la question en réponse à cette erreur:
jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file
vous dire d'écrire import * as $ from "jquery"
dans le composant dans lequel vous souhaitez utiliser jQuery. Cependant, c'est [~ # ~] pas [~ # ~] correct de le faire. La raison en est que, selon cet angular à propos des scripts globaux , l'utilisation de la syntaxe import
l'inclura dans la charge module
et placez-le dans le fichier vendor.js
créé à partir de l'exécution d'une commande ng build
. La raison en est que jQuery sera d'abord chargé à partir de votre angular.json, puis signalR sera chargé , alors le module de vendor.js RELOAD jQuery et détachera tous les événements qui venaient d'être attachés à jQuery de signalR.
3) Puisque vous avez remarqué que vous utilisiez la version .Net Core de signalr, vous n'aurez pas accès à HubConnectionBuilder
lorsque vous essayez d'instancier une nouvelle HubConnection dans votre composant angular angulaire).
Au lieu de cela, lorsque le script signalr est exécuté, il attachera des événements supplémentaires au $
Dans votre code. Remarque: si vous obtenez des erreurs lors de la compilation ou de la compilation de vos fichiers ts, assurez-vous d'avoir inclus le @types/signalr
Et @types/jquery
De npm
Pour configurer une nouvelle connexion concentrateur, utilisez $.hubConnection("your-url-here/signalr")
. Cela s'attachera au hub de votre serveur lors de l'exécution. Remarque: J'ai stocké le résultat de ceci sous la forme d'une variable appelée hubConnection
dans mon angular
Sur votre code serveur (fichier .cs), où vous avez votre classe Hub, vous devrez ajouter au-dessus du nom de la classe: [HubName("YourHubName")]
. Donc, dans votre cas, votre fichier .cs ressemblerait à quelque chose comme ceci en haut:
[HubName("Broadcast")]
public class BroadcastHub : Hub
Vous devrez très probablement l'inclure en haut de votre fichier .cs: using Microsoft.AspNet.SignalR.Hubs;
Ensuite, dans votre composant Angular, vous configurez un proxy à attacher à ce concentrateur sur votre serveur.La ligne suivante après l'instanciation de votre nouveau hubConnection, écrivez:
this.hubConnection.createHubProxy("yourHubName");
.
Dans votre cas, this.hubConnection.createHubProxy("broadcast");
Après avoir créé votre proxy, vous pouvez ensuite attacher des écouteurs pour écouter les événements émis par le serveur, ou vous pouvez appeler des fonctions de serveur à partir de vos composants angular.
J'ai suivi ceci exemple ici pour apprendre comment configurer les événements d'appel et écouter les événements du serveur. Oui, c'est angular 2, mais les fonctions de signalr fonctionnent toujours de la même manière dans mon angular 7 app.
Réponse courte: utilisez proxy.on('eventname')
pour écouter les événements du serveur, et utilisez proxy.invoke('eventname')
pour appeler les fonctions sur votre Hub à partir de vos composants angular.
Enfin, quelques notes dans vos fichiers cs. Dans mon Startup.cs, la seule chose que j'ai pour mapper signalr est app.MapSignalR()
. Je ne suis pas entré dans beaucoup de détails concernant d'autres propriétés à définir comme vous l'avez fait, mais cela peut être une autre cause de problèmes?
Je faisais moi-même des recherches sur ce sujet et j'ai trouvé le paquet npm ng2-signal. Peut-être quelque chose à regarder en toi?
Êtes-vous sûr que le hubEndpoint est correct? Il semble que le hub fasse partie du routage angular (à en juger par la façon dont vous l'écrivez). Essayez de définir le chemin complet (ex. https: // www. myapp.com/SynBroadcastHub/BroadcastMessage )
Installez le package signalR
npm i @ aspnet/signalr --save
import { Component, OnInit } from '@angular/core';
import { HubConnection } from '@aspnet/signalr';
import * as signalR from '@aspnet/signalr';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
private hubConnection: HubConnection;
public ngOnInit() {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:50930/pushNotification").build();
this.hubConnection.start().then(() => {
console.log("connection started");
}).catch(err => console.log(err));
this.hubConnection.onclose(() => {
debugger;
setTimeout(() => {
debugger;
this.hubConnection.start().then(() => {
debugger;
console.log("connection started");
}).catch(err => console.log(err));
}, 5000);
});
this.hubConnection.on("clientMethodName", (data) => {
debugger;
console.log(data);
});
this.hubConnection.on("WelcomeMethodName", (data) => {
debugger;
console.log(data);
this.hubConnection.invoke("GetDataFromClient", "user id", data).catch(err => console.log(err));
});
}
public stopConnection() {
this.hubConnection.start().then(() => {
console.log("stopped");
}).catch(err => console.log(err));
}
}
API Web avec netcoreapp2.2
Installer Microsoft.AspNetCore.SignalR
Startup.cs
Le client s'exécute sur le port 4200 (" http: // localhost: 42 ").
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace SignalRWebApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddCors(option =>
{
option.AddPolicy("CorsPolicy", builder =>
builder.WithOrigins("http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<SignalHub>("/pushNotification");
});
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
SignalHub.cs
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SignalRWebApp
{
public class SignalHub : Hub
{
public void GetDataFromClient(string userId, string connectionId)
{
Clients.Client(connectionId).SendAsync("clientMethodName", $"Updated userid {userId}");
}
public override Task OnConnectedAsync()
{
var connectionId = Context.ConnectionId;
Clients.Client(connectionId).SendAsync("WelcomeMethodName", connectionId);
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
var connectionId = Context.ConnectionId;
return base.OnDisconnectedAsync(exception);
}
}
}
Maintenant, envoyez un message signalR comme dans l'exemple ci-dessous
[Route("api/[controller]")]
[ApiController]
[EnableCors("CorsPolicy")]
public class ValuesController : ControllerBase
{
private IHubContext<SignalHub> _hub;
public ValuesController(IHubContext<SignalHub> hub)
{
_hub = hub;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
_hub.Clients.All.SendAsync("clientMethodName", "get all called");
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{connectionId}")]
public ActionResult<string> Get(string connectionId)
{
_hub.Clients.Client(connectionId).SendAsync("clientMethodName", "get called");
return "value";
}
}
}