Why is my signalR proxy info not generated correctly? - asp.net

Ok so im using the latest version of signalR (2.10.0 at this moment in time) and I am adding a new hub to my application, I don't want to use it yet nor do I intend to in it's own right I simply want to use this hub to template some basic crud stuff so I don't have to rewrite it all the time.
The adding of this hub to my project causes signalR to fail and not correctly generate the proxy stuff on the client.
Can anyone explain why this would happen?
Here's my code ...
Existing hub:
public class NotificationHub : Hub { ... }
New hub:
public class Hub<T> : Hub { ... }
Client side code that breaks by adding new hub ...
$(function () {
var notifications = $.connection.notificationHub;
notifications.client.success = function (message) <--- js exception here
{ ... };
});
The js exception reads "cannot read property client of undefined".
EDIT:
Looking closer at the details in chromes tools I can see that the client side error is due to : "GET http://localhost/signalr/hubs 500 (Internal Server Error)"
Question is, how do I fix this as attaching the debugger does not let me handle / see where this error is occurring.
I'm guessing this is in the signalR code somewhere.
Is there an issue with generics in signalR ?

Ah ok ...
Since I never have any intention of actually creating a Hub only its sub types I made it abstract.
This appears to resolve the resolve the problem.
public abstract class Hub<T> : Hub { ... }

Related

Error: Scope IdentityServerApi not found in store

While trying to add routing to additional endpoints within my project built with IdentityServer4, I ran into the following issue:
Error: Scope IdentityServerApi not found in store.
I followed IdentityServer4's documentation for adding more API Endpoints exactly, so I wasn't sure why I was getting this error.
It turns out that in v4 IdentityServer is moving away from ApiResources and toward ApiScopes. For anyone just learning about IdentityServer4, it can be a little confusing navigating all of these changes, so hopefully this clarification will be helpful to someone else as well.
Solution: as opposed to what it says in the documentation included in my question, register your "LocalApi" as an ApiScope not an ApiResource. Following in an example of my setup for a local API:
Your config file:
// Add to your ApiScopes (not ApiResources)
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope(IdentityServerConstants.LocalApi.ScopeName)
};
// Then add to your client's allowed scopes:
// ... extraneous code omitted ...
AllowedScopes = new string[]
{
IdentityServerConstants.LocalApi.ScopeName
}
Your startup/program.cs:
// Register IdentityServer, then:
builder.Services.AddLocalApiAuthentication();
Your "LocalApi" controller class:
[ApiController]
[Authorize(LocalApi.PolicyName)]
[Route("api/[controller]/[action]")]
public class YourController : ControllerBase
{
// ...
}

How do I make a SignalR external reference to hub and not perform circular reference?

So, I'm trying to create a sample where there are the following components/features:
A hangfire server OWIN self-hosted from a Windows Service
SignalR notifications when jobs are completed
Github Project
I can get the tasks queued and performed, but I'm having a hard time sorting out how to then notify the clients (all currently, just until I get it working well) of when the task/job is completed.
My current issue is that I want the SignalR hub to be located in the "core" library SampleCore, but I don't see how to "register it" when starting the webapp SampleWeb. One way I've gotten around that is to create a hub class NotificationHubProxy that inherits the actual hub and that works fine for simple stuff (sending messages from one client to all).
In NotifyTaskComplete, I believe I can get the hub context and then send the message like so:
private void NotifyTaskComplete(int taskId)
{
try
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
if (hubContext != null)
{
hubContext.Clients.All.sendMessage(string.Format("Task {0} completed.", taskId));
}
}
catch (Exception ex)
{
}
}
BUT, I can't do that if NotificationHubProxy is the class being used as it's part of the SampleWeb library and referencing it from SampleCore would lead to a circular reference.
I know the major issue is the hub in the external assembly, but I can't for the life of me find a relevant sample that's using SignalR or MVC5 or setup in this particular way.
Any ideas?
So, the solution was to do the following two things:
I had to use the SignalR .NET client from the SampleCore assembly to create a HubConnection, to create a HubProxy to "NotificationHub" and use that to Invoke the "SendMessage" method - like so:
private void NotifyTaskComplete(string hostUrl, int taskId)
{
var hubConnection = new HubConnection(hostUrl);
var hub = hubConnection.CreateHubProxy("NotificationHub");
hubConnection.Start().Wait();
hub.Invoke("SendMessage", taskId.ToString()).Wait();
}
BUT, as part of creating that HubConnection - I needed to know the url to the OWIN instance. I decided to pass that a parameter to the task, retrieving it like:
private string GetHostAddress()
{
var request = this.HttpContext.Request;
return string.Format("{0}://{1}", request.Url.Scheme, request.Url.Authority);
}
The solution to having a Hub located in an external assembly is that the assembly needs to be loaded before the SignalR routing is setup, like so:
AppDomain.CurrentDomain.Load(typeof(SampleCore.NotificationHub).Assembly.FullName);
app.MapSignalR();
This solution for this part came from here.

signalR script reference?

