如何使用Angular 7应用程序连接SignalR

时间:2019-01-21 20:54:07

标签: c# angular signalr signalr-2

我根本无法弄清楚如何从Angular建立信号发送器连接。

https://docs.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc上使用以下教程

我在vs2017中的现有.Net 4.6解决方案中添加了一个新的SignalR 2.4.0项目。

我还有一个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());
        }
    }
}

4 个答案:

答案 0 :(得分:4)

我花了两天的时间试图弄清楚这件事。我终于让它工作了,这些是我必须要做的几件事:

1)您注意到使用@aspnet/signalr包对于.Net框架是不正确的,这是正确的。您需要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脚本。

许多其他stackover流回答了该问题,以回答此错误:

jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file

告诉您要在其中使用jQuery的组件中编写import * as $ from "jquery"。但是,这样做是正确的。原因是,根据这篇有角度的文章about global scripts,使用import语法会将其包括在module负载中,并将其放入通过运行创建的vendor.js文件中ng build命令。这是一个问题的原因是因为jQuery将首先从您的angular.json中加载,然后将signalR加载,然后vendor.js中的模块将重新加载jQuery并从signalR取消附加刚刚附加到jQuery的所有事件。

3)既然您已经注意到使用的是.Net Core版本的signalr,那么在实例化角度组件中的新HubConnection时将无法访问HubConnectionBuilder

相反,执行Signalr脚本时,它将在代码中将附加事件附加到$注意:如果您从ts文件中在构建或编译时出错,请确保已包含npm中的@types/signalr@types/jquery

要建立新的集线器连接,请使用$.hubConnection("your-url-here/signalr")。运行时,它将连接到服务器的集线器。 注意:我将结果作为变量hubConnection存储在我的角度分量中

在具有Hub类的服务器代码(.cs文件)上,您需要在类名称上方添加:[HubName("YourHubName")]。因此,在您的情况下,您的.cs文件在顶部看起来像这样:

[HubName("Broadcast")]    
public class BroadcastHub : Hub

您很可能必须在.cs文件顶部添加以下内容: using Microsoft.AspNet.SignalR.Hubs;

然后在Angular Component中设置代理以附加到服务器上的该Hub  实例化新的hubConnection后的下一行,写:

this.hubConnection.createHubProxy("yourHubName");

在您的情况下,this.hubConnection.createHubProxy("broadcast");

创建代理后,可以附加侦听器以侦听服务器发出的事件,或者可以从角度组件调用服务器功能。

我在这里example后面学习了如何设置呼叫事件和监听服务器事件。是的,它是角度2,但是在我的角度7应用程序中,信号器的所有功能仍然相同。

简短答案:使用proxy.on('eventname') 收听服务器上的事件,并使用proxy.invoke('eventname') 呼叫从中心角度调用集线器上的功能组件。

最后,cs文件中有一些注释。在我的Startup.cs中,我唯一需要映射信号器的是app.MapSignalR()。我没有像您一样详细设置其他属性,但这可能是引起某些问题的另一个原因?

答案 1 :(得分:2)

我自己才研究此主题,并找到了npm包ng2-signal。也许有些事要看你自己?

答案 2 :(得分:1)

您确定hubEndpoint是正确的吗?轮毂似乎是角度布线的一部分(通过编写方式来判断)。 尝试设置完整路径(例如https://www.myapp.com/SynBroadcastHub/BroadcastMessage

答案 3 :(得分:1)

  • Angular应用

安装signalR软件包

npm我@ 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));
  }
}
  • 具有 netcoreapp2.2

    的Web API

    安装 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";
        }
    }
}

Github