SignalR Error when renaming hub - asp.net

I am encountering an issue when attempting to rename a SignalR hub. Please note that I'm not using generated proxies with SignalR.
I have a SignalR Hub that was previously defined as:
[HubName("WidgetHub")
public WidgetHub : Hub
{
...
}
Creating the proxy on the client is done using this code:
this.proxy = connection.createHubProxy('WidgetHub');
this.proxy.on('WidgetUpdated', function() {
$rootScope.$emit('refreshWidget');
});
return connection.start().then(function(connectionObj) {
return connectionObj;
}, function(error) {
console.log(error)
return error.message;
});
};
This is working correctly, however, I now want to rename the hub. I figured I could easily do this by specifying a new name in the [HubName()] attribute:
[HubName("CloudHub")
public WidgetHub : Hub
{
...
}
And the updating the client proxy creation to reference the new hub name:
this.proxy = connection.createHubProxy('CloudHub');
However, when I do this, the client connection errors out with the following message being return:
Error: SignalR: Connection must be started before data can be sent.
Call .start() before .send()
I suspected that I have a caching issue somewhere, because even after specifying the new hub name, if I reference the old hub name, it works correctly.
I have tried cleaning up IIS and all the browser caches, but to no avail. Is there something else that I'm missing here on why changing the HubName attribute is not working for me?

try to make your hubName as follows
[HubName("cloudHub")]
public WidgetHub : Hub
{
...
}
and create proxy like
this.proxy = connection.createHubProxy('cloudHub');
then try to start hub by calling method Start();
$.connection.hub.start().done(function () {
....
....
});

Related

SignalR needs to target specific games with Game ID and not all live games

I didnt think about this but this code is sending the game model to all clients. I need to use the GameID from this controller action and only target the clients watching that game. How do I do that?
Publish Controller Action
public UpdateGameResponse UpdateGame(int gameId)
{
...
var model = Game.Create(XDocument.Load(httpRequest.Files[0].InputStream)).Parse();
GlobalHost.ConnectionManager.GetHubContext<GameCastHub>().Clients.All.receiveUpdates(Newtonsoft.Json.JsonConvert.SerializeObject(model));
}
Hub
[HubName("gamecastHub")]
public class GameCastHub : Hub
{
}
Client
var connected = false;
var gamecastHub = $.connection.gamecastHub;
if (gamecastHub) {
gamecastHub.client.receiveUpdates = function (updates) {
console.log('New updates received');
processUpdates(updates);
};
connectLiveUpdates();
$.connection.hub.connectionSlow(function () {
console.log('Live updates connection running slow');
});
$.connection.hub.disconnected(function () {
connected = false;
console.log('Live updates disconnected');
setTimeout(connectLiveUpdates, 10000);
});
$.connection.hub.reconnecting(function () {
console.log('Live updates reconnecting...');
});
$.connection.hub.reconnected(function () {
connected = false;
console.log('Live updates reconnected');
});
}
I suggest using either the connection Id associated with each connection to the hub or creating groups.
Note: Each GameID must have its own connection to the hub in order to use the connection Id solution.
I prefer to use groups from personal experience but either way can be done.
To create a group in the hub you will need to create a method in your hub class.
public async void setGroup(string groupName){
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
Secondly, you will need a JS function on the client side to call the hub function.
$.connection.hub.invoke("setGroup", groupName).catch(err => console.error(err.toString()));
In your case, you can place your gameID as the groupname and then call GlobalHost.ConnectionManager.GetHubContext<GameCastHub>().Clients.Groups(gameID).receiveUpdates(Newtonsoft.Json.JsonConvert.SerializeObject(model));
To retrieve the connection Id:
var _connectionId = $.connection.hub.id;
Then send the connection Id to the server,
and proceed to using the call GlobalHost.ConnectionManager.GetHubContext<GameCastHub>().Clients.Clients.Client(_connectionId).receiveUpdates(Newtonsoft.Json.JsonConvert.SerializeObject(model)); to call that specific connection.

SignalR - unable to call server method from external JavaScript client

I am learning SignalR. Using a tutorial I managed to create a very simple ASP.NET MVC based SignalR Server. This is the code of the hub:
[HubName("echo")]
public class EchoHub : Hub
{
public void Say(string message)
{
Trace.WriteLine(message);
}
}
This is my Startup file:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
map.RunSignalR(hubConfiguration);
});
}
}
Next I created a HTML/JavaScript based client, that was in the same project as the server code. This client works correctly, when I debug the application, the Output window displays my message. This the code of the first client:
<script src="../Scripts/jquery-1.10.2.min.js"></script>
<script src="../Scripts/jquery.signalR-2.1.2.min.js"></script>
<script src="/signalr/hubs"></script>
<script>
$(function() {
var hubProxy = $.connection.echo;
$.connection.hub.logging = true;
$.connection.hub
.start()
.done(function () {
hubProxy.server.say('Hello SignalR');
});
})
</script>
The last thing I wanted to do is to create an client in separate project. I changed a little bit the code, mostly by adding the url's of the server. But this client don't work properly. This is the code of the client:
<script src="Scripts/jquery-1.6.4.min.js"></script>
<script src="Scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="http://localhost:51644/signalr/hubs"></script>
<script>
$(function() {
$.connection.hub.url = 'http://localhost:51644/signalr';
var hubProxy = $.connection.echo;
$.connection.hub.logging = true;
$.connection.hub
.start()
.done(function () {
hubProxy.server.say('Hello SignalR');
});
})
</script>
The JavaScript don't even enters the function in the done method after starting the connection. But in console on Chrome there are no errors or exceptions being thrown.
So the question is what am I doing wrong in the second client?
EDIT: I enabled and checked the SignalR client logs in Chrome console. The second client is stoping by step "SignalR: Negotiating with '/signalr/negotiate?clientProtocol=1.4&connectionData=%5B%5D'." No errors are returned, everything seems alright, but the client cannot go past this step.
EDIT2: According to the comment from JF Beaulieu the negotiate return 200 status code, but nothing else. After the negotiate there should be next steps, like invoking Say, but in the external client hangs on the negotiate. I paste here console outputs from the build in and external JavaScript client.
Build in client output:
External client output:
The second screenshot shows, that the client stops executing on the negotiate step.
I've also tried the solution of Mareq, but adding the jsonp attribute didn't help.
EDIT3: I add here a link to my Github repo with this project, the server is in the AspNetServer project and client in JavaScriptClient project, hope that helps:
https://github.com/RomanSuska/SignalRSandbox
I finally found the solution of my problem and it's stupid simple. The problem was with the SignalR JavaScript file version, on the server I used version 2.1.2 of SignalR and on the client I used 2.2.0. When I switched the version of the client to 2.1.2 everything started to work.
My case was due to my Hub having a constructor because I was trying to do Dependency Injection with Unity. After I removed the constructor, everything worked.
Add this argument to start:
$.connection.hub.start({ jsonp: true });
EDIT I hosted SignalR in console app and I used simple set configuration:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration { EnableJSONP = true });
}
}
and client code I used like this:
$(function () {
var hub = $.connection.serviceHub;
$.connection.hub.url = 'http://mareq.ddns.net:8087/signalr';
$.connection.hub.start({ jsonp: true });
...
});

