jQuery Synchronous Form Submission with Timeout - javascript

I am attempting to submit a form to an external site which is prepared to receive it, but there were some points when the external site was done and caused my site to be stuck waiting with no response just using form.submit()
I wrote the following to try to handle this, but the timeout is not being thrown.
$.ajax({
async: false,
url: url,
data: $('#tppForm').serialize(),
type: 'POST',
timeout: 5000,
statusCode: {
404: function() {
console.log('Site Not Found');
},
500: function() {
console.log('Site Down');
}
},
error: function() {
console.log('Timeout');
}
});

I checked both the jQuery documentation as well as other sources, the behavior of timeout: is not clearly specified for async:false.
My suggestion is to change this to async:true to better handle the unpredictable nature of remote (network) calls. See jquery ajax() async false and jQuery ajax success anonymous function scope .
This will change the challenge to one where your client side (browser) must have a "ready" state flag that can indicate to the user they should wait and block overlapping calls. Personally I like to alter the submit button text / color for user feedback. Just prior to making your AJAX call you should also check the state flag to ensure there are no pending operations.

Related

jQuery .load doesn't work whilst ajax query is in progress

I have a web application that loads different content into the same div (.content) using either an ajax request or a .load() request.
A simplified version of the ajax request is:
$(document).on('click', '.button1', function() {
$.ajax({
type: "POST",
url: "/path/to/file/ajax.php",
data: {
'param1': 'xyz',
'param2': '123',
},
timeout: 10000,
dataType: "json",
success: function(data) {
if (data.status == 'Success') {
$('.content').html(data.result);
} else if (data.status == 'Error'){
console.log('Something went wrong')
}
},
error: function(x, status, error) {
if (status === "timeout") {
console.log('Request Timed Out');
}
},
});
});
on another button I have:
$(document).on('click', '.button2', function() {
$('.content').load('/path/to/file/getContent.php?ref=foo');
});
The problem is that if I click on button1, and then click on button 2 whilst the ajax request is still executing, nothing happens. (ie. getContent.php doesn't seem to return anything for anywhere between 15-30 seconds)
Clicking on button 2 returns the results instantly, but as soon as button1 is clicked and the ajax request is being processed, it seems to "stall" the entire web app until it's completed (or errors).
(I've also tried using the abort() method to cancel the ajax request, but the same problem persists, even when the abort is successful).
UPDATE
See solution/Answer below
Following on from the tip from #adeneo, I did some more research into session_start and ajax calls which led me to this article:
http://konrness.com/php5/how-to-prevent-blocking-php-requests/
The problem absolutely was to do with session_start locking the session file which causes back-end issues with multiple ajax requests. Following the advice in the article above, I made use of session_write_close() in my PHP and everything is working great.
If it helps anyone else, I actually changed my code from session_start() to:
if (session_status() == PHP_SESSION_NONE) {
session_start();
session_write_close();
}
This makes session variables readable, but no longer locks the session file causing ajax request delays. (It also checks to make sure session_start() hasn't already been called)

How to handle specific HTTP error for all AJAX calls?

I have a web app, requesting and sending data via AJAX, and as a response, my server-side sends HTTP status codes, depending on the situation. so for example if a user tries to login while he's logged I probably return a 400 HTTP status code. And eventually i handle it with an alert, etc.
But handling these HTTP Status codes gets too heavy, since I'm making heavy use of AJAX. that means I'll be handling HTTP status code repeatedly with every AJAX request, which will result in duplicated code, and that's a bad practice.
So, what I'm looking for is a way to handle all these errors in one place, so I just handle all 400, 401, etc with the same code.
What i'm currently doing:
Handling the errors manually for each AJAX call. By using the statusCode in$.ajax().
statusCode: {
500: function(data) {
alert('Some friendly error message goes here.');
}
It seems like an overkill for me, as my web app develops, and as I create more ajax calls. I'll be repeating this piece of code again and again.
Currently, the only idea I have in mind is creating a function that will work on top of AJAX, something like:
function doAjax(type,url, data, moreVars) {
//this function is just a SIMPLE example, could be more complex and flexible.
$.ajax({
type: type,
url: url,
data: data,
moreOptions:moreVars,
//now handling all status code.
statusCode: {
//handle all HTTP errors from one place.
}
});
}
doAjax("POST", 'mydomain.com/login.php', dataObj);
You can use $.ajaxSetup() to register global error state handlers.
Description: Set default values for future Ajax requests.
Example:
$.ajaxSetup({
statusCode: {
500: function(data) {
alert('Some friendly error message goes here.');
}
}
});

Is this a scope issue in JavaScript / JQuery? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it posible to use ajax respone outside of it?
I've created the following JavaScript routine that goes to a WCF oData service and gets some data. In the success element I get the results into the results variable and alert them - I see that there are objects returned. When I run the second alert, outside of the ajax call and before returning the results, the results variable is "undefined".
Can anyone please tell me where I'm going wrong?
function retrieveJsonpODataSet(baseUrl, query)
{
var oDataUrl = baseUrl + "?$format=json&$callback=?";
var results;
$.ajax(
{
url: oDataUrl,
contentType: 'application/json; charset=utf-8',
type: 'GET',
dataType: 'jsonp',
async: false,
success:
function (data, textStatus, xhr)
{
results = data.d;
alert(results); // This shows the results
},
error:
function (xhr, textStatus, errorThrown)
{
alert("Query failed.\n\n" + oDataUrl + "\n\n" + errorThrown);
results = null;
}
});
alert(results); // This shows "undefined"
return results;
}
Please ignore the query parameter - I've not finished the routine yet.
EDIT
Initially I had no async:false in the ajax call. I've added that now but it doesn't fix the problem.
The ajax call is an asynchronous operation. It fires and your code does not stop at it. So results is returned which at that point is undefined. What you need to do is to pass callback to the function.
function retrieveJsonpODataSet(baseUrl, query, callback) {
/* some code */
$.ajax({
/* some settings */
success: function(res) {
/* some code */
callback(results);
}
});
}
Now you use it like this
retrieveJsonpODataSet(baseUrl, query, function(res) {
/* Hurray, I have result now in res variable! */
});
DO NOT USE async: false OPTION! It blocks ALL scripts untill the call finishes... and what if it does not finish at all?? You will be blocked forever.
EDIT
I've missed that the request is JSONP. In that case async: false won't even work (it does not work for cross-domain requests and JSONP). So you have to use callbacks anyway.
A fellow Richard!
This isn't a scope issue, but more of an execution one. Both the success and error options are event handlers, and run asynchronously (hence it being called AJAX). This essentially means that the alert(results) and return results can, and likely will, get executed before the success or error events are triggered.
Your ajax is async, so the alert executes before the ajax completes. You need to set the ajax call async property to false in order for script to halt the execution until ajax request is made & processed.
However, jQuery docs says:
async
Default: true
By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. As of jQuery 1.8, the use of async: false is deprecated.
AJAX request are sent, without the script waiting for a response, that's what Dave Newton means by A-synchronus, put the alert inside the success callback function, and you'll see what the actual response is.
alternatively, you can specify the async property, and set it to false, to force your script to wait for the response, before continuing.

AD FS 2.0 Authentication and AJAX

I have a web site that is trying to call an MVC controller action on another web site. These sites are both setup as relying party trusts in AD FS 2.0. Everything authenticates and works fine when opening pages in the browser window between the two sites. However, when trying to call a controller action from JavaScript using the jQuery AJAX method it always fails. Here is a code snippet of what I'm trying to do...
$.ajax({
url: "relyingPartySite/Controller/Action",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});
The issue is that AD FS uses JavaScript to post a hidden html form to the relying party.
When tracing with Fiddler I can see it get to the AD FS site and return this html form which should post and redirect to the controller action authenticated. The problem is this form is coming back as the result of the ajax request and obviously going to fail with a parser error since the ajax request expects json from the controller action. It seems like this would be a common scenario, so what is the proper way to communicate with AD FS from AJAX and handle this redirection?
You have two options.
More info here.
The first is to share a session cookie between an entry application (one that is HTML based) and your API solutions. You configure both applications to use the same WIF cookie. This only works if both applications are on the same root domain.
See the above post or this stackoverflow question.
The other option is to disable the passiveRedirect for AJAX requests (as Gutek's answer). This will return a http status code of 401 which you can handle in Javascript.
When you detect the 401, you load a dummy page (or a "Authenticating" dialog which could double as a login dialog if credentials need to be given again) in an iFrame. When the iFrame has completed you then attempt the call again. This time the session cookie will be present on the call and it should succeed.
//Requires Jquery 1.9+
var webAPIHtmlPage = "http://webapi.somedomain/preauth.html"
function authenticate() {
return $.Deferred(function (d) {
//Potentially could make this into a little popup layer
//that shows we are authenticating, and allows for re-authentication if needed
var iFrame = $("<iframe></iframe>");
iFrame.hide();
iFrame.appendTo("body");
iFrame.attr('src', webAPIHtmlPage);
iFrame.load(function () {
iFrame.remove();
d.resolve();
});
});
};
function makeCall() {
return $.getJSON(uri)
.then(function(data) {
return $.Deferred(function(d) { d.resolve(data); });
},
function(error) {
if (error.status == 401) {
//Authenticating,
//TODO:should add a check to prevnet infinite loop
return authenticate().then(function() {
//Making the call again
return makeCall();
});
} else {
return $.Deferred(function(d) {
d.reject(error);
});
}
});
}
If you do not want to receive HTML with the link you can handle AuthorizationFailed on WSFederationAuthenticationModule and set RedirectToIdentityProvider to false on Ajax calls only.
for example:
FederatedAuthentication.WSFederationAuthenticationModule.AuthorizationFailed += (sender, e) =>
{
if (Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest())
{
e.RedirectToIdentityProvider = false;
}
};
This with Authorize attribute will return you status code 401 and if you want to have something different, then you can implement own Authorize attribute and write special code on Ajax Request.
In the project which I currently work with, we had the same issue with SAML token expiration on the clientside and causing issues with ajax calls. In our particular case we needed all requests to be enqueud after the first 401 is encountered and after successful authentication all of them could be resent. The authentication uses the iframe solution suggested by Adam Mills, but also goes a little further in case user credentials need to be entered, which is done by displaying a dialog informing the user to login on an external view (since ADFS does not allow displaying login page in an iframe atleast not default configuration) during which waiting request are waiting to be finished but the user needs to login on from an external page. The waiting requests can also be rejected if user chooses to Cancel and in those cases jquery error will be called for each request.
Here's a link to a gist with the example code:
https://gist.github.com/kavhad/bb0d8e4a446496a6c05a
Note my code is based on usage of jquery for handling all ajax request. If your ajax request are being handled by vanilla javascript, other libraries or frameworks then you can perhaps find some inspiration in this example. The usage of jquery ui is only because of the dialog and stands for a small portion of the code which could easly be swapped out.
Update
Sorry I changed my github account name and that's why link did not work. It should work now.
First of all you say you are trying to make an ajax call to another website, does your call conforms to same origin policy of web browsers? If it does then you are expecting html as a response from your server, changedatatype of the ajax call to dataType: "html", then insert the form into your DOM.
Perhaps the 2 first posts of this serie will help you. They consider ADFS and AJAX requests
What I think I would try to do is to see why the authentication cookies are not transmitted through ajax, and find a mean to send them with my request. Or wrap the ajax call in a function that pre authenticate by retrieving the html form, appending it hidden to the DOM, submitting it (it will hopefully set the good cookies) then send the appropriate request you wanted to send originally
You can do only this type of datatype
"xml": Treat the response as an XML document that can be processed via jQuery.
"html": Treat the response as HTML (plain text); included script tags are evaluated.
"script": Evaluates the response as JavaScript and evaluates it.
"json": Evaluates the response as JSON and sends a JavaScript Object to the success callback.
If you can see in your fiddler that is returning only html then change your data type to html or if that only a script code then you can use script.
You should create a file anyname like json.php and then put the connection to the relayparty website this should works
$.ajax({
url: "json.php",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});

Jquery ajax onSuccess event

I am doing AJAX with JQuery but every time the "onSuccess" event must be executed after another AJAX request disconnected.
Here is the code:
d.ajax({
url: f.options.url.offline,
dataType: "jsonp",
jsonp: "callback",
cache: false,
data: {
status: "offline",
ticket: f.connection.options.ticket
},
success: function(g) {
f._offlineSuccess()
},
error: function() {
f._offlineError()
}
})
All my AJAX requests are JSONP, and when the above code is triggered, there is another AJAX connection (long polling request, last about 10 senconds) already established in the mean time. So the "f._offlineSuccess" function is always executed after another AJAX connection disconnected.
I can not see any relationship between the two AJAX requests, and I don't know why the "onSuccess" function must be executed after another AJAX connection stopped.
Any help is appreciated~
================================
updated:
I just found out if I have two JSONP connection at the same time, the "onSuccess/onFailure" function will be blocked. I don't know if some one encountered the same problem before?
Ajax requests are asynchronous. so a new request is not going for the previous one to finish. If you want that behaviour use async parameter to false, or use the complete() function to call for another request. This will fire only when the first request is finished.
UPDATE
For JsonP use jQuery.getJSON() and do the second request on callback if the call was succesfull.
function (data, textStatus) {
// data will be a jsonObj
// textStatus will be one of the following values:
// "timeout","error","notmodified","success","parsererror"
this; // the options for this ajax request
}
If you use firebug - net tab, you will be able to see the full url of the two jsonp requests. You should be able to see the callback function names on the end of the url. Are these different or the same? I can only assume they are the same.

Categories

Resources