Make $ .ajax consider Response code 40x as success - javascript

jQuery executes the function "success" if the HTTP status code is in the range of 200 and 299 or is equal to 304.
However, for example, for the code 401 I need jQuery considers that the Ajax call is successful, and it evaluates the response as JSON and executes the function "success".
The problem is that this behavior is hard-coded in the method "done":
// Determine if successful
isSuccess = status> = 200 && status <300 || === status 304;
I do not really see how to do that.
EDIT:
This is what I have for the moment:
var options = {
url: '',
type: 'POST',
data: {},
success: function(response, status){},
error: function(res, status, error){
notify("Une erreur s'est produite !", "danger");
},
complete: function(res, status){}
};
$.extend(options, opts);
var dataString = '';
$.each(options.data, function(key, value){
dataString += ((dataString.length > 0) ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value)
});
$.ajax({
url: site_url + options.url,
type: options.type,
data: dataString,
dataType: 'json',
statusCode: {
401: function() {
setTimeout(function(){
location.reload();
}, 2000);
}
},
success: function(response, status){
if (response.response.result.status == 'ok'){
options.success(response, status);
} else {
if ('message' in response.response.result){
notify(response.response.result.message, "danger");
} else if (response.response.errors.length > 0) {
notify(response.response.errors[0], "danger");
}
}
},
error: options.error,
complete: options.complete
});
I want the answer to be parsed according to the dataType provided (which is only for the "success" method), and, in the case of a code 401, processing is the same as for the other responses containing the correct JSON code, except for a further instruction.
I think it is a mistake for jQuery not be able to change the codes indicating a request as having failed. The content of the response may be important anyway and require special processing.
For a complete web page, the browser still displays the content returned by the server in case of error.

Instead of trying to override the "success" callback why not just make the function call inside the "error" callback,ofcourse before checking the specific error occurred.
error: function(a, b, c){
if(a.status == 401){
// Your custom function call / code.
}
}

Do you have to handle the status code in the success or error block? How about the complete block? It follows both outcomes..
complete
Type: Function( jqXHR jqXHR, String textStatus )
A function to be called when the request finishes (after success and error callbacks are executed). The function gets passed two arguments: The jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object and a string categorizing the status of the request ("success", "notmodified", "nocontent", "error", "timeout", "abort", or "parsererror"). As of jQuery 1.5, the complete setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event.
Source: http://api.jquery.com/jquery.ajax/
Example:
$.ajax({
url: "http://www.google.com"
}).success(function(){ //--> use .done() instead
//things to do on success
}).error(function(){ //--> use .fail() instead
//things to do on error
}).complete(function( data ) { //--> use .always() instead
switch(data.status){
//your logic here
}
});

