Как подключить SignalR к приложению Angular 7
Я просто не могу понять, как сделать подключение сигнализатора из Angular.
Используя следующий учебник по адресу https://docs.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc
Я добавил новый SignalR
2.4.0 проект для существующего решения.Net 4.6 в vs2017.
У меня также есть Angular 7
приложение, к которому я добавил SignalR
пакет через npm install @aspnet/signalr
Сейчас я пытаюсь установить простое соединение между клиентом и сервером, но не могу понять, как установить первоначальное соединение.
Мой интерфейс продолжает выдавать исключение:
core.js:15714 ERROR Error: Uncaught (in promise): Error: Cannot send data if the connection is not in the 'Connected' State.
Ошибка: невозможно отправить данные, если соединение не находится в состоянии "Подключено".
В моем компоненте поиска переднего плана я добавил несколько полей для тестирования:
<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>
И в моем файле 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 = '';
}
}
и на стороне C#, я добавил 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);
}
}
}
}
также как и 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());
}
}
}
2 ответа
Я просто потратил два дня, пытаясь понять то же самое. Я наконец заставил его работать, и вот несколько вещей, которые мне пришлось сделать:
1) Вы отметили, что используя @aspnet/signalr
Пакет был неверным для.Net Framework, и это правильно. Вам нужно signalr
пакет (npm install signalr
).
2) Это самая важная часть всего процесса. SignalR
имеет зависимость от jQuery
, Вы должны включить jQuery, прежде чем включать скрипт-сигнализатор. в angular.json
файл, под scripts
В раздел необходимо включить:
"./node_modules/jquery/dist/jquery.js", "./node_modules/signalr/jquery.signalR.js"
в этом точном порядке. При запуске вашего проекта он сначала загрузит jQuery, а затем скрипт signalR.
Ответы на этот вопрос в ответах на этот вопрос на многие другие потоки стека
jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file
скажу тебе написать import * as $ from "jquery"
в компоненте, в котором вы хотите использовать jQuery. Однако это НЕ правильно. Причина в том, что согласно этой угловой статье о глобальных сценариях, использование import
синтаксис будет включать его в module
загрузить и положить в vendor.js
файл, который создается из запуска ng build
команда. Причина в том, что проблема в том, что сначала jQuery будет загружен из вашего angular.json, затем будет загружен signalR, затем модуль из vendor.js перезапустит jQuery и отсоединит все события, которые были только что присоединены к jQuery из signalR.
3) Поскольку вы заметили, что используете версию сигнализатора.Net Core, у вас не будет доступа к HubConnectionBuilder
при попытке создать новую HubConnection в вашем угловом компоненте.
Вместо этого, когда исполняется сценарий, он присоединяет дополнительные события к $
в вашем коде. Примечание: если вы получаете ошибки при сборке или во время компиляции из ваших файлов TS, убедитесь, что вы включили @types/signalr
а также @types/jquery
с нпм
Чтобы установить новое соединение с концентратором, используйте $.hubConnection("your-url-here/signalr")
, Он будет подключен к концентратору вашего сервера при запуске. Примечание: я сохранил результат этого как переменную с именем hubConnection
в моем угловом компоненте
В коде вашего сервера (файл.cs), где у вас есть класс Hub, вам нужно добавить над именем класса: [HubName("YourHubName")]
, Так что в вашем случае ваш файл.cs будет выглядеть примерно так:
[HubName("Broadcast")]
public class BroadcastHub : Hub
Скорее всего, вам придется включить это в начало вашего файла.cs: using Microsoft.AspNet.SignalR.Hubs;
Затем в вашем Angular Component вы настраиваете прокси для подключения к этому хабу на вашем сервере. В следующей строке после создания вашего нового hubConnection напишите:
this.hubConnection.createHubProxy("yourHubName");
,
В твоем случае, this.hubConnection.createHubProxy("broadcast");
После того, как вы создали свой прокси, вы можете присоединить слушателей для прослушивания событий, исходящих от сервера, или вы можете вызывать функции сервера из ваших угловых компонентов.
Я следовал этому примеру здесь, чтобы узнать, как настроить вызовы и прослушивание событий сервера. Да, это angular 2, но функции из signalr все еще работают одинаково в моем приложении angular 7.
Краткий ответ: используйте proxy.on('eventname')
прослушивать события с сервера и использовать proxy.invoke('eventname')
вызывать функции на вашем концентраторе из ваших угловых компонентов.
Наконец, несколько заметок в ваших файлах cs. В моем Startup.cs единственное, что у меня есть для отображения сигнализатора, это app.MapSignalR()
, Я не стал вдаваться в подробности относительно других свойств, которые вы должны установить, но это может быть еще одной причиной некоторых проблем?
- Угловое приложение
Установить пакет signalR
npm i @aspnet/signalr - сохранить
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 с netcoreapp2.2
Установите Microsoft.AspNetCore.SignalR
Startup.cs
Клиент работает на порту 4200 (" http://localhost:4200/").
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);
}
}
}
Теперь отправьте сообщение signalR, как в примере ниже.
[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";
}
}
}
Сам изучал эту тему и нашел пакет npm ng2-signal. Может быть, что-то посмотреть на себя?
Вы уверены, что hubEndpoint правильный? Кажется, что концентратор является частью угловой маршрутизации (судя по тому, как вы это пишете). Попробуйте установить полный путь (например, https://www.myapp.com/SynBroadcastHub/BroadcastMessage)