Signalr (1.0.0-alpha2) Hubs - Can you add client functions after connection has been started? - signalr

Using Signalr (1.0.0-alpha2), I want to know if it is possible to add client functions after a connection has been started.
Say I create my connection and grab the proxy. Then I add some Server Fired client functions to the hub to do a few things. Then I start my connection. I then want to add some more Server Fired functions to my hub object. Is this possible?
var myHub= $.connection.myHub;
myHub.SomeClientFunction = function() {
alert("serverside called 'Clients.SomeClientFunction()'");
};
$.connection.hub.start()
.done(function() {
myHub.SomeNewClientFunction = function() {
alert("serverside called 'Clients.SomeNewClientFunction()'");
}
})
This example is not realistic, but I basically want to send my 'myHub' variable to a different object after the hub is started to subscribe to new events that the original code did not care for.
Real Life Example: A dashboard with a number of different hub events (new site visits, chat message, site error). I 'subscribe' after the connection has started and then pass my hub proxy to all of my different UI components to handle their specific 'message types'. Should I create separate Hubs for these or should I be able to add more Server Fired client functions on the fly?

Yes you can. Use the .on method.
Example:
myHub.on('somethingNew', function() {
alert("This was called after the connection started!");
});
If you want to remove it later on use the .off method.

I have the exact same situation. You might want to consider adding another layout of abstraction if you're trying to call it from multiple places.
Here's a preliminary version of what I've come up with (typescript).
I'll start with the usage. SignalRManager is my 'manager' class that abstracts my debuggingHub hub. I have a client method fooChanged that is triggered on the server.
Somewhere in the module that is using SignalR I just call the start method, which is not re-started if already started.
// ensure signalR is started
SignalRManager.start().done(() =>
{
$.connection.debuggingHub.server.init();
});
Your 'module' simply registers its callback through the manager class and whenever the SignalR client method is triggered your handler is called.
// handler for foo changed
SignalRManager.onFooChanged((guid: string) =>
{
if (this.currentSession().guid == guid)
{
alert('changed');
}
});
This is a simple version of SignalRManager that uses jQuery $.Callbacks to pass on the request to as many modules as you have. Of course you could use any mechanism you wanted, but this seems to be the simplest.
module RR
{
export class SignalRManager
{
// the original promise returned when calling hub.Start
static _start: JQueryPromise<any>;
private static _fooChangedCallback = $.Callbacks();
// add callback for 'fooChanged' callback
static onfooChanged(callback: (guid: string) => any)
{
SignalRManager._fooChangedCallback.add(callback);
}
static start(): JQueryPromise<any>
{
if (!SignalRManager._start)
{
// callback for fooChanged
$.connection.debuggingHub.client.fooChanged = (guid: string) =>
{
console.log('foo Changed ' + guid);
SignalRManager._fooChangedCallback.fire.apply(arguments);
};
// start hub and save the promise returned
SignalRManager._start = $.connection.hub.start().done(() =>
{
console.log('Signal R initialized');
});
}
return SignalRManager._start;
}
}
}
Note: there may be extra work involved to handle disconnections or connections lost.

Related

How do I reliably pull data from Meteor server collections to client collections when using an existing mongodb as MONGO_URL?

