Using WebSocket to reconnect to server - javascript

I have an experimental task implemented in javascript that posts its data to the server via an ajax POST request. This POST request is mandatory for compatability with other tools.
Saving data works fine when the task is finished quickly but over a certain time the server times out and when the tasks tries the POST request, I get a 400 error and nothing gets saved.
My question is can I combine this with a WebSocket and use it to reconnect to the server occasionally and still post my data with an ajax POST request? If so, how do I do this?
This is what my POST function looks like:
$.ajax({
type: 'POST',
url: '/save',
data: {'data': JSON.stringify(mainData)},
success: function() {
console.log('success');
document.location = '/next'
},
// Endpoint not running, local save
error: function(err) {
if (err.status == 200) {
document.location = '/next'
}
}
});
Optimally I would like to have something like this
if (err.status == 200) {
document.location = '/next'
} else if (err.status == 400) {
// RECONNECT TO SERVER
// call ajax function again
}
I don't really have any experience with these tools, so any info will be much appreciated!
///////////////////// EDIT: ///////////////////////
I've been told that a more preferable option is to keep the connection constantly open with regular websocket requests. So a suggestion on how to do this would be appreciated as well.

Related

Understanding routing from client side (javascript) to server side (node.js)

I'm new at web apps and at the moment I'm trying to wrap my head around routing from client side to server side and back. I ran in to a problem where I was doing xmlhttprequest on my client side to get a json, which was working. But now that im not running locally none of the GETS are working. So I figured I have to do routing to server side, do the request() to get the json, which I can.
But now what I don't understand is how to pass that json back to client side to use the function there, since all my functions that use this json are there. Is this possible? or do I have to do everything server side now?
server side
server.get('thankyou.html/something', function(req, res) {
var options = {
url: 'https://**.***.**.**:****/****/*******/',
rejectUnauthorized: false,
method: 'GET'
};
request(options, function (error, response, body) {
if (error) console.log(error);
else displaytable(body);//<------- clientside funtion
});
});
client side
var uri = 'thankyou.html/something';
function addTable() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && (xhr.status == 201 || xhr.status == 200)) {
// var json = JSON.parse(xhr.responseText);
displaytable(json);
}
};
xhr.open("GET", uri, true);
xhr.send();
}
I think I'm not doing the routing right either.
What's the expected flow here? client -> your server # thankyou.html/something -> some other server url (the one you have censored) -> response back to your server -> response back to client -> client uses response to display table?
Either way, you definitely can't call client functions from your server. Not like that, anyways. You'll need to return the body with something like res.json(body) (what routing / server library are you using?), and then parse the xhr.responseText, like your commented-out line was doing. Then you'll have the json on the client, and can continue as expected.
Make sure if your request call returns an error that you pass the error through to the client as well, or it will hang until timeout.

res.redirect not responding in nodejs

imagine user click on something and a modal dialog appear, he done key in the things and the modal close. What should I do next after I send the data back to the db? I do res.redirect('store') but it does nothing, I know what's the problem because I'm already on localhost:2000/store.
I was expecting a refresh because the user just added / updated the data. How will you do it in your case? I'm using node with express 4.
Are you using Ajax to send data from modal to the server? In this case redirect you send from server will not work, it the Ajax request will be redirected only. If you want to refresh the page you need to do this on the client after you receive the response for your request.
Could you provide more details so I can provide a complete answer?
In my frontend modal code using Angularjs, I redirect the client only when I successfully get data back from the server using 'window.loction'
Client code (angular):
$http({ method: 'POST', url: '/login', data:user
}).then(function successCallback(response) {
if (response.status===200) {
$uibModalInstance.dismiss('cancel');
window.location.href = '/home'; <<< redirect user here
} else {
console.log('unexpected response:', response)
}
}, function errorCallback(response) {
if (response.status === 401) {
console.log("scope:",$scope)
$scope.loginform.notauthorised = true;
}
console.log('err: ', response);
});

How much of `$.post` is blocking?

