使用 SignalR Core 将消息从控制器方法发送到 Angular

Using SignalR Core to send messages from a Controller Method to Angular

我正在尝试将 SignalR 用于 Asp Net Core 2.1,以便从 Angular 中的测试按钮触发调用的控制器方法发送消息。
我期望的行为是当我单击按钮时,我的服务调用控制器方法,该方法发送测试消息。然后,我将简单地记录消息。

我想在服务中管理它以避免所有组件中的代码重复。

我读过一些示例,例如 (I've used the second solution) and this article and the official docs,但即使应用这些概念,它似乎也不起作用。
(所以,或者我绝对以错误的方式应用它们,或者仍然缺少一些东西,但我找不到什么......)

客户端成功连接到 Message Hub,如果我单击该按钮,该方法被点击但是我没有收到任何消息,而是在 Chrome 控制台中收到此警告:

Warning: No client method with the name 'SendAsync' found.

发送消息正常,问题在于接收消息...

问题是:我做错了什么?错误是在后端还是在Angular这边?


我与您分享我的所有代码(调用控制器方法的按钮和服务不相关,因为对服务的调用正常):

> Startup.cs

public void ConfigureServices(IServiceCollection services)
{
   //...
   services.AddSignalR();
}
//...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   //...
   app.UseSignalR(routes =>
   {
      //...
      routes.MapHub<MessageHub>("/messagehub");
      //...
   });
}

> MessageHub.cs

public class MessageHub : Hub<ITypedHubClient>
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

public interface ITypedHubClient
{
    Task SendAsync(string title, string name, string message);
}

> MessageController.cs

IHubContext<MessageHub, ITypedHubClient> _messageHubContext;

public MessageController(IHubContext<MessageHub, ITypedHubClient> messageHubContext)
{
    _messageHubContext = messageHubContext;
}

[HttpPost("Test")]
public async Task<IActionResult> Test()
{
    try
    {
        await _messageHubContext.Clients.All.SendAsync("ReceiveMessage","test", "test");

        return Ok(true);
    }
    catch (Exception e)
    {
        return BadRequest(e);
    }
}

> communication.service.ts

@Injectable()
export class CommunicationService {

  private _hubConnection: HubConnection | undefined;
  public async: any;
  message = '';
  messages: string[] = [];

  private newmessage = new Subject<string>();
  message$ = this.newmessage.asObservable();


  constructor() {
    this._hubConnection = new signalR.HubConnectionBuilder()
      .withUrl('/messagehub')
      //.configureLogging(signalR.LogLevel.Information)
      .configureLogging(signalR.LogLevel.Debug)
      .build();

    this._hubConnection.start().catch(err => console.error(err.toString()));

    this._hubConnection.on('SendMessage', (user: any, message:any) => {
      const received = `Received: ${message}`;
      //this.messages.push(received);
      this.newmessage.next(received);
      console.log("got something new...", received);
    });
  }

  clear() {
    this.newmessage.next("");
  }

  public sendMessage(): void {
    const data = `Sent: ${this.message}`;

    if (this._hubConnection) {
      this._hubConnection.invoke('SendMessage', 'AAA' ,data);
    }
    this.messages.push(data);
  }
}

在 signalr core 2.1 中,您可以使用强类型集线器在接口中声明可以在客户端上调用哪些操作:

public class MessageHub : Hub<ITypedHubClient>
{
    public async Task SendMessage(string title, string user, string message)
    {
        await Clients.All.SendMessageToClient(title, user, message);
    }
}
public interface ITypedHubClient
{
    Task SendMessageToClient(string title, string name, string message);
}

在控制器中:

    IHubContext<MessageHub, ITypedHubClient> _messageHubContext;

    public async Task<IActionResult> Test()
    {
        await _messageHubContext.Clients.All.SendMessageToClient("test", "test", "test");
        return Ok("ok");
    }

在客户端中:

_hubConnection.on('SendMessageToClient', (title, user, message) => {
    const received = `title: ${title}, name: ${user}, message: ${message}`;
    console.log(received);
});

如果您不使用强类型集线器,那么在客户端中调用相同的方法就变成了:

public class MessageHub : Hub
{
    public async Task SendMessage(string title, string user, string message)
    {
        await Clients.All.SendAsync("SendMessageToClient", title, user, message);
    }
}

在这种情况下,您可以在客户端代理上使用 SendAsync 方法,它的第一个参数是您要调用的方法的名称。

更新: 当我们用接口定义强类型集线器时,所有接口方法都必须 return 一个任务。使用自定义方法,signalr 生成调用 SendCoreAsync 的方法。这允许我们异步调用这些方法。

如果接口方法的 return 类型不是任务,我们会得到错误:所有客户端代理方法必须 return 'System.Threading.Tasks.Task'