SignalR call asp.net method for all clients? - javascript

I made a simple chat app. I am impressed by signalR.
My question is, does this technology give us an ability to call asp.net server side methods (for example ones in aspx.cs backend) ?
Let me explain you what I am talking about. I have a WCF service which retrieves data from database and sends it to client. On client side, there is a method getCars(), which is called on page_Load and shows all cars to the user.
But for example if other client changes the cars and sends changes to WCF service, service updates new data to database. So is there an option to get this changes shown to other people with SignalR..
Like normal SignalR sendMessage works.. Some client sends message to server, and server adds message to all clients, is there a possibility to to the same with ASP.NET method for ALL clients with signalR (when one client invokes changes in database to call that method again fro ALL clients)?
I heard this is done with Duplex with WCF but I am interested if it is possible with SignalR?

Yes you can, since you are using the JavaScript client, when the client do a change you call the server
commandHubProxy.invoke('InformClients').done(function (result) {
//Call Succeeded
}).fail(function (error) {
//log error
});
And on the server, the method informs the rest of the clients
public void InformClients(string text)
{
Clients.Others.DataUpdated();
}
Then on the other clients you can do an Ajax request to refresh the data
commandHubProxy.on('DataUpdated', function () {
//Refresh the data or inform the user
});

Related

Browser Notification Push Service: How to specify push server?

I'm trying to set up push notifications for a browser web app.
I found the following code can be used to subscribe to a push server. Running a simple push server locally seems to work fine, but
Q: I was wondering how I would specify the push server if it wasn't local?
I mean, how would the browser know where to subscribe to just by looking at the public key of the server?
function createNotificationSubscription(pushServerPublicKey) {
return navigator.serviceWorker.ready.then(
(serviceWorker) => {
return serviceWorker.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: pushServerPublicKey
})
.then((subscription) => {
console.log("✅ User is subscribed.", subscription);
return subscription;
});
}
);
}
References:
https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe
how do I specify the push server [...]?
You don't.
if it wasn't local?
There is a misunderstanding.
The push server will always be a fixed server given by the browser vendor.
It basically works like this:
You subscribe()
This request goes to the notification server of the browser vendor (mozilla, google, ...).
That server will create a so called push endpoint - just a fancy word for URL. This URL serves as a mailbox: If someone sends a message to it, the push server (still being the server of the browser vendor) will forward it to the browser (client).
The server will return the push endpoint and some other information as a result of your initial subscribe().
Currently only the push server and your web app know about the push endpoint....
So your web app needs to the whole result of subscribe to the application server (which is your server).
Now your server is able to use that information to send messages to the push server. The push server will then forward the message to the client.
Here's also a flow chart depicting the flow with a little more detail in regard of the different players:
Push service: Service running on the browser vendor's server
Subscription information: URL of the push endpoint along with some keys.

SignalR Javascript Client - Invoke method work but promise didn't work (???)

Currently I'm studying SignalR for ASP.NET Core web project, I use ASP.NET Core as a server for SignalR and web clients use SignalR Javascript Client.
I'm making a chat app that will return some message when the client invoke function from the server.
Here's my Javascript client function:
this.hub.invoke(method, data)
.then((res)=>{ alert(res);})
.catch(err=> alert(err));
The server method:
public async Task<IActionResult> joinRoom(string roomId)
{
Console.WriteLine(roomId);
return new OkObjectResult("Ok");
}
Problem:
Invocation success, Console on the server side wrote the roomId
However, the client side's Promise.then/Promise.catch didn't work.
When I close the server, there're multiple error on client side that said Invocation has been canceled due to the connection was closed.
My SignalR version is 1.0.3
Really, I can't find out what's the reason. I hope you can help me, please... I also tried with return a string on the server side but it didn't work too.
You can't use IActionResults with SignalR, it's not an http request, it's an RPC style invocation. Just return the object you want to get back or make the return type Task if you don't need to return anything to the client.
public async Task<string> joinRoom(string roomId)
{
Console.WriteLine(roomId);
return "Ok";
}

Rails - Call ajax request when database is updated