I have a form collecting some information that I use $.post to handle an ajax request.
$.post(ajaxEndpoint, dataObject)
.done(function (response) {
if (response.status === 'success') {
// Send data to process asynchronously
otherApiCall(response.otherData);
// Redirect to the thank you page
window.location.replace(getThankYouUrl());
}
});
function otherApiCall (data) {
$.post(otherAjaxEndpoint, data);
}
The problem I have, from what I'm guessing, is that it redirects too quickly before the other POST can be made. But I do want it to POST asynchronously then redirect so the user isn't waiting for that second response. I don't care what the result of the second response is. I just want to finish the first response, send a second POST and the redirect immediately to cut down on the user looking at a spinner.
My second $.post seems like it doesn't get sent in time before the redirect happens because I never get the data from it. If I comment out the redirect, I do. I don't want to wait until the second done() but I can't figure how not to. What am I not understanding and/or doing wrong?
Additional Information/Update
I do have control over the server side handling. Is there something on that end that I could do to get a response quickly without waiting for the rest of the processing to finish?
You probably want to let the second post complete and then do the redirect.
A simple fix would be to return the $.post from second method and use done() of the second call to manage the redirect
$.post(ajaxEndpoint, dataObject)
.done(function (response) {
if (response.status === 'success') {
// Send data to process asynchronously
otherApiCall(response.otherData).done(function(){
// second post call now complete
// Redirect to the thank you page
window.location.replace(getThankYouUrl());
}).fail(function(){
// handle failed response
});
}
});
function otherApiCall (data) {
return $.post(otherAjaxEndpoint, data);
}
The best way to send data back to a server without having to wait for it to complete would be to use the navigator.sendBeacon API.
navigator.sendBeacon('/url/to/handler', yourData);
Quote from MDN:
Using the sendBeacon() method, the data will be transmitted asynchronously to the web server when the User Agent has had an opportunity to do so, without delaying the unload or affecting the performance of the next navigation.
Your data will have to be made into a ArrayBufferView, Blob, DOMString, or FormData, and I'm not sure if it is technically a POST request or not, but the request will persist after redirection.
It is currently supported in Firefox 31+, Chrome 39.0+, Opera 26+. For other browsers, you would have to do something else. You can feature-detect like so.
if (navigator.sendBeacon) {
// Use sendBeacon API.
}
else {
// Something else.
}
The redirect is probably cancelling the AJAX request that has been queued, but not yet sent. Try doing the redirect after a timeout, to give the second AJAX call a chance to be sent.
$.post(ajaxEndpoint, dataObject)
.done(function(response) {
if (response.status === 'success') {
// Send data to process asynchronously
otherApiCall(response.otherData);
// Redirect to the thank you page
setTimeout(function() {
window.location.replace(getThankYouUrl());
}, 10);
}
});
I'm not sure how reliable this is, though. Perhaps a better solution would be to perform the redirect when the second AJAX call goes to readystate == 3, which means the server is processing the request. There's no jQuery interface to this, so you'll probably have to do it using the low-level XMLHttpRequest interface.

Node.js progress indicator feedback

I have a question about the progress of sending with Node.js 'request'. I have a Node.js application that acts as a proxy, actually forwarding a body to another server. I want to see the progress of this upload action. Now I am doing this:
BROWSER
-> UPLOAD TO NODE
-> UPLOAD TO 3rd PARTY SERVICE
-> RETURN 3rd PARTY RESPONSE TO BROWSER
If this is possible, I would log it to check in the console.log how much progress is done. But, would it also be possible to return a
res.send(progress)
in the mean time, while waiting for the upload to finish and send the client back the upload has succeeded?
BROWSER
-> UPLOAD TO NODE
-> UPLOAD TO 3rd PARTY SERVICE
-> RETURN progress <-
-> RETURN progress <-
...etc.
-> RETURN 3rd PARTY RESPONSE TO BROWSER
This is the upload code (pretty much straightforward).
var requestOptions = {
timeout: 120000,
url: url, //URL to hit
method: 'post',
headers: headers,
body: payload //Set the body as a string
};
request(requestOptions, function (error, response, body) {
if (error) {
res.send(error);
}
else {
//res.sendStatus(response.statusCode);
console.log("[RETURNING RESPONSE BODY]");
res.send(body);
}
});
Your question contains two parts. One is for getting the progress from request, which can be found here: Upload Progress — Request
The other part would be notifying the browser of the progress. The HTTP protocol does not let you send multiple responses, so using res.send(progress) is not possible.
However, you can keep sending partial responses until it finishes. Writing something to res without closing it is as simple as res.write("string"), but accessing the response can be harder: Accessing partial response using AJAX or WebSockets?
You also need a way to wrap the body (and also errors) from the backing server so that it can fit as the final partial response.
Another solution would be opening another WebSocket request to track the uploading/downloading process. socket.io is a good library for node for this purpose.
Solution for first part was in r.req.connectin.bytesWritten
var r = request(requestOptions, function (error, response, body) {
clearInterval(q);
...
});
var q = setInterval(function () {
console.log("Uploaded: " + r.req.connection.bytesWritten);
}, 250);

