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);
Related
I'm switching from ajax to axios, this is the code I wanted to convert.
$.ajax({
method: 'GET',
url: SPSConstants.SPS_BASE_URL + 'api/test?filter=' + encodeURIComponent(JSON.stringify(filter)),
dataType: 'json',
beforeSend: function (xhr) {
SPSUtils.getReqHeader(xhr, self, this, true);
},
}).done(function (result) {
var resultObj = JSON.parse(JSON.stringify(result));
let totalRecords = resultObj.pageInfo.total;
let currentPage = self.state.currentPage;
let currentRecords = resultObj.tollFreeNumberMasters;
if (totalRecords !== 0 && currentRecords.length === 0) {
self.setState({ currentPage: 1, pageInfo: { ...pageInfo, offset: 0 } }, () => {
self.loadTollFreeNumberTblData();
});
}
})
I've worked with axios earlier, so its not big of a deal. The only problem I'm facing is converting this part of ajax code into axios.
beforeSend: function (xhr) {
SPSUtils.getReqHeader(xhr, self, this, true);
},
is there any beforesend equivalent in axios?
It seems that you want to get the xhr object before the http request.
It is the Interceptors that functions similarly in the Axios.
axios.interceptors.request.use(config => {
console.log(config);
});
However, if you set the interceptors function on an Axios instance, the callback will work for all requests from that instance. To make callback work only for specific requests, you need to create and use a new instance of Axios.
const newAxios = axios.create();
newAxios.interceptors.request.use(config => {
console.log(config);
});
I'm trying to send a post request to the rest api with some custom fields. THis is my code
let newCharacter = {
'title': $('.create-char-name').val(),
'acf': {
'char_class': $('#char-class').val(),
'char_subclass': $('#char-subclass').val(),
'char_level': $('#char-level').val()
},
'status': 'publish'
}
$.ajax({
beforeSend: (xhr) => {
xhr.setRequestHeader('X-WP-Nonce', spbk_data.nonce);
},
url: spbk_data.root_url + '/wp-json/wp/v2/character/',
method: 'POST',
data: newCharacter,
success: (response) => {
console.log("congrats");
console.log(response);
},
error: (response) => {
console.log("Sorry");
console.log(response);
}
});
The request goes through without any problems, but when I check the json, the "acf" field returns false.
I'm using the acf to wp api plugin, if that information is useful.
The only info I found about this issue was this post, but I don't really understand what the answer meant. I tried adding xhr.setRequestHeader('Content-Type', application/json); (I also tried with lower case initials), below the nonce, like the post seems to suggest, but that returns this error:
"{"code":"rest_invalid_json","message":"Invalid JSON body passed.","data":{"status":400,"json_error_code":4,"json_error_message":"Syntax error"}}"
Try something like below:
function NewCharacter(){
this.title;
this.acf;
this.status;
}
function CharInfo(){
this.char_class;
this.char_subclass;
this.char_level;
}
var charInfo = new CharInfo();
charInfo.char_class=$('#char-class').val();
charInfo.char_subclass=$('#char-subclass').val();
charInfo.char_level=$('#char-level').val();
var newCharacter = new NewCharacter();
newCharacter.title=$('.create-char-name').val();
newCharacter.acf=charInfo
newCharacter.status="publish";
$.ajax({
beforeSend: (xhr) => {
xhr.setRequestHeader('X-WP-Nonce', spbk_data.nonce);
},
url: spbk_data.root_url + '/wp-json/wp/v2/character/',
method: 'POST',
data: JSON.stringify(newCharacter),
success: (response) => {
console.log("congrats");
console.log(response);
},
error: (response) => {
console.log("Sorry");
console.log(response);
}
});
Yeah, I'm kinda dumb. I tried another plugin to make the bridge between acf and the rest api, and it worked.
It came to my mind many times to try another plugin, but I thought "they do the same thing, there's no point in trying that". It goes to show that you shouldn't just brush off solutions that seem too obvious or stupid.
I am working on a phonegap app which uses Backbone JS.
During ajax calls the header contains:
"Origin":"file://"
Which is not supported by the server. I tried to set Origin header as null but in chrome it is not allowed.
Backbone.ajax = function() {
arguments[0].headers = {
'Accept': "application/json",
'Origin': "null"
};
return Backbone.$.ajax.apply(Backbone.$, arguments);
};
Which throws error:
Refused to set unsafe header "Origin"
Only work around I can think of to solve this issue is to use the cordovaHttp plugin. But I am unable to figure out how to override Backbone.ajax to use cordovHTTP.
Link to the cordova plugin:
https://github.com/silkimen/cordova-plugin-advanced-http
Although this is related to CORS, my question is specific to Overriding Backbone ajax method using the cordovaHttpPlugin
It works:
function isPhoneGap() {
return (window.cordova || window.PhoneGap || window.phonegap)
&& /^file:\/{3}[^\/]/i.test(window.location.href)
&& /ios|iphone|ipod|ipad|android/i.test(navigator.userAgent);
}
Backbone.sync = function( method, model, options ) {
if(method == "read"){
if(isPhoneGap()){
cordova.plugin.http.get(model.url, {}, { Origin: "null" }, function(response) {
// prints 200
console.log(response.status);
try {
options.success(JSON.parse(response.data));
} catch(e) {
console.error("JSON parsing error");
}
}, function(response) {
console.log(response.status);
console.log(response.error);
});
}else{
$.ajax({
type : 'GET',
url : model.url,
dataType : 'json',
success : function(data) {
console.log(data);
if(model instanceof Backbone.Collection){
model.reset(JSON.parse(JSON.stringify(data)));
}else{
model.set(JSON.parse(JSON.stringify(data)));
}
}
});
}
}
}
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.
How would I setup a Backbone collection to always send a content type of "application/json" in all requests?
code I have tried:
myCollection = Backbone.Collection.extend({
headers: {"Content-Type": 'application/json'},
url: '/foo'
});
and:
myCollection = Backbone.Collection.extend({
contentType: 'application/json',
url: '/foo'
});
however on fetch() there is no content type being sent?
One way to do it is overriding Backbone.sync
var _sync = Backbone.sync;
Backbone.sync = function(method, model, options) {
options.beforeSend = function(xhr) {
xhr.setRequestHeader('Content-Type': 'application/json');
};
_sync.apply(Backbone, arguments);
}
Then if you want to define your Content-Type per-model, you could do this :
var _sync = Backbone.sync;
Backbone.sync = function(method, model, options) {
options.beforeSend = function(xhr) {
var contentType = model.contentType || null;
// var contentType = model.contentType || 'application/json'; // default to json
if(contentType) {
xhr.setRequestHeader('Content-Type': contentType);
}
};
_sync.apply(Backbone, arguments);
}
Though, if you're looking for a way to tell your server to return JSON on fetch() calls, what you're really looking for is the Accept header.
If you want to do it globally for all your Backbone models and collections then you could provide your own Backbone.ajax function:
ajax Backbone.ajax = function(request) { ... };
If you want to use a custom AJAX function, or your endpoint doesn't support the jQuery.ajax API and you need to tweak things, you can do so by setting Backbone.ajax.
Something like this:
Backbone.ajax = function(request) {
request = _({ contentType: 'application/json' }).defaults(request);
return Backbone.$.ajax.call(Backbone.$, request);
};
The _.defaults call will make a copy of request with contentType always set to 'application/json', if you don't copy request you'll end up alter data that you don't necessarily own; altering request may be harmless but good habits are good habits.