I have the following AJAX call:
$.ajaxSetup({
csrfSafeMethod: function(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
},
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
I am getting:
csrfSafeMethod is not defined
Why is csrfSafeMethod not visible from inside beforeSend?
How can I fix this?
Can't you just define a regular function like so:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Why? Because your method is attached to an object which you don't reference in beforeSend. You can basically imagine it like this:
$.ajaxSetup = function(options) {
var beforeSend = options.beforeSend;
// do stuff...
var xhr = getXHR();
var settings = getSettings();
beforeSend(xhr, settings);
};
$.ajaxSetup({
csrfSafeMethod: function() { ... },
beforeSend: function() {
// `this` is the same as if I called this function in the global scope
// It has no reference to the `options` object
}
});
The actual code in the source code looks like this:
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend &&
( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {
// Abort if not done already and return
return jqXHR.abort();
}
Where s is some jQuery object, not in any available scope.
As for how to fix this, you need to declare your function elsewhere or assign your options to a referencable object.
var options = {
csrfSafeMethod: function(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
},
beforeSend: function(xhr, settings) {
if (!options.csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
};
Try this.csrfSafeMethod instead of csrfSafeMethod
Related
Below is my code and issue is with cache code is not working properly if any ajax call has callback in success.
var localCache = {
/**
* timeout for cache in millis
* #type {number}
*/
timeout: 30000,
/**
* #type {{_: number, data: {}}}
**/
data: {},
remove: function (url) {
delete localCache.data[url];
},
exist: function (url) {
return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
},
get: function (url) {
console.log('Getting in cache for url' + url);
return localCache.data[url].data;
},
set: function (url, cachedData, callback) {
localCache.remove(url);
localCache.data[url] = {
_: new Date().getTime(),
data: cachedData
};
if ($.isFunction(callback)) callback(cachedData);
}
};
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.cache) {
var complete = originalOptions.complete || $.noop,
url = originalOptions.url;
//remove jQuery cache as we have our own localCache
options.cache = false;
options.beforeSend = function () {
if (localCache.exist(url)) {
complete(localCache.get(url));
return false;
}
return true;
};
options.complete = function (data, textStatus) {
localCache.set(url, data, complete);
};
}
});
function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {
if (typeof (method) === 'undefined') {
method = "GET";
}
if (typeof (headers) === 'undefined') {
headers = "";
}
if (typeof (parameters) === 'undefined') {
parameters = "";
}
successHandler = typeof (successHandler) === 'undefined' ? function (data) {} : successHandler;
errorHandler = typeof (errorHandler) === 'undefined' ? function (data) {} : errorHandler;
return $.ajax({
method: method.toUpperCase(),
url: url,
// async: false,
data: parameters,
headers: headers,
success: function (data) {
console.log('hi');
successHandler(data, options);
console.log('bye');
},
error: function (data) {
$('.loader').hide();
errorHandler(data);
},
});
}
As per the above code after successfully run ajax successHandler(data, options);function should be the trigger but it not due to above cache handler code. I have no idea why this is not working. If I write simple something rather than callback function it is working. Same issue with datatable Ajax callbacks.
I have to use above cache handler at global level in my project doesn't matter ajax request is from datatable or from any other source.
Above cache code is from here https://stackoverflow.com/a/17104536/2733203
As discussed in the chatroom I've made some changes in your code :
var localCache = {
/**
* timeout for cache in millis
* #type {number}
*/
timeout: 30000,
/**
* #type {{_: number, data: {}}}
**/
data: {},
remove: function(url) {
delete localCache.data[url];
},
exist: function(url) {
return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
},
get: function(url) {
console.log('Getting in cache for url ' + url);
return localCache.data[url].data;
},
set: function(url, cachedData, callback) {
localCache.remove(url);
localCache.data[url] = {
_: new Date().getTime(),
data: cachedData
};
console.debug('caching data for '+url, cachedData);
if ($.isFunction(callback)) callback(cachedData);
}
};
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
if (options.cache) {
var complete = originalOptions.complete || $.noop,
url = originalOptions.url;
//remove jQuery cache as we have our own localCache
options.cache = false;
options.beforeSend = function() {
if (localCache.exist(url)) {
console.log('using cache, NO QUERY');
complete(localCache.get(url));
return false;
}
console.log('sending query');
return true;
};
options.complete = function(data, textStatus) {
localCache.set(url, data, complete);
};
}
});
function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {
method = method || "GET";
headers = headers || {};
parameters = parameters || {};
return $.ajax({
method: method.toUpperCase(),
url: url,
cache: true,
// async: false,
data: parameters,
headers: headers,
success: successHandler,
error: errorHandler,
});
}
handleAjaxRequests('/echo/json/', {p1: 'hey'}, null, 'POST', function(data){console.log('first success without cache', data);});
setTimeout(function(){
handleAjaxRequests('/echo/json/', {p1: 'hey'}, null, 'POST', function(data){console.log('success! with cache hopefully', data);});
}, 2000);
Fiddle here
added some logs in the localCache methods to see what's happening. Cache is never used so I've added the missing cache:true option
Added some logs inside beforeSend method to monitor the toggle between cache and query. Everything works fine.
Cleaned up the arguments null checks and removed empty function(){} (use $.noop() instead btw.
Now the core of your issue. The callbacks errorHandler and successHandler are arguments. $.ajax is asynchronous! it means at some point of the execution, right after this call is made, you won't be sure if the variable has the same value. Easiest solution is to just reference the function directly and let jQuery do the scope management. Hardest solution would be to give these functions to the context option in ajax settings which I don't recommend.
Now, the solution you use allows you to directly call $.ajax without a wrapper method. Why don't you use it directly? simpler and less prone to errors
EDIT: I'm really not fond of context so there is another alternative.
function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {
method = method || "GET";
headers = headers || {};
parameters = parameters || {};
return $.ajax({
method: method.toUpperCase(),
url: url,
cache: true,
// async: false,
data: parameters,
headers: headers,
success: (function(handler, opt) {
return function( /*Anything*/ data, /*String*/ textStatus, /*jqXHR*/ jqXHR) {
console.log('hi');
handler(data, opt);
console.log('bye');
};
})(successHandler, options),
error: (function(handler, opt) {
return function( /*jqXHR*/ jqXHR, /*String*/ textStatus, /*String*/ errorThrown) {
console.log('ouch');
handler(errorThrown);
};
})(errorHandler, options),
});
}
You scope the function with this well known javascript trick aka currying.
New fiddle here.
EDIT 2: if you want successHandler to run even when getting from cache you should use complete instead of success
function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {
method = method || "GET";
headers = headers || {};
parameters = parameters || {};
return $.ajax({
method: method.toUpperCase(),
url: url,
cache: true,
// async: false,
data: parameters,
headers: headers,
complete: (function(handler, opt) {
return function( /*Anything*/ data, /*String*/ textStatus, /*jqXHR*/ jqXHR) {
console.log('hi');
handler(data, opt);
console.log('bye');
};
})(successHandler, options),
error: (function(handler, opt) {
return function( /*jqXHR*/ jqXHR, /*String*/ textStatus, /*String*/ errorThrown) {
console.log('ouch');
handler(errorThrown);
};
})(errorHandler, options),
});
}
Fiddle here.
I'm using django-rest-framework.
And I have to add the X-CSRFToken header before every jquery ajax send.
Ref: https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax
jQuery.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", Cookies.get('csrftoken'));
}
}
});
So, everything is well until I make a ajax call with an beforeSend setting given:
jQuery.ajax({
url: '...',
data: { ... },
beforeSend: function(xhr, settings) {
// This function do shadows the ajaxSetup one.
}
});
So, is there any efficient way to cascade the beforeSend processors on the jQuery.ajaxSetup call?
In fact, in the jQuery document of jQuery Event, setting the beforeSend from the $.ajax call or $.ajaxSetup is called a Local Event, but in the current case, using a $(document).ajaxSend() which is called a Global Event is much more suitable.
Final solution
In the case if you want to add multiple global event processor on ajax send, do not set it on $.ajaxSetup.
Use the ajaxSend event instead!
http://api.jquery.com/ajaxSend/
So the code may look like:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$(document).ajaxSend(function(event, xhr, settings) {
if (!csrfSafeMethod(settings.type) && !settings.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", Cookies.get('csrftoken'));
}
});
If defining a new beforeSend inject in $.ajaxSetup, I can cascade the other ones defined in previous $.ajaxSetup:
(function() {
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
var originBeforeSend = jQuery.ajaxSettings.beforeSend;
jQuery.ajaxSetup({
beforeSend: function(xhr, settings) {
// Call the previous beforeSend processor first.
if(originBeforeSend && originBeforeSend(xhr, settings) === false) {
return false;
}
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", Cookies.get('csrftoken'));
}
}
});
})();
But anyway, if I specified another beforeSend in $.ajax() call, this way has not luck. It was still shadowed.
The following excerpt changes the html of the login button until the response is received from the server.
When testing on my iPhone in an app build with Cordova. The alert is fired before the html of the login button has been updated. In a desktop browser it works as expected.
I've tried setting the cache and async to false but it makes no difference.
I can't think what else to try other than different ajax libraries?
Is there a better library to use for AJAX with cordova?
$.ajax({
data: $data,
cache: false,
async: false,
beforeSend: function() {
$('#btn-login').html('Logging In...');
},
success: function(r, status) {
if (r.status == 'success') {
getUser();
initNavSwipeGestures();
$('#page-index').removeClass('active');
} else {
alert(r.message);
}
},
error: function(data, status) {
alert(status);
}
});
Success is called when it is successful - http://api.jquery.com/jquery.ajax/:
success
Type: Function( PlainObject data, String textStatus, jqXHR jqXHR )
A function to be called if the request succeeds. The function gets passed three arguments: The data returned from the server, formatted according to the dataType parameter; a string describing the status; and the jqXHR (in jQuery 1.4.x, XMLHttpRequest) object. As of jQuery 1.5, the success setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event.
Use "success" if you want to check what data is returned... otherwise you should use "complete".
Something like this should work:
$.ajax({
data: $data,
cache: false,
async: false,
beforeSend: function() {
$('#btn-login').html('Logging In...');
},
success: callback()
},
complete : function() {
getUser();
initNavSwipeGestures();
$('#page-index').removeClass('active');
},
error: function(data, status) {
alert(status);
}
});
var callback = function(response) {
///stuff to do after success here
};
Here's what I ended up with:
$('#form-login').on('submit', function(e) {
e.preventDefault();
if ($.trim($('#email').val()) === '' || $.trim($('#password').val()) === '') {
return;
}
var loginBtn = Ladda.create( document.querySelector('#btn-login') );
$data = $('#form-login').serializeArray();
$.ajax({
data: $data,
beforeSend: function() {
$('#btn-login').addClass('isloading');
loginBtn.start();
},
success: function(r, status) {
setTimeout(loginResponse(r, loginBtn), 500);
},
error: function(data, status) {
if (PLATFORM == 'desktop') {
alert(status);
} else {
loginBtn.stop();
$('#btn-login').removeClass('isloading');
navigator.notification.alert(status, false, false);
}
}
});
});
function loginResponse(r, loginBtn) {
if ($('#btn-login').hasClass('isloading')) {
if (r.status == 'success') {
getUser();
$('#page-index').removeClass('active');
} else {
if (PLATFORM == 'desktop') {
alert(r.message);
} else {
navigator.notification.alert(r.message, false, false);
}
}
loginBtn.stop();
$('#btn-login').removeClass('isloading');
} else {
setTimeout(loginResponse(r, loginBtn), 500);
}
}
Sometimes, in IE, my ajax requests do not send the header X-Requested-With. I searched in Google and found 2 ways to do it. Both ways seem to make sense. I want to know if there is any difference between them.
1) Using ajaxSend
$(document).ajaxSend(function (event, request, settings) {
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
});
2) Using AjaxSetup
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
}
});
If you use the full blown jQuery.ajax() you can use the headers property:
$.ajax({
headers: { "ResponseDataType" : "Json",
"X-Requested-With", "XMLHttpRequest"},
// etc
});
Added DRY version:
(function (window, undefined)
{
function extendedAjax(settings)
{
var defaultSettings = {
headers: { "X-Requested-With": "XMLHttpRequest" }
};
$.extend(defaultSettings, settings);
var jqXHR = $.ajax(settings);
return jqXHR;
}
$.extend({
eajax: extendedAjax
});
})(window);
I'm trying to create a global handler that gets called before the ajax success callback. I do a lot of ajax calls with my app, and if it is an error I return a specific structure, so I need to something to run before success runs to check the response data to see if it contains an error code bit like 1/0
Sample response
{"code": "0", "message": "your code is broken"}
or
{"code": "1", "data": "return some data"}
I can't find a way to do this in jQuery out of the box, looked at prefilters, ajaxSetup and other available methods, but they don't quite pull it off, the bets I could come up with is hacking the ajax method itself a little bit:
var oFn = $.ajax;
$.ajax = function(options, a, b, c)
{
if(options.success)
{
var oFn2 = options.success;
options.success = function(response)
{
//check the response code and do some processing
ajaxPostProcess(response);
//if no error run the success function otherwise don't bother
if(response.code > 0) oFn2(response);
}
}
oFn(options, a, b, c);
};
I've been using this for a while and it works fine, but was wondering if there is a better way to do it, or something I missed in the jQuery docs.
You can build your own AJAX handler instead of using the default ajax:
var ns = {};
ns.ajax = function(options,callback){
var defaults = { //set the defaults
success: function(data){ //hijack the success handler
if(check(data)){ //checks
callback(data); //if pass, call the callback
}
}
};
$.extend(options,defaults); //merge passed options to defaults
return $.ajax(options); //send request
}
so your call, instead of $.ajax, you now use;
ns.ajax({options},function(data){
//do whatever you want with the success data
});
This solution transparently adds a custom success handler to every $.ajax() call using the duck punching technique
(function() {
var _oldAjax = $.ajax;
$.ajax = function(options) {
$.extend(options, {
success: function() {
// do your stuff
}
});
return _oldAjax(options);
};
})();
Here's a couple suggestions:
var MADE_UP_JSON_RESPONSE = {
code: 1,
message: 'my company still uses IE6'
};
function ajaxHandler(resp) {
if (resp.code == 0) ajaxSuccess(resp);
if (resp.code == 1) ajaxFail(resp);
}
function ajaxSuccess(data) {
console.log(data);
}
function ajaxFail(data) {
alert('fml...' + data.message);
}
$(function() {
//
// setup with ajaxSuccess() and call ajax as usual
//
$(document).ajaxSuccess(function() {
ajaxHandler(MADE_UP_JSON_RESPONSE);
});
$.post('/echo/json/');
// ----------------------------------------------------
// or
// ----------------------------------------------------
//
// declare the handler right in your ajax call
//
$.post('/echo/json/', function() {
ajaxHandler(MADE_UP_JSON_RESPONSE);
});
});
Working: http://jsfiddle.net/pF5cb/3/
Here is the most basic example:
$.ajaxSetup({
success: function(data){
//default code here
}
});
Feel free to look up the documentation on $.ajaxSetup()
this is your call to ajax method
function getData(newUrl, newData, callBack) {
$.ajax({
type: 'POST',
contentType: "application/json; charset=utf-8",
url: newUrl,
data: newData,
dataType: "json",
ajaxSuccess: function () { alert('ajaxSuccess'); },
success: function (response) {
callBack(true, response);
if (callBack == null || callBack == undefined) {
callBack(false, null);
}
},
error: function () {
callBack(false, null);
}
});
}
and after that callback success or method success
$(document).ajaxStart(function () {
alert('ajax ajaxStart called');
});
$(document).ajaxSuccess(function () {
alert('ajax gvPerson ajaxSuccess called');
});