SignalR: "The user identity cannot change during an active SignalR connection" error with windows auth website

I have an MVC 5 website running signalR 2.1.0 using Windows Authentication. Because I'm using windows auth login/logout is handled automatically by IIS. Occassionally I'm getting a 403 error saying "Unrecognized user identity. The user identity cannot change during an active SignalR connection." This doesn't happen all the time, and I can't seem to find a pattern to when it does and does not work. Has anyone else encountered this?
Here is the code on the view:
<script type="text/javascript">
$(document).ready(function() {
SignalRSetup();
});
function SignalRSetup() {
// Declare a proxy to reference the hub.
var hub = $.connection.tokenRequestHub;
// Create a function that the hub can call to broadcast messages.
hub.client.updateFromService = function(tokenRequestID, message, success) {
var msg = "Token Request ID {0} => {1}".format(tokenRequestID, message);
var notyType = (success) ? 'success' : 'error';
noty({ text: msg, type: notyType, timeout: 2000 });
if (success) {
refreshGrids();
}
};
$.connection.hub.start();//this is where it errors!
}
</script>
Any help would be greatly appreciated.
I think I fixed the issue by adding an [Authorize] attribute to my Hub class. It's only been a few hours, but my SignalR powered page is behaving much better.
What worked for me was to disable Anonymous Authentication in the IIS settings.
Another solution I found other than disable Anonymous Authentication is to add
GlobalHost.HubPipeline.RequireAuthentication();
to public void Configuration(IAppBuilder app) method in the Startup class.

SignalR Negotiate 404