Ajax - I need to check whether a valid session exists before every AJAX requests

I'm working on a project which uses user authentication. I'm facing a issue with my AJAX requests if there is no authenticated session present when the request is made.
I've a session timeout of 3min, so if the user keeps idle for 3 min then do some action which causes a AJAX request then the request will fail and return a 403 error. Here What I'm planning to do is intercept all the AJAX request from the page and sent a ping to the server which will return a JSON object saying whether there is a valid session. If there is one then the client will continue with the current request else it will reload the current page which will take the user to the login page and the user has to provide the credentials again.
Here is my implementation.
$("#param-ajax").ajaxSend(function(evt, request, settings) {
var pingurl = GtsJQuery.getContextPath() + '/ping.json';
var escapedurl = pingurl.replace(/\//g, "\\/");
var regexpr1 = eval('/^' + escapedurl + '\\?.*$/');
var regexpr2 = eval('/^' + escapedurl + '$/');
// Proceed with the ping only if the url is not the ping url else it will
// cause recursive calls which will never end.
if (!regexpr1.test(settings.url) && !regexpr2.test(settings.url)) {
var timeout = false;
$.ajax({
url : pingurl,
cache : false,
data : {
url : settings.url
},
async : false,
complete : function(request, status) {
if (status == "error") {
try {
// GtsJQuery.getJsonObject() converts the string
// response to a JSON object
var result = GtsJQuery
.getJsonObject(request.responseText)
if (result.timeout) {
timeout = true;
return;
}
} catch (e) {
// ignore the error. This should never occure.
}
}
}
});
// Reload the window if there is a timeout -- means there is no valid
// sesstion
if (timeout) {
window.location.reload();
}
}
});
Here everything work fine included the window.location.reload(), but the original ajax request is not aborted. Since the original AJAX request is not aborted after the page reload is triggered, the AJAX request also is sent to the server. I want some mechanism which will allow me to abort the original request if the timeout turns out to be true.
This post offers some answer, but the issue remains with the third party plugins like datatables which uses AJAX. We cannot write a error handler for those AJAX requests.
Thank you.
If I am understanding the situation, you do not need any of that. In your original ajax request, simply add an error function that will redirect the user.
errHandler = function(XMLHttpRequest, textStatus, errorThrown) {
if( textStatus.match(/forbidden/i) ) {
redirectUserToLoginHere();
}
}
$.ajax({
success: yourFunctionHere,
error: errHandler
})
Then you might be able to make some ajax wrapper which always has that errHandler so you don't have to place it in every single ajax call.
EDIT:
after some experimentation, if an 'ajaxSend' handler throws an Error, then the original request will never be sent.
Also, if the handler does
document.location = '/login';
then the original request is never sent.
Hopefully that helps :)
I changed my concept now, I'm checking for the xmlHTTPRequest in the server side using the request header 'x-requested-with'.
If it is a xmlHTTPRequest then 'x-requested-with' will have the value 'XMLHttpRequest'. Both the javascript libraries(EXTjs and jQuery) I'm using sets this header correctly.
Here is my server side code
boolean isAjaxRequest = StringUtils.endsWithIgnoreCase(request.getHeader("x-requested-with"), "XMLHttpRequest")
EDIT
If the given request is a ajax request the response will be json data which will have status 403 and it will contain a key called timeout with value true
ex: {timeout: true, ....}
Then we will handle this in the $.ajaxError() event handler will handle the error. If the error status is 403 and the timeout value is true then I'll reload the page.

Categories