So I'm creating a basic jackpot betting site for fun and it allows users to put money into a pot and then based on the percentage of money each user puts in it randomly picks a winner. It's working fine at the moment but I feel like the way I'm updating the jackpot page with ajax is really bad. At the moment I have javascript that makes an ajax request each second to get the pot info (size of the pot, players, etc..). I feel like there is a much better way to do this. Is it possible to only make an ajax call when the database is updated? Thanks!
My javascript at the moment:
setInterval(update, 1000);
function update() {
getPotID();
$.ajax({
type: "POST",
url: "/jackpot/update/" + potID,
complete: function(response) {
$('.live-jackpot').html(response.responseText);
getPotInfo();
},
error: function(xhr, status,error) {
console.log("Error");
}
});
}
as said from 7urkm3n, ActionCable has great advantage for this functionality.
Right now you are writing Javascript code that is executed on the client side. Every user that will start a GET http request to your site, will load that javascript files. They will start performing POST request every second to your backend server.
ActionCable is a websocket preinstalled in Rails 5. This means that to configure the notifications with ActionCable and Rails 5, you already have installed everything in your app (if you are using rails 5), you just need to install Redis on your local machine for testing the app in development.
I am not an expert, my understanding is that you use a specific database called redis to store the information about the subscription. I quote a useful article
So PubSub is this concept that Redis and Postgres and a few other things supports, and basically you have this ability to have Channels and the ability to subscribe (and receive those messages) and publish to those channels and anyone listening will then receive those messages.
An example of this is a ChatroomChannel, you could have people publishing messages to the ChatroomChannel and anyone who was subscribed to the ChatroomChannel would receive the broadcast from the channel with the websocket.
This is something you are missing, this way you could only find which users are actually on the playing page and which users are just browsing around, based on this ActionCable creates a channel to communicate between server and client and then creates a subscription to distinguish users that are actually on the playing page and those that left and should not be anymore notified
I quote another useful article
Before we dive into some code, let's take a closer look at how Action Cable opens and maintains the WebSocket connection inside our Rails 5 application.
Action Cable uses the Rack socket hijacking API to take over control of connections from the application server.
For every instance of your application that spins up, an instance of Action Cable is created, using Rack to open and maintain a persistent connection, and using a channel mounted on a sub-URI of your main application to stream from certain areas of your application and broadcast to other areas.
so every user that connects, ActionCable creates a channel that uses a specific url localhost:3000/cable to communicate between server and client (browser)
Action Cable offers server-side code to broadcast certain content (think new messages or notifications) over the channel, to a subscriber. The subscriber is instantiated on the client-side with a handy JavaScript function that uses jQuery to append new content to the DOM.
This means that the server can call the client with parameters and change the page with jquery functions. For ex. appending a div, with a new message.
In my app https://sprachspiel.xyz I do the following in the MessagesController#create
ActionCable.server.broadcast 'messages',
message: message.content,
user: message.user.name,
chatroom_id: message.chatroom_id,
lastuser: chatroom.messages.last(2)[0].user.name
head :ok
so basically, I have my message in my controller and I can update the client by using the ActionCable.server.broadcast function
then in my asset pipeline file /app/assets/javascripts/channels/messages.js I have the following code that trigger the change in the browser adding the message
App.messages = App.cable.subscriptions.create('MessagesChannel', {
received: function(data) {
$('#messages').append(this.renderMessage(data));
},
renderMessage: function(data) {
return "<br><p> <strong>" + data.user + ": </strong>" + data.message + "</p>";
}
});
I build an app called https://sprachspiel.xyz that is an actioncable app, this is the github page for the project, on my portfolio you can read more info about my app, so please ask me anything, I'll be happy to look into it!
Good luck
Fabrizio

I can't call Accounts.findUserByEmail() server-side via Meteor.call