All the signalR examples shows
<script src="/signalr/hubs" type="text/javascript"></script>
without explainning enough what is it used for .
For the Html page it is a location of folder . and there is no script there.
Why and how this line is being used ?
The /signalr/hubs page is a JavaScript file that is auto generated by SignalR which contains generated hub proxies for every hub in your SignalR project.
For instance (got this from http://shootr.signalr.net/signalr/hubs) lets take a look at the code snippet.
proxies.h = this.createHubProxy('h');
proxies.h.client = { };
proxies.h.server = {
changeViewport: function (viewportWidth, viewportHeight) {
return proxies.h.invoke.apply(proxies.h, $.merge(["changeViewport"], $.makeArray(arguments)));
},
fire: function () {
return proxies.h.invoke.apply(proxies.h, $.merge(["fire"], $.makeArray(arguments)));
},
...
"this" refers to the hub connection, or rather $.connection.hub. So we're essentially saying proxies.h = $.connection.hub.createHubProxy('h');
Now 'h' is a hub on the server See https://github.com/NTaylorMullen/ShootR/blob/master/ShootR/ShootR/Server/GameHub.cs, more specifically look at the hub name attribute.
Next we have proxies.h.client; this is the endpoint for where users are able to declare their client side functions that will be invoked from the server.
We then have proxies.h.server, this references all the public hub methods on the GameHub.cs. So some of the functions are changeViewport, and fire. This code is auto generated on the server to represent each of your hubs.
Lastly at the end of the signalr/hubs file we extend all of the dynamically created hubs onto the $.connection object so users can then access them via
var myGameHub = $.connection.h;
Hope this helps!

Get full URL inside SignalR hub

I'm developing an user tracking solution using SignalR, as a fun project to learn SignalR, for ASP.NET MVC applications.
Currently i can track logged users and how long are they on a specific page. If they move to another page i track that also and the timer that SignalR is updating resets... Many other features are implemented or partially implemented.
The problem i'm facing is how to get the full url Controller/Action/Parameters
inside SignalR hub?
When i use HttpContext.Current.Request.Url the url is always /signalr/connect.
NOTE:
var hub = $.connection.myHub;
$.connection.hub.start();
is in the _Layout.cshtml.
UPDATE:
I've tried to use
var location = '#HttpContext.Current.Request.Url';
var hub = $.connection.myHub;
$.connection.hub.start().done(function () {
hub.setLocation(location);
});
And the location is passed correctly but I need it on the Connect() task not later.
Is it possible to do this?
UPDATE 2:
This approach doesn't work
var hub = $.connection.myHub;
$.connection.hub.start(function(){hub.setLocation(location)});
as the Connect() is called before.
In my hub i have several methods but i would like pass a value (in my case a location) to the Connect(), is that possible?
public class MyHub : Hub, IDisconnect, IConnected
{
public Task Connect()
{
//do stuff here
//and i would like to have the **location** value
}
public Task Disconnect()
{
//do stuff here
}
}
Update 3
Use QueryString to pass data before the Connect() occurs.
var location = '#HttpContext.Current.Request.Url';
var hub = $.connection.myHub;
$.connection.hub.qs = "location= + location;
$.connection.hub.start();
Passing data like your location value to Connect() is possible via a querystring parameter: SignalR: How to send data to IConnected.Connect()
Using query-string is not very secure, cause a hacker can forge JS code and send you wrong location breaking whatever logic you have behind it.
You can try to get this from owin-enviromment variables
var underlyingHttpContext =
Context.Request.Environment[typeof(HttpContextBase).FullName] as HttpContextBase;
Then extract whatever you need.
It will work on IIS, for non-IIS hosting look for other OWIN stuff https://github.com/aspnet/AspNetKatana/wiki/OWIN-Keys
You could pass it from your client js call to your hub as a parameter.

How to pass message between client & server over the web using SignalR

I heard SignalR is a good messaging library. I got some code for SignalR but I am not able to understand how it works.
JS
var hooking;
$(function() {
hooking = $.connection.hooking;
hooking.removeLead = function(ref) {
$("lead" + ref).remove();
};
$.connection.hub.start();
});
C#
// Hooking.cs (placed in application root)
public class Hooking : Hub
{
public void Submit(string jsonString)
{
var serializer = new JavaScriptSerializer();
var json = serializer.Deserialize<HookingLeadResult>(jsonString);
Clients.removeLead(json.Ref); // Remove lead from client hooking windows
// update lead gen
}
}
I have questions about the above code.
What does hooking mean ins $.connection.hooking;
Where is removeLead in hooking.removeLead
What will this do $.connection.hub.start(); ? What does it start? Which method it will invoke at the server side?
Who & how Submit method will be called at the server side? how to pass data from client side to server side. If possible please give me a url for good start for SignalR library.
The Javascript function hooking.removeLead will be invoked whenever you call Clients.removeLead(). All the bindings are done dynamically, between Javascript to C# and between C# and Javascript.
$.connection.hub.start() is actually the connect function. It will connect your client to the server. No messages can be sent or received until you do. The start function allows you to define a callback to be called when it's done connecting.
The Submit method at the server will be called whenever you do a hooking.submit(json) call on your client. For instance, as a result of the user filling in some form and clicking a button.
I recommend starting with the SignalR official wiki: http://www.asp.net/signalr

Resources