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);
}
}
In signalr core 2.1 you can use strongly typed hubs to declare in an interface what actions can be called on the clients :
in the controller :
in the client :
If you don't use strongly typed hub, then to call the same method in the client it becomes :
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'