I am using SignalR 2.0. Everything works fine when running locally on my VS 2012. But when I publish the site on IIS, it breaks. The site loads but one of the scripts returns 404 Not Found. The script is something like.
https://example.com/signalr/negotiate?xxx
This path doesn't exist indeed. The correct path should be:
https://example.com/private/signalr/negotiate?xxx
Note the part in bold.
Inside the WebSite (https://example.com/) I have another Application (https://example.com/private/). This one is using SignalR.
This seems like a bug in SignalR since the signalr/hubs path is accessible from my private site.
I had a similar problem.
Here is the documentation for configuring the /signalr URL.
However, my solution differed from the docs.
Instead of changing the standard app.MapSignalR(), I changed my client code to use /MyApp/signalr. Here is the code where "MyApp" is the virtual directory of my web application.
var connection = $.hubConnection('/MyApp/signalr', {useDefaultPath: false});
var changesHub = connection.createHubProxy('changesHub');
changesHub.on('userCountChanged', function (count) {
$('#user-count').text(count);
});
connection.start().done(function () {
console.log('Hub has started');
changesHub.invoke('subscribeToChanges', user.id);
});
I tried the other way around (change the MapSignalR to the /signalr path) but this did not work and the negotiation was still routed to /MyApp/signalr/negotiate.
I had the same problem, with an application running in the IIS Default Web Site.
All the Microsoft examples show the hub url with a starting \, and I had copied those examples. But this meant that the signalr routing was from the Default Web Site rather than the application. Removing the leading \ solved it.
So I used endpoints in Startup.cs like:
endpoints.MapHub<MyHub>("myHub");
and hub connections in Javascript like:
var connection = new signalR.HubConnectionBuilder().withUrl("myHub").build();
I had the same issue when web site with signalr is not running as root site. Below solution worked for me. instead of using /signalr, use ../signalr. it will work with any site name folder. no hardcoded name 'MyApp'
var connection = $.hubConnection('../signalr', {useDefaultPath: false});
Had the same issue. web sites running as virtual directories of the root site. For some reason prefixing with ../ as in ../signalr didn't work, but ./signalr did.
My sample code:
function initSR() {
// logs signalr messages
$.connection.hub.logging = true;
// Declare a proxy to reference the hub.
var chat = $.connection.myHub;
$.connection.hub.url = "./signalr";
$.connection.hub.start();
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (message) {
// Process Message, take action upon receipt
alert(message);
};
}
I had the same problem, it is all about CORS, you should add Host URL in CORS config in Startup.cs like this:
services.AddCors(option =>
{
option.AddPolicy("AutomationCors", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("YOUR LOCALHOST URL",
"YOUR HOST URL")
.AllowCredentials();
});
});
I faced the same problem. The mistake i was doing that i was calling the wrong endpoint url like i was mapping the Signal Url in Configure service like /notification but calling [API-Host]/api/notification. Removing the api from url and calling [API-Host]/notification fixed for me.
Probably you added MapSignalR() in your Application (https://example.com/private/).
If you want it on the root, then do the configuration on your WebSite (https://example.com/)
#styfle point me in the right direction the problem can be resolve in a more flexible way injecting BASE_URL (at least in angular 4)
import { Injectable, Inject } from '#angular/core';
import { HubConnection } from '#microsoft/signalr';
import * as signalR from '#microsoft/signalr';
import { Subject, Observable } from 'rxjs';
#Injectable()
export class SignalRService {
private hubConnection: HubConnection;
private message: Subject<any>;
constructor(#Inject('BASE_URL') private baseUrl: string) {
}
public connect() {
this.message = new Subject<any>();
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.baseUrl+"notification-hub")
.withAutomaticReconnect()
.build();
// ...
}
}

The On event on the SignalR Client Hub does not get called

I seem to have an issue with SignalR's JS Client Hub.
The problem is that the 'on' handler does not seem to work - it generates no error but doesn't receive any signals sent by the server.
The code below shows an extract where I call the server (using the invoke) which works fine - then on the server I call back to acceptHubData which should be picked up on the client but isn't.
My objective is when navigating to pages that each page will open a connection to a specific hub and releases this connection when the user moves to another page!!
EDIT: using the following code snippet works but I wonder why the code further below using the 'on' event doesn't work!
var superHub = $.connection.mySuperHub;
superHub.client.acceptHubData = function (data) {
$('<li>hello there' + data + '</li>').prependTo($('#ul1'))
}
$.connection.hub.start().done(function () {
$('<li>done phase 1</li>').prependTo($('#ul1'))
});
Any help would be much appreciated!
This is the client code (in js)
$(document).ready(function () {
var myHub;
try {
var connection = $.hubConnection();
connection.start().done(function () {
myHub = connection.createHubProxy("mySuperHub");
myHub.on('acceptHubData', function (data) {
alert(data); // THIS IS NOT CALLED!
});
myHub.invoke('AcceptSignal', "hello from the client2");
});
}
catch (e) {
alert(e.message);
}
});
This is the Server code:
[HubName("mySuperHub")]
public class MyHub : Hub
{
private readonly HubEngine _hubEngine;
public MyHub() : this(HubEngine.Instance) { }
public MyHub(HubEngine hubEngine)
{
_hubEngine = hubEngine;
}
public void AcceptSignal(string msg)
{
Clients.Caller.acceptHubData("hi");
Clients.All.acceptHubData("hi");
}
}
You can still use the on method to add events for JS client hub method calls in the latest version of SignalR, but if you do not add any event listeners to a hubProxy before calling hubConnection.start(), you will not be subscribed to the hub. SignalR subscribes to the hubs you have event handlers for when the hubConnection starts. If you are not subscribed to your hub, adding any events to that hub after start() won't work.
If you add at least one event listener to the hub before start(), even if it doesn't do anything, you can then add any additional event handlers you want to the hub using on after start() and your handlers will be called.
It doesn't matter if you add an event using hubProxy.on('eventName', function (... or autogeneratedHubProxy.client.eventName = function (... before you call start(), but only on will successfully add event listeners after start() is called.
Not sure which version of SignalR you are using, but I have had more success using the following syntax on my server:
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.acceptHubData("hello");
and on my clients:
myHub.client.acceptHubData = function (data) {
console.log(data);
}

Resources