I know that there are several methods to share collections on both the client and server -- namely either in top level lib folder or publish/subscribe model -- but when I try either of these things when using mongodb running at localhost:27017 as my MONGO_URL, I am not reliably getting data on the client. Occasionally console.log(myCollection.findOne({})) will return expected data in the browser but most of the time it returns undefined.
//Client side code
Template.controls.onCreated(function controlsOnCreated() {
Meteor.subscribe("myEvents");
Events = new Mongo.Collection("events");
});
//Server side code
Meteor.startup(() => {
Events = new Mongo.Collection("events");
}
Meteor.publish('myEvents', function() {
console.log(Events.find());
return Events.find();
});
UPDATED CODE -- returns Events on server but not client:
//Client
Template.controls.onCreated(function controlsOnCreated() {
this.subscribe("myEvents");
});
//Server
if (Meteor.isServer) {
Meteor.publish("myEvents", function() {
return Events.find();
});
}
// /collections/events.js
Events = new Mongo.Collection("events");
UPDATE 2:
I am attempting to verify the publication in the browser after the page has rendered, calling Events.findOne({}) in the Chrome dev tools console.
on your client:
Template.controls.onCreated(function controlsOnCreated() {
Meteor.subscribe("myEvents");
Events = new Mongo.Collection("events");
});
that is an odd place to define the Events variable. typically, you would put that line of code in a JS file common to both platform. e.g.
collections/events.js:
Events = new Mongo.Collection("events");
when that line runs on the server, it defines the mongo collection and creates a server-side reference to it. when it runs on the client, it creates a collection by that name in mini-mongo and creates a client-side reference to it.
you can write your onCreated like this (note "this" instead of "Meteor"):
Template.controls.onCreated(function() {
this.subscribe("myEvents");
});
you don't say where on the client you ran your console.log with the find(). if you did it in the onCreated(), that's too early. you're seeing the effects of a race condition. typically, you might use it in a helper:
Template.controls.helpers({
events() {
return Events.find({});
}
});
and display the data in the view:
{{#each event in events}}
{{event.name}}
{{/each}}
that helper will run reactively once the data from the publish shows up.

Is there a way to be notified when a client has unsubscribe from server sent events?

As I understand when a request to an event emitter on the server arrives, that request is never closed and you only need to res.write() every time you would like to send a message. However is there a way to be notified when the client that performed this request has left? Is there a property on the request object?
suppose I have the following route
app.get('/event',function(req,res){
//set response headers
//how do I check if req object is still active to send a message and perform other actions?
})
The basic sequence of events should be similar in other frameworks, but this example is Grails 3.3.
First set up endpoints to subscribe, and to close the connection.
def index() {
// handler for GET /api/subscribe
rx.stream { Observer observer ->
// This is the Grails event bus. background tasks,
// services and other controllers can post these
// events, CLIENT_HANGUP, SEND_MSG, which are
// just string constants.
eventBus.subscribe(CLIENT_HANGUP) {String msg ->
// Code to handle when the grails event bus
// posts CLIENT_HANGUP
// Do any side effects here, like update your counter
// Close the SSE connection
observer.onCompleted()
return
}
eventBus.subscribe(SEND_MSG) {String msg ->
// Send a Server Sent Event
observer.onNext(rx.respond(msg))
}
}
}
def disconnecting()
{
// handler for GET /api/disconnect
// Post the CLIENT_HANGUP event to the Grails event bus
notify(CLIENT_HANGUP, 'disconnect')
}
Now in the client, you need to arrange to GET /api/disconnect whenever your use-case requires it. Assuming you want to notice when someone navigates away from your page, you could register a function on window.onbeforeunload. This example is using Vue.js and Axios.
window.onbeforeunload = function (e) {
e.preventDefault()
Vue.$http({
method: 'get',
url: 'http://localhost:8080/api/disconnect'
})
.then((response) => { console.log(response) })
.catch(({error}) => { console.log(error) })
}
In the case of Servlet stacks like Grails, I found that I needed to do this even if I had no housekeeping of my own to do when the browser went away. Without it, page reloads were causing IOExceptions on the back end.

Are there 'private' server methods in Meteor?

Is there a way to stop a Client calling a Server Method from the browser console?
I gather from the Unofficial Meteor FAQ that there isn't. I just wanted to check if that's definitely the case - the FAQ isn't really specific. I mean are there no 'private' methods?
In meteor the 'methods' described by Meteor.methods can all be called from the client. In this sense there aren't private methods because the purpose of the RPC call is for the client to make the call.
If you want a 'private' method you could use an ordinary JavaScript method. If you define the method with var, it would only be accessible within the file, and cannot be called from the client.
var yourmethod = function() {
...
}
which is equivalent to:
function yourmethod() {
...
}
Or you can define it so any of your server script can use it:
yourmethod = function() {
....
}
If you mean you want a RPC method call that is accessible only from the javascript code, but not from the javascript console in chrome this isn't possible. This is because the idea behind meteor is all RPCs from the client are not trusted & there is no way to distinguish whether it came from the console or not. You can use meteor user authentication or Collection.allow or Collection.deny methods to prevent any unauthorized changes this way.
I made a private method by checking this.connection to be null.
Ref: http://docs.meteor.com/#/full/method_connection
Ex.
Meteor.methods({
'serverCallOnlyFunc': function() {
if (this.connection === null) {
//do something
} else {
throw(new Meteor.Error(500, 'Permission denied!'));
}
}
});

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