17

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

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

SignalR在 vs2017 中的现有 .Net 4.6 解决方案中添加了一个新的 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

4 回答 4

35

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

1)您注意到@aspnet/signalr对于 .Net 框架使用该包是不正确的,这是正确的。您需要signalr包 ( npm install signalr)。

2)这是整个过程中最关键的部分。SignalR依赖于jQuery. 在包含信号器脚本之前,您必须包含 jQuery 。在文件中的部分下,您需要包括:angular.jsonscripts

"./node_modules/jquery/dist/jquery.js", "./node_modules/signalr/jquery.signalR.js"

按照那个确切的顺序。在您的项目启动时,它将首先加载 jQuery,然后是 signalR 脚本。

许多其他stackover flow回答回答了这个错误的问题:

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 并从 signalR 取消附加刚刚附加到 jQuery 的所有事件。

HubConnectionBuilder3) 由于您注意到您使用的是 .Net Core 版本的信号器,因此当您尝试在角度组件中实例化新的 HubConnection 时,您将无法访问。

相反,当信号器脚本被执行时,它会$在你的代码中附加额外的事件。注意:如果您在构建或编译时从 ts 文件中遇到错误,请确保您已包含来自 npm的@types/signalrand@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 组件中,您设置一个代理以附加到服务器上的该 Hub 实例化新的 hubConnection 后的下一行,编写:

this.hubConnection.createHubProxy("yourHubName");.

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

制作代理后,您可以附加侦听器以侦听从服务器发出的事件,或者您可以从 Angular 组件调用服务器函数。

我在这里按照这个例子来学习如何设置调用事件和监听服务器事件。是的,它是 Angular 2,但是 Signalr 的功能在我的 Angular 7 应用程序中仍然可以正常工作。

简短的回答:用于proxy.on('eventname')监听来自服务器的事件,并用于proxy.invoke('eventname')从您的角度组件调用集线器上的函数。

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

于 2019-03-07T17:25:49.253 回答
2
  • 角应用

安装 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));
  }
}
  • 带有netcoreapp2.2的 Web API

    安装Microsoft.AspNetCore.SignalR

启动.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

于 2020-02-04T18:25:35.660 回答
1

我自己只是在研究这个主题,发现了 npm package ng2-signal。也许有什么可以调查一下自己?

于 2019-01-23T10:36:55.377 回答
1

Are you sure that the hubEndpoint is correct? It seems like the hub is part of the angular routing (judging by the way you are writing it). Try to set the full path (ex. https://www.myapp.com/SynBroadcastHub/BroadcastMessage)

于 2019-02-04T08:36:45.173 回答