Reputation:
I am trying to send notification to My client(Angular) from .net core server(c#) using signalR. to notify client when the event is fired. But i am getting an error. Error: Error: Failed to invoke 'Send' due to an error on the server
Appcomponent.ts
ngOnInit() {
this._hubConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Information)
.withUrl("http://localhost:5000/message/Send", this.transportType)
.build();
}
form(){
this._hubConnection.on("Send", (data) => {
const received = `message: ${data}`;
console.log(received);
that.disabled = false;
});
this._hubConnection.start()
.then(() =>
this._hubConnection.invoke('Send', "hey")
.catch(function (err) {
console.error(err.toString());
})
)
.then(() =>
console.log('connected'))
.then(() => this._hubConnection.stop())
.then(() => console.log('Disconnected'))
.catch(e => console.error(e.message));
}
SignalR hub
public class DataHub : Hub
{
public async Task Send1(string message1, string Root)
{
await Clients.All.SendAsync("Send1", message1);
}
}
Controller
public class DataController : Controller
{
private IHubContext<DrawingHub> _hubContext;
public DataController(IHubContext<DataHub> hubContext)
{
_hubContext = hubContext;
}
private void OnProvideData(DataAndHeader data)
{
// server side code
_hubContext.Clients.All.SendAsync("Send", data);
}
}
Startup.cs
services.AddSignalR();
app.UseSignalR(routes =>
{
routes.MapHub<DrawingHub>("/message/Send");
});
Error logging
fail: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[8] Failed to invoke hub method 'Send'. System.IO.InvalidDataException: Invocation provides 1 argument(s) but target expects 0. at Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol.BindArguments(JArray args, IReadOnlyList`1 paramTypes) at Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol.ParseMessage(Utf8BufferTextReader textReader, IInvocationBinder binder)
I want to invoke the signalr connection to notify client after the server side code is executed in the event. Can someone please guide me. Thanks in Advance.
Upvotes: 2
Views: 30551
Reputation: 1401
In your startup.cs
, you should register signalR service and map your hub in the http request pipe, if you have not done that already.
startup.cs --> ConfigureServices()
needs to register the signalR service, e.g:
services.AddSignalR();
startup.cs --> Configure()
should contain your mappings, e.g:
app.UseSignalR(routes =>
{
routes.MapHub<YourHubClass>("/hub/test1");
routes.MapHub<YourHubClass2>("/hub/test2");
});
More options can be added, if needed. But I belive the defaults are good enough for now.
Your hub class can simply just inherit from : Hub for a test to work. But to show some consol output, you can override virtual Hub functions like this:
public class YourHubClass: Hub
{
public override async Task OnConnectedAsync()
{
Console.WriteLine(Context.ConnectionId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception ex)
{
Console.WriteLine(Context.ConnectionId);
await base.OnDisconnectedAsync(ex);
}
}
Depending on when/where in you back-end code you want to notify all clients (or specific clients) that are listening to you hub, you can simply emit the data you want to transfer by doing something like this.
var test1data = GetTest1Data();
await _hub.Clients.All.SendAsync("test1", test1data);
var test2data = GetTest1Data();
await _hub.Clients.All.SendAsync("test2", test2data);
Where _hub
is dependency injected in this scope. Just to clarify that:
private readonly IHubContext<YourHubClass> _hub;
public YourScopedConstructor(IHubContext<YourHubClass> hub)
{
_hub = hub ?? throw new ArgumentNullException(nameof(hub));
}
Now you can connect and listen to your hub. E.g:
public yourSubscribableSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
private _baseUrl: string = "your base url";
private _frontendHubConnection: HubConnection;
this._frontendHubConnection = new HubConnectionBuilder()
.withUrl(`${this._baseUrl}/hub/test1`) // or hub/test2
.configureLogging(LogLevel.Information)
.build();
this._frontendHubConnection
.start()
.then(() => console.log('SignalR - init'))
.catch(err => console.log('Error while starting connection: ' + err));
this._frontendHubConnection.on('test1', (data: any) => {
console.log(data);
this.yourSubscribableSubject.next(data);
});
And for any other component to get the data, just subscribe to yourSubscribableSubject
. Hopefully it helps.
Also, if you want to trigger the data to be sent from server to clients from the API endpoint, just move the scoped code i suggested from step 3.
public async Task<ActionResult> YourApiRoute()
{
try
{
var test1data = GetTest1Data();
await _hub.Clients.All.SendAsync("test1", test1data);
return Ok();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
Upvotes: 1
Reputation: 5574
The method name you call on the client must be the same as in your SignalR Hub (with the exception that lowercase methods are also allowed).
Since your Hub method is called Send1
and your client calls Send
, the method is not found. To fix the issue you can rename your Hub method to Send
or change your client code to call this._hubConnection.invoke('send1', "hey", "ho")
.
Here's what you do:
this._hubConnection.on("Send", ...
have the client listen for a server event called Send
.
this._hubConnection.invoke('Send', "hey")
invoke a server method called Send
. THIS DOES NOT EXIST! the server method is called Send1
!
public async Task Send1( string message1, string Root)
declare a method called Send1
. nowhere in your client you are you trying to invoke Send1
, only Send
(without 1
).
await Clients.All.SendAsync("Send1", message1);
if Send1
would be invoked, you'd try to publish a Send1
message to all clients. no client is listening for Send1
though, only for Send
.
Now look at your error message: Failed to invoke hub method 'Send'.
it's looking for a hub method Send
but it does not exist, because you called it Send1
instead.
Quick Fix: Simply rename all occurrences of Send1
to Send
. Because that's what you are trying to invoke on your client. In Addition, you also have to call it using 2 arguments, not one like currently, since the hub method has 2 parameters.
Upvotes: 3