Finally, given the need for that to go through the "complete" method, it is necessary to recode the entire automation of jQuery.
So there is no interest in using $ .ajax in this case.
That's why I had to code this replacement function that uses the jQuery syntax:
var altAjax = function(opts){
var options = {
url: '',
type: 'GET',
data: {},
dataType: 'text',
successCodes: [304, 401, 403, 404, 500],
statusCode: {},
success: [],
error: [],
complete: []
};
$.extend(options, opts);
var success = function(data, textStatus, xhr){
if ($.isArray(options.success)){
$.each(options.success, function(index, callback){
callback(data, textStatus, xhr);
});
} else if ($.isFunction(options.success)){
options.success(data, textStatus, xhr);
}
if ($.isFunction(options.statusCode[xhr.status])){
options.statusCode[xhr.status](data, textStatus, xhr);
}
}
var error = function(xhr, textStatus, errorThrown){
if ($.isArray(options.error)){
$.each(options.error, function(index, callback){
callback(xhr, textStatus, errorThrown);
});
} else if ($.isFunction(options.error)){
options.error(xhr, textStatus, errorThrown);
}
if ($.isFunction(options.statusCode[xhr.status])){
options.statusCode[xhr.status](xhr, textStatus, errorThrown);
}
}
var complete = function(xhr, textStatus){
if ($.isArray(options.complete)){
$.each(options.complete, function(index, callback){
callback(xhr, textStatus);
});
} else if ($.isFunction(options.complete)){
options.complete(xhr, textStatus);
}
}
var dataString = '';
$.each(options.data, function(key, value){
dataString += ((dataString.length > 0) ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(($.isArray(value) || $.isPlainObject(value)) ? JSON.stringify(value) : value);
});
var req = new XMLHttpRequest();
var url = options.url;
if (options.type.toUpperCase() != 'POST'){
url += ((url.indexOf('?') > -1) ? '&' : '?') + dataString;
}
req.onload = function(){
var textStatus = 'error';
if ((this.status >= 200 && this.status <= 299) || $.inArray(this.status, options.successCodes) > -1) {
var data;
switch (options.dataType.toLowerCase()) {
case 'json':
try {
data = JSON.parse(this.responseText);
} catch (ex){
error(this, textStatus, ex.name + ': ' + ex.message);
break;
}
textStatus = 'success';
success(data, textStatus, this);
break;
case 'xml':
try {
data = $.parseXML(this.responseText);
} catch (ex){
error(this, textStatus, ex.name + ': ' + ex.message);
break;
}
textStatus = 'success';
success(data, textStatus);
break;
default:
textStatus = 'success';
success(this.responseText, textStatus);
}
} else {
error(this, textStatus, null);
}
complete(this, textStatus);
};
req.open(options.type, url, true);
if (options.type.toUpperCase() == 'POST'){
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send(dataString);
} else {
req.send();
}
req = null;
};

Instead of success use the complete function and check the xhr.statusText value
$.ajax('url.json', {
complete:function(result) {
if(/^(2\d\d|304|401)$/.test(result.statusText)) {
success();
} else {
error();
}
}
});

You need to handle the conditions at client side checking the status code. You can fetch the status as below:
success: function(data, textStatus, xhr) {
console.log(xhr.status);
},

Related

Ajax result always returns error even if function is successful

I have an Ajax function that looks like this
$.ajax({
type: "POST",
url: "#IGT.baseUrl/SODetailsAjax/AddUnits",
traditional: true,
data: {
__RequestVerificationToken: token,
so_id: #Int32.Parse(Request["orderId"]),
site_id: site,
addItem_id: items,
addItem_qty: itemsqty,
addItem_disc: itemsdisc,
addComp_id: comps,
addComp_qty: compsqty,
addComp_disc: compsdisc,
addPart_id: parts,
addPart_qty: partsqty,
addPart_disc: partsdisc
},
success: function (data) {
if(data.success === "False"){
var errorMessage = data.Message;
alert("Error:" + errorMessage);
return;
}
if(data.success === "True"){
location.href = "../SalesOrders/Details?id=#so.ID";
}
},
error: function (jqXHR, status, error) {
alert("Error:" + error);
}
});
And I have a JSON ActionResult method that does this in it.
if (!canCreate)
{
var errorMessage = string.Join(",", errors);
var stock = new { success = "False", Message = errorMessage };
return Json(stock, JsonRequestBehavior.AllowGet);
}
else
{
var result = new { success = "True" };
return Json(result, JsonRequestBehavior.AllowGet);
}
But everytime Success is true it returns an error message saying "Error:Not defined" when I click "OK" it proceeds. But how can I make it so it just proceeds instead of sending an error message?
You have a couple of errors. In your if (data.Success = "false") statement, this is not a condition. This is an assignment. You should do if (data.success === "false") this would check for the condition. Also note that "success" is all lower case because it's converted to Json. You also need to note that "False" does not equal "false" so you must pick a casing. Either do "False"/"True" in both c# and JavaScript or "false"/"true".

AJAX call doesn't go into "error:" block if the API server returns http 500

I want to implement a retry logic in my javascript code. This is how I'm calling the API:
$.ajax({
url: api_url + 'report',
type: 'GET',
dataType: 'json',
async: false,
tryCount : 0,
retryLimit : 3,
headers: {
"Authorization": "Basic " + btoa(api_username + ":" + api_pass)
},
data: {start: start_date, end: end_date},
success: function(result) {
data = result.results;
console.log("success");
},
error : function(xhr, textStatus, errorThrown ) {
console.log("in error");
if (textStatus == 'timeout') {
this.tryCount++;
if (this.tryCount <= this.retryLimit) {
//try again
console.log("try count:");
console.log(this.tryCount);
$.ajax(this);
return;
}
return;
}
if (xhr.status == 500) {
console.log("still 500");
} else {
console.log("still !500");
}
}
});
So when there are issues with the server and it returns http 500 then still my control in the above JS file doesn't go into the "error:" block and this line: "console.log("in error");" doesnt get printed on the console.
How can I correctly implement a retry logic in my code in case my server returns 500 then it should keep on retrying for some x amount of times?
500 error generally means that something is wrong with backend server. So it doesn't get into error block of client JavaScript. I don't think there is anything you can do. But in general you can always ask backend developers to do better error handling and return apt error response if possible.

Ajax success function not working in jquery mobile

I am trying to validate a basic login form with username and password fields. I need to validate username and password from check.php ajax page. There is no problem in ajax request and response. I am getting proper response from ajax page. But Ajax success function is not working properly.
ajaxrequest.html
$(document).on('pagebeforeshow', '#login', function(){
$(document).on('click', '#submit', function() {
if($('#username').val().length > 0 && $('#password').val().length > 0){
$.ajax({
url : 'serverurl/check.php',
data: {action : 'login', formData : $('#check-user').serialize()},
type: 'post',
beforeSend: function() {
$.mobile.loading(true);
alert("beforesend");
},
complete: function() {
$.mobile.loading(false);
alert("complete");
},
success: function (result) {
console.log("Ajax response");
res = JSON.stringify(result);
if(res.status == "success"){
resultObject.formSubmitionResult = res.uname;
localStorage["login_details"] = window.JSON.stringify(result);
$.mobile.changePage("#second");
}else{
$.mobile.changePage("#login");
alert("incorrect login");
}
},
error: function (request,error) {
alert('Network error has occurred please try again!');
}
});
} else {
alert('Fill all fields');
}
return false;
});
});
Here i have added my ajax page. This page only validates posted username and password. Finally it returns json object. What am i doing wrong?
serverurl/check.php
header("Access-Control-Allow-Origin: *");
header('Content-Type: application/json');
if(isset($_POST['formData']) && isset($_POST['action']) && $_POST['action'] == 'login'){
parse_str($_POST['formData'],$searchArray);
$uname = "arun";
$pwd = "welcome";
$resultArray = array();
if($uname == $searchArray['username'] && $pwd == $searchArray['password'])
{
$resultArray['uname'] = $searchArray['username'];
$resultArray['pwd'] = $searchArray['password'];
$resultArray['status'] = 'success';
}else{
$resultArray['status'] = 'failed';
}
echo json_encode($resultArray);
}
Your code should be
success: function (result) {
console.log("Ajax response");
//don't do this
//res = JSON.stringify(result);
if(result.status == "success"){
resultObject.formSubmitionResult = result.uname;
localStorage["login_details"] = window.JSON.stringify(result);
$.mobile.changePage("#second");
}else{
$.mobile.changePage("#login");
alert("incorrect login");
}
After JSON.stringify you are accessing like stringJson.status this will not work. it mast have "parsed" "json object" not stringify.
Don't need to convert your JSON to String.
$(document).on('pagebeforeshow', '#login', function(){
$(document).on('click', '#submit', function() {
if($('#username').val().length > 0 && $('#password').val().length > 0){
$.ajax({
url : 'serverurl/check.php',
data: {action : 'login', formData : $('#check-user').serialize()},
type: 'post',
beforeSend: function() {
$.mobile.loading(true);
alert("beforesend");
},
complete: function() {
$.mobile.loading(false);
alert("complete");
},
success: function (result) {
console.log("Ajax response");
//Don't need to converting JSON to String
//res = JSON.stringify(result);
//directly use result
if(result.status == "success"){
resultObject.formSubmitionResult = result.uname;
localStorage["login_details"] = window.JSON.stringify(result);
$.mobile.changePage("#second");
}else{
$.mobile.changePage("#login");
alert("incorrect login");
}
},
error: function (request,error) {
alert('Network error has occurred please try again!');
}
});
} else {
alert('Fill all fields');
}
return false;
});
});
Your AJAX call is perfect but datatype is not declared in ajax
Try with jSON OR JSONP. You will get success.
$.ajax({
url : 'serverurl/check.php',
type: 'post',
dataType: "json", OR "jsonp",
async: false,
data: {action : 'login', formData : $('#check-user').serialize()},
beforeSend: function() {
$.mobile.loading(true);
alert("beforesend");
},
complete: function() {
$.mobile.loading(false);
alert("complete");
},
success: function (result) {
console.log("Ajax response");
alert(JSON.stringify(result)); // Check response in alert then parse according to that
res = JSON.stringify(result);
if(res.status == "success"){
resultObject.formSubmitionResult = res.uname;
localStorage["login_details"] = window.JSON.stringify(result);
$.mobile.changePage("#second");
}else{
$.mobile.changePage("#login");
alert("incorrect login");
}
},
error: function (request,error) {
alert('Network error has occurred please try again!');
}
});
Under some circumstances your server might not return the response correctly. Have you tried to handle the actual response code (e.g. if your server returns 200) like this:
$.ajax({
url : 'serverurl/check.php',
data: {action : 'login', formData : $('#check-user').serialize()},
type: 'post',
....
statusCode: {
200: function (response) {
// do your stuff here
}
}
});

Change ajax data on retry

Is there a way to change the supplied data on an ajax retry? I want to make the first call with user="auth" passed in the data params , if it fails then change the user to "ANON" and retry the call this new data param. The user shows up as undefined with the way I have it set up below.
$.ajax({
url : proxyurl,
type : 'GET',
dataType : 'xml',
user : "auth",
tryCount : 0,
retryMax : 2,
data : {'apireq': apiurl+"?user="+this.user},
success : function(data){
},
error: function(xhr,textStatus,errorThrown){
if (textStatus == 'parsererror') {
this.user = "ANON";
this.tryCount++;
if (this.tryCount <= this.retryMax) {
$.ajax(this) //try the call again
return;
}
return;
}
}
});
We were able to reach the following solution:
error: function(xhr,textStatus,errorThrown){
if (textStatus == 'parsererror') {
this.user = "ANON";
this.data = {'apireq': apiurl + "?user=" + this.user };
this.tryCount++;
if(this.tryCount <= this.retryMax) {
$.ajax(this); //try the call again
return;
}
return;
}
}

Force ajax .fail() during custom authorization to manage login redirect

In a MVC application I have a jQuery ajax post to a method in a controller:
function initFormForInsert(metodoLoadForm, nomeForm, divForm, widthForm, heightForm, metodoInsert) {
blockPage();
var request = $.ajax(
{
type: 'POST',
url: getRootURL() + metodoLoadForm
});
request.done(function (data) {
//alert(data);
LoadFormForInsert(data, nomeForm, divForm, widthForm, heightForm, metodoInsert);
});
request.fail(function (jqXHR, textStatus) {
unblockPage();
showErrorDialog("Error", textStatus);
});
}
Can I force request.fail() in some way?
As I use a global filter for authorization for all my methods in my controllers(code below)
in particular I'd like to throw an exception when I receive this Ajax post and my session variable is null.
protected override bool AuthorizeCore(HttpContextBase httpContext) {
try {
UserToken cUt = httpContext.GetUser();
if (cUt == null) {
//session is null
return false;
}
string request = httpContext.Request.Path;
if (httpContext.Request.Path.LastOrDefault() == '/')
request = httpContext.Request.Path.Remove(httpContext.Request.Path.Length - 1);
if (cUt.DeniedActions.Contains(request.ToUpper())) {
//user is not authorized
return false;
}
return true;
} catch (Exception) {
return false;
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
UserToken cUt = filterContext.HttpContext.GetUser();
if (cUt == null) {
//session is null
if (
//filterContext.HttpContext.Response.StatusCode == 302 &&
filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest"
) {
//filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 401;
} else {
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new {
controller = "Login",
action = "Login"
})
);
}
}
I've modified the JavaScript dialog this way:
function initFormForInsert(metodoLoadForm, nomeForm, divForm, widthForm, heightForm, metodoInsert) {
blockPage();
var request = $.ajax(
{
type: 'POST',
url: getRootURL() + metodoLoadForm,
statusCode: {
200: function (data) {
//alert(200);
LoadFormForInsert(data, nomeForm, divForm, widthForm, heightForm, metodoInsert);
},
401: function (jqXHR, textStatus, errorThrown) {
//alert(401);
hrefTo("/Login/Login") ;
}
}
});
//request.done(function (data) {
// //alert(data);
// LoadFormForInsert(data, nomeForm, divForm, widthForm, heightForm, metodoInsert);
//});
request.fail(function (jqXHR, textStatus) {
unblockPage();
showErrorDialog("Errore inizializzando la form per inserimento ", textStatus);
});
}
and the authorize like this:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
try
{
UserToken cUt = httpContext.GetUser();
if (cUt == null)
//session is null
{
return false;
}
string request = httpContext.Request.Path;
if (httpContext.Request.Path.LastOrDefault() == '/')
request = httpContext.Request.Path.Remove(httpContext.Request.Path.Length - 1);
if (cUt.DeniedActions.Contains(request.ToUpper()))
{
//user is not authorized
return false;
}
return true;
}
catch (Exception)
{
return false;
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
UserToken cUt = filterContext.HttpContext.GetUser();
if (cUt == null)
{
// session is null
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
filterContext.HttpContext.Response.End();
return;
}
else
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Login",
action = "Login"
})
);
}
}
}
Do you think is ok?
Or another method even if slower could be add when in an if brench
the check over the session variable
blockPage();
if (checkSessionVariable()) {
hrefTo("/Login/Login");
}
else {
var request = $.ajax(
{
type: 'POST',
url: getRootURL() + metodoLoadForm,
});
request.done(function (data) {
//alert(data);
LoadFormForInsert(data, nomeForm, divForm, widthForm, heightForm, metodoInsert);
});
request.fail(function (jqXHR, textStatus) {
unblockPage();
showErrorDialog("Errore inizializzando la form per inserimento ", textStatus);
});
}
where check session variable is another post which check session variable
function checkSessionVariable() {
var request = $.ajax(
{
type: 'POST',
url: getRootURL() + "/Login/SessionExpired"
});
request.done(function (data) {
return (data);
});
request.fail(function (jqXHR, textStatus) {
return 0;
});
}
maybe this is a more reliable solution?
Dont recommend to give 500 or 403 errors as a workaround. Remember these are status code which mean N/w failure to Resource not found
Rather, try processing your result from server as a positive response with negative scenario
which means data now need to processed as below
request.done(function (data) {
//alert(data);
if(data.positive) {
LoadFormForInsert(data, nomeForm, divForm, widthForm, heightForm, metodoInsert);
}
else if(data.negative){
unblockPage();
showErrorDialog("Error", textStatus);
}
});
You could pass cookie too for example (pseudo code below)
request.done(function (data) {
//alert(data);
if(cookie == "positive") {
LoadFormForInsert(data, nomeForm, divForm, widthForm, heightForm, metodoInsert);
}
else if(cookie == "negative"){
unblockPage();
showErrorDialog("Error", textStatus);
}
});

Categories

Resources