Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to use SignalR for Asp Net Core 2.1 in order to send a message from a controller method which call is triggered from a test button in Angular.
The behavior I'd expect is that when I click the button, my service invokes the controller method, which sends the test message. Then, I will simply log the message.

I want to manage this in a service in order to avoid code duplication in all of the components.

I've read some examples like this question about using SignalR in a service (I've used the second solution) and this article and the official docs but even with applying these concepts it don't seems to work.
(So, or I'm absolutely applying them in a wrong way or there's still something missing but I can't find out what...)

The client connects to the Message Hub successfully and if I click the button, the method is getting hit but I don't get any message and instead I get this warning in the Chrome console:

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

Sending messages works fine, the issue is just with receiving them...

The question is: what am I doing wrong? Is the error on the back-end side or in the Angular side?


I share with you all of my code (the button and the service to call the controller method are not relevant since the call to the service goes fine):

> 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);
  }
}
like image 448
Deadpool Avatar asked May 30 '18 15:05

Deadpool


People also ask

How do I send client specific messages using SignalR?

We change the BroadcastChartData() method to accept connectionId as an additional parameter. This way, we can find the client using the connectionId and send a message just to that client. Additionally, we add a new GetConnectionId() method, which returns the connectionId of the client.

Can you use SignalR with angular?

Microsoft's @aspnet/signalr package makes it possible to leverage both Azure and Angular to create applications that utilize SignalR.

How do I send a message to a group in SignalR?

When user click on send button, the message to be posted to server side using signalR connection hub. Thus whenever you post any message after clicking the join group button, the message will appear to all the clients who has joined the group.


1 Answers

In signalr core 2.1 you can use strongly typed hubs to declare in an interface what actions can be called on the clients :

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);
}

in the controller :

    IHubContext<MessageHub, ITypedHubClient> _messageHubContext;

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

in the client :

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

If you don't use strongly typed hub, then to call the same method in the client it becomes :

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

In that case you can use the SendAsync method on the client proxy, it's first parameter is the name of the method you want to call.

Update : When we define a strongly typed hub with an interface, all interface methods must return a Task. With custom methods, signalr generates methods that call SendCoreAsync. That allow us to call these methods asynchronously.

If the return type of the interface methods is not a Task we get the error : All client proxy methods must return 'System.Threading.Tasks.Task'

like image 69
J.Loscos Avatar answered Oct 20 '22 16:10

J.Loscos