I have recently been working on a project with both a client (in the browser) and node.js. I am using web sockets to communicate between the two. I have implemented an API on my server that communicates over the WebSockets and the server works just fine. In the browser, I am implementing the following class (in javascript) to interface with the API.
class ApiHandlerV1 {
constructor(apiUrl) {
//create the object
this.ws = null;
}
makeRequest(request,callback) {
//make a request
}
connect() {
//connect the websocket
this.ws = new WebSocket(this.url);
this.ws.onmessage = function () {
//call callback?
}
}
}
The issue that I am caught up on is that I want to be able to call makeRequest, provide a callback, and once the socket has gotten data back trigger the callback. I have thought about just re-defining .onmessage every time that I make a request but that just seems dirty to me and there is most likely a nice and easy solution to this.
Clarifications: Because of how I implemented my server I will only get a single message back from the server.
As Dominik pointed out in the comments I should also say that I am going to call .connect() before I make a request. I will be calling makeRequest multiple times after in other parts of my code.
Related
I have a React app. In that react app, I have certain button click events which are triggered just before page changes (analytics event)
sendEvent('xyz')
let win = window.open(`${PAGE_PATHS.dashboard}`, "_self");
if (win !== null) {
win.focus();
}
Where event is
async event(name, data) {
await this.sendEvent(name, data)
}
Where sendEvent() is
async sendEvent(name, data) {
// some code
axios.post(url, payload, userConfig)
return
}
Now, because of the current structure, the events sometimes don't get logged. There are two things which I could do here.
Use await but I don't want to do it because sending event and getting response might take some time (user experience) and I don't care about the response
Use setTimeout.
For some reason I don't like either one of the approach. Is there a way, I can have the task to execute (if it isn't completed) even after the webpage have changed it's href? maybe using service or webworker
What you are doing is commonly called "sending a beacon", and there is a method in the Web standards especially for this case: Navigator.sendBeacon().
This method will allow your script to send a request to the web server, even after the page has been killed.
I am not an axios ninja, so I can't tell you how it should be rewritten to perform the same, but certainly it can.
The basic usage is:
navigator.sendBeacon(url, data);
where data can be an ArrayBuffer, a TypedArray, a Blob, a DOMString, FormData or an URLSearchParams object.
I'm introducing service worker on my site.And i'm using app-shell approach for responding to requests.Below is my code structure.
serviceWorker.js
self.addEventListener("fetch", function(event) {
if (requestUri.indexOf('-spid-') !== -1) {
reponsePdPage(event,requestUri);
}else{
event.respondWith(fetch(requestUri,{mode: 'no-cors'}).catch(function (error){
console.log("error in fetching => "+error);
return new Response("not found");
})
);
}
});
function reponsePdPage(event,requestUri){
var appShellResponse=appShellPro();
event.respondWith(appShellResponse); //responds with app-shell
event.waitUntil(
apiResponse(requestUri) //responds with dynamic content
);
}
function appShellPro(){
return fetch(app-shell.html);
}
function apiResponse(requestUri){
var message=['price':'12.45cr'];
self.clients.matchAll().then(function(clients){
clients.forEach(function (client) {
if(client.url == requestUri)
client.postMessage(JSON.stringify(message));
});
});
}
App-shell.html
<html>
<head>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.onmessage = function (evt) {
var message = JSON.parse(evt.data);
document.getElementById('price').innerHTML=message['price'];
}
}
</script>
</head>
<body>
<div id="price"></div>
</body>
</html>
serviceWorker.js is my only service worker file. whenever i'm getting request of -spid- in url i calls reponsePdPage function.In reponsePdPage function i'm first responding with app-shell.html. after that i'm calling apiResponse function which calls postmessage and send the dynamic data.The listener of post message is written in app-shell.html.
The issue i'm facing is, sometimes post message gets called before the listener registration.It means the apiResponse calls post message but their is not register listener to that event. So i cant capture the data.?Is their something wrong in my implementation.
I'm going to focus on just the last bit, about the communication between the service worker and the controlled page. That question is separate from many of the other details you provide, such as using PHP and adopting the App Shell model.
As you've observed, there's a race condition there, due to the fact that the code in the service worker and the parsing and execution of the HTML are performed in separate processes. I'm not surprised that the onmessage handler isn't established in the page yet at the time the service worker calls client.postMessage().
You've got a few options if you want to pass information from the service worker to controlled pages, while avoiding race conditions.
The first, and probably simplest, option is to change the direction of communication, and have the controlled page use postMessage() to send a request to the service worker, which then responds with the same information. If you take that approach, you'll be sure that the controlled page is ready for the service worker's response. There's a full example here, but here's a simplified version of the relevant bit, which uses a Promise-based wrapper to handle the asynchronous response received from the service worker:
Inside the controlled page:
function sendMessage(message) {
// Return a promise that will eventually resolve with the response.
return new Promise(function(resolve) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
resolve(event.data);
};
navigator.serviceWorker.controller.postMessage(message,
[messageChannel.port2]);
});
}
Inside the service worker:
self.addEventListener('message', function(event) {
// Check event.data to see what the message was.
// Put your response in responseMessage, then send it back:
event.ports[0].postMessage(responseMessage);
});
Other approaches include setting a value in IndexedDB inside the service worker, which is then read from the controlled page once it loads.
And finally, you could actually take the HTML you retrieve from the Cache Storage API, convert it into a string, modify that string to include the relevant information inline, and then respond with a new Response that includes the modified HTML. That's probably the most heavyweight and fragile approach, though.
In my web application, on click on a specific button, AJAX call is triggered. On the server side, I want to execute a query to a DB.
Now, I am thinking about this, there are queries that are executing for some period, and maybe user isn't patient enough to wait for the results, and wants to stop the execution.
Is there a way to add one more button, that when clicked could stop previous AJAX call from executing and ALSO stop execution of a query? And is it a safe approach?
I am using angularjs, alongside with C# on server side.
related answers on your question
client side is streight forward
How to cancel/abort jQuery AJAX request?
but serverside is not as easy as client side, here related post to move forward
How do you cancel an AJAX long running MVC action client side (in javascript)?
The best way to go about "cancelling" your request on the client side is by taking advantage of the $q service, creating a deferred and then attaching it to your request using the "timeout" property for the $http({}) function call.
function sendSomeRequestToServer() {
var requestCanceler = $q.defer();
var request = $http({
method: "get",
url: "/your/url",
timeout: requestCanceler.promise
});
var promise = request.then(function(response) {
return response.data;
});
promise._requestCanceler = requestCanceler;
return promise;
}
function cancel(requestPromise) {
if (promise && promise._requestCanceler && promise._requestCanceler.resolve) {
promise._requestCanceler.resolve();
}
}
Most likely these functions would be wrapped in the same angular service. Credit to Ben Nadel and his blog for the tutorial and code.
I want to use an API in my meteor app. The API is restricted to a few requests per second per unique IP.
Does anyone know if the server IP or the user IP is used, when I make an API call in Meteor.methods like this
Meteor.methods({
searchTerm: function (term, lang) {
var parameters = {
"api_key": Meteor.settings.API
};
try {
var result = HTTP.call("GET", apiLink, { params: parameters });
return result.data;
} catch (e) {
return e;
}
}
}
Thanks in advance.
As already noted in the comments, if this code (the methods call itself) is runs on the server, then the method call (later with Meteor.call) is like a remote procedure call and the HTTP will be executed on the server only. If, however, this code, the methods call, is invoked on both the client and the server, then that defines a stub (http://docs.meteor.com/#/full/methods_header). That stub is executed in parallel on the client and the server. It is meant to help with latency compensation. I don't think you want that in this case though, since you are more concerned with the number of API requests. So I would suggest to leave it where it is right now (in the server folder somewhere). That way you can be sure that it will only execute on the server and not the client, and hence use the server IP.
I have some Meteor methods and I want to secure them so that only certain users can call them from the client. However these methods are also used by the server. I am passed the userid in this.userid so I can check if the user is logged in and if they are allowed to make the call, no problem. But when I also need to call the method from the server side how do I determine that it was a server call so I can allow the method to execute. Checking that there is no this.userid to determine if its a server call allows un-authenticated users to call the method as well. I am looking for a way to determine if the method was called by the server so I can allow it and still prevent un-authenticated users from calling the method.
Meteor.methods({
makeCoffee: function (time) {
check(time, Number);
if(calledByServer || (Meteor.user() && Meteor.user().profile.usertype === 'coffee dude')){
//Makin' Coffee
}
else
throw new Meteor.Error(404, "Can't find my pants");
return "Coffee will be made at " + time;
}
this.connection will be null inside a server side method if the method was not called from a client
See the this.connection docs.
Looks like Meteor.call can be called from server side too now:
http://docs.meteor.com/#meteor_call
Original answer:
Make it like this:
makeCoffee = function (time) { //code here }
Meteor.methods({
makeCoffeeMethod: function (time) {
if (calledByAllowedUser())
return makeCoffee(time);
else
throw new Meteor.Error(403, 'Forbidden');
}
});
Now you can call it on server bypassing the authentication.