I'm just trying to verify if an Account exists with a particular email, however I learned that Accounts.findUserByEmail() only works server-side.
It would appear the repeatedly-suggested way would be to define a Meteor.method() and do all the work in there. Unfortunately I apparently have no idea what I'm doing because I'm getting an error that no one else has been getting.
component.js:
Meteor.call('confirm', email);
methods.js:
Meteor.methods({
'confirm': (email) => {
if (Accounts.findUserByEmail(email)) {
return;
}
}
});
All I get is this error:
Exception while simulating the effect of invoking 'confirm' TypeError: Accounts.findUserByEmail is not a function
Am I completely misunderstanding the dynamic of Meteor.methods + Meteor.call? Is it not actually server-side??
Currently using Meteor package, accounts-password#1.3.3
Meteor simulates method calls in the front-end too by running "stubs" of your methods. The idea is to have a better user experience because the UI is updated immediately before the server has responded. However this also means that if you run server-only code in Meteor methods, you have to make sure that the code is only run on the server:
Meteor.methods({
'confirm': (email) => {
if (Meteor.isServer && Accounts.findUserByEmail(email)) {
return;
}
}
});
Alternatively, you can place the above method definition in a file that is only loaded on the server, like any file on the /server-directory or (recommended) in /imports to a file that is only included by server code. Then you shouldn't need to use Meteor.isServer separately.
If your client-side code includes a method definition, it is treated as a stub, which means that it is run in a special mode that provides "optimistic UI" and its effects on data are undone once the actual server method returns its response to the client.
It could be worthwhile to implement different versions of (at least some of the) methods for the client and server, and to avoid including some of them on the client altogether.
If you choose to use the same function on both the client and the server, there are Meteor.isServer, Meteor.isClient and this.isSimulation (the latter is specifically for the methods), that allow you to execute some of the blocks only on the client/server.
Note that the code in your question does not do what you expect it to, and you do not check the method argument.
For this specific use case, you should probably only implement the method on the server (simply don't import its code in your client build):
Meteor.methods({
isEmailInSystem(email) {
check(email, String);
return !!Accounts.findUserByEmail(email);
}
});
You can read more about the method lifecycle in The Meteor Guide.
From the guide (gist, some details omitted):
Method simulation runs on the client - If we defined this Method in client and server code, as all Methods should be, a Method simulation is executed in the client that called it.
The client enters a special mode where it tracks all changes made to client-side collections, so that they can be rolled back later. When this step is complete, the user of your app sees their UI update instantly with the new content of the client-side database, but the server hasn’t received any data yet.
A method DDP message is sent to the server
Method runs on the server
Return value is sent to the client
Any DDP publications affected by the Method are updated
updated message sent to the client, data replaced with server result, Method callback fires
After the relevant data updates have been sent to the correct client, the server sends back the last message in the Method life cycle - the DDP updated message with the relevant Method ID. The client rolls back any changes to client side data made in the Method simulation in step 1, and replaces them with the actual changes sent from the server in step 5.
Lastly, the callback passed to Meteor.call actually fires with the return value from step 4. It’s important that the callback waits until the client is up to date, so that your Method callback can assume that the client state reflects any changes done inside the Method.

Share module function with client side script nodejs

I made a function to check if someone is logged in on the site in the user controller module:
exports.isLoggedIn = function(req, res, next) {
if (req.user) {
return true;
} else {
return false;
}
};
I have no idea how I want to use this in a imported script on the client side. I couldn't find a good solution on the web so I thought I would ask the question myself.
If I import the script in the .html I get an error that says it doesnt know the require() function that node has.
I hope someone can help me :)
If you want client access to some data that is only available on the server, then you need to send an ajax call from the client to your server and you need to create a route on your server to respond to that ajax call.
Client code runs only on the client and has no direct access to any data on the server.
Server code runs only on the server and has no direct access to any data on the client.
To communicate between the two, you have to send a request from one to the other and then return a response. Usually this is done with an Ajax call sent from client to server. You could also establish a webSocket connection between the two and then either client or server could send data to the other.
The server also has the opportunity, when creating the original page content, to embed settings or values in the page itself, either as Javascript variables, as HTML values or even as a cookie. This obviously has to be done ahead of time when the page is rendered so it can't be a request that the client comes up with later after the page has been rendered to the client.
FYI, in the particular example you show, it is common for a client to be able to tell if it is logged in via some state in the page (either the presence of a particular cookie) or something else embedded in the page by the server. This isn't necessarily secure and isn't the way the server would tell if a request was logged in, but it usually suffices for client-side logic to decide how it wants to behave.

Categories