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)));
}
}
});
}
}
}
Related
I'm using a node.js script that load in-built https package. When using it I get error:
XMLHttpRequest cannot load [constructed-api-url]. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'http://localhost:3000' is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute.
I'm using node.js 4.4.3, and its https api docs does not really mention anything about withCredentials.
The script being used is this one.
Is there anyway to set the xhr call's withCredentials to false using node.js https?
I am looking to something analogous to this jquery ajax call (just focusing on the xhr field):
$.ajax({
type: 'POST', async:true,
url: 'https://someapp.constructed.url/token',
dataType: "json",
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
xhrFields: {
withCredentials: true
},
headers: {
'Authorization': 'Basic ' + appInfo
},
success: function (result) {
var token = result.access_token;
//…
},
error: function (req, status, error) {
if (typeof(req) != 'undefined') {
var msg = status || req.responseJSON.error;
//…
}
}
});
There is another very similar example, but this is related to the request package, which I don't want to include in dependencies. Beside, my used script is already using https.
So the answer was there all the time, after all:
After a bit of research, found that node's https package uses practically same options as http, including the withCredentials option (not documented in node's http/https, but part of xhr documentation). It was a matter of including the url option within the options object along the withCredentials option, and then pass the options object as parameter for https.get.
And the constructed code would be more or less as follows (focus on the options variable):
var options = {
url: 'https://my.domain.com/api/endpoint',
withCredentials: false
}
var querystring = '?key=' + [_my_api_key];
var param1 = '&' + [paramKey] + '=' + [paramValue];
var datos;
options.url += querystring;
options.url += param1;
https.get(options, function (res) {
res.on('data', function (data) {
datos += data;
});
res.on('end', function () {
try {
var data = JSON.parse(datos);
} catch (e) {
console.error('Unable to parse response as JSON', e);
}
});
}).on('error', function (e) {
console.error('An error occurred with the request:', e.message);
callback(e.message);
});
In Microsoft Edge, a GET request is not running. I have stepped through the code to the point of the AJAX request being run, and set a breakpoint in the callback(s). However, the code never reaches the callbacks.
I already have a .then() and .fail() setup with callbacks, and tried adding a .done() and .always() with callbacks, but none of the code in the callbacks is running.
I then checked the network tab in dev-tools, and I cannot find the request at all. It seems as though Edge is not firing the request off for some reason.
request = function(options, resolveScope) {
var deferred = $.Deferred();
corsHandler.makeRequest(options)
.done(this._wrap(function(response) {
deferred.resolveWith(resolveScope, [response]); //never gets here
}, this))
.fail(this._wrap(function(response) {
deferred.rejectWith(resolveScope, [response]); //never gets here
}, this));
return deferred;
}
This is what calls the request function above.
ajaxFunc = function(data, scope) {
return request({
url: '/path/to/server',
internalUrl: true,
method: 'GET',
datatype: 'json',
data: data
}, scope);
}
This is the implementation used to make that request.
(function() {
// set data var
return ajaxFunc(data, self)
.then(function(res) { console.log(res); }) //never gets here
.done(function(res) { console.log(res); }) //never gets here
.fail(function(res) { console.log(res); }) //never gets here
.finally(function(res) { console.log(res); }) //never gets here
})();
Here is the cors stuff. (I don't know a whole lot about this.)
corsHandler.makeRequest = function(options) {
// resolve default options
_.defaults(options, {
xhr: null,
corsUrl: null,
url: null,
method: 'GET',
data: {},
success: function() {},
error: function() {},
terminate: false,
binary: false,
headers: {},
internalUrl: false,
datatype: ''
});
// if url is internal, create absolute url from relative url
if (options.internalUrl) {
options.url = this.createAbsoluteInternalUrl(options.url);
}
// resolve cors url or proxy url
options.corsUrl = options.corsUrl || this.getCorsUrl(options.url);
if (!options.corsUrl) {
options.url = this.getProxyUrl(options.url);
options.corsUrl = this.getCorsUrl(options.url);
}
// create xhr
if (!options.xhr && options.corsUrl) {
options.xhr = this.createXhr(options.corsUrl);
}
// create cleanup procedure
var cleanUpAfterRequest = $.proxy(function() {
if (options.terminate) {
options.xhr.destroy();
this._removeCacheXhr(options.corsUrl);
}
}, this);
// prepare deffered object
var deferred = $.Deferred();
deferred
.done(function() {
if (options.success) {
options.success.apply(null, Array.prototype.slice.call(arguments));
}
})
.fail(function() {
if (options.error) {
options.error.apply(null, Array.prototype.slice.call(arguments));
}
});
// make actual request
if (!options.xhr) {
throw 'corsHandler: xhr object was not created or defined to make request';
// this does not happen
}
options.xhr.request(
{
url: options.url,
method: options.method,
data: options.data,
binary: options.binary,
headers: options.headers,
datatype: options.datatype
},
function() {
deferred.resolve.apply(null, Array.prototype.slice.call(arguments));
cleanUpAfterRequest();
},
function() {
deferred.reject.apply(null, Array.prototype.slice.call(arguments));
cleanUpAfterRequest();
}
);
return deferred;
}
UPDATE
It looks like the issue is in easyXDM. waitForReady() is not firing on(window, "message", waitForReady) in edge. I'm looking into the issue more now.
easyXDM snippet:
targetOrigin = getLocation(config.remote);
if (config.isHost) {
// add the event handler for listening
var waitForReady = function(event){
if (event.data == config.channel + "-ready") {
// replace the eventlistener
callerWindow = ("postMessage" in frame.contentWindow) ? frame.contentWindow : frame.contentWindow.document;
un(window, "message", waitForReady);
on(window, "message", _window_onMessage);
setTimeout(function(){
pub.up.callback(true);
}, 0);
}
};
on(window, "message", waitForReady);
// set up the iframe
apply(config.props, {
src: appendQueryParameters(config.remote, {
xdm_e: getLocation(location.href),
xdm_c: config.channel,
xdm_p: 1 // 1 = PostMessage
}),
name: IFRAME_PREFIX + config.channel + "_provider"
});
frame = createFrame(config);
}
The above snippet runs, but the waitForReady method is never called. The only browser it's not called in is Edge (Works in IE8+, Chrome, Safari, FF, and mobile chrome/safari).
It turns out there was a required "hack" that a previous developer wrote into our implementation of easyXDM.
In our implementation of easyXDM, we had to update the Window object on IE because our app launches in an iFrame. As Edge isn't technically a version of IE, our test was failing, so the code was not running to update window to be window.parent in the context of easyXDM.
We are using typeof document.documentMode === 'number' to detect IE, but document.documentMode is undefined in Edge, so we added a navigator.userAgent check for Edge.
This resolved the issue.
I had thought that if dataType was specified as JSON that jQuery therefore expects a JSON response. Is that correct? http://api.jquery.com/jquery.ajax/ seems clear on the point.
In the function below I have tried specifying no dataType (for jQuery "Intelligent Guess") and explicitly to JSON (as seen below).
Both result in
"Refused to execute script from [SNIP] because its MIME type ('application/json') is not executable, and strict MIME type checking is enabled."
This function previously worked for some years so either jQuery version changes or the headers sent from the API server must be causing?
How do I adjust so the sent MIME type 'application/json' is accepted?
function refresh() {
$.post('https://api.example.com/api/v1/screenshot/create?callback=?',
{
'url': 'http://example.org/reports_scorecard_blank_HTMLMAKER.php?CTN=' + $('#CTN').val(),
'dataType': "json",
'size': 'page',
'key': 'MY_KEY_HERE',
'screen_width': 1900,
'flash_delay' : 0,
'delay' : 0,
'instance_id' : 65,
},
function(remote, textStatus) {
console.dir(remote);
console.log('textStatus '+textStatus);
if (remote.error) {
clearInterval(interval);
$('#cardMaking').hide();
$('#error').html(remote.error).show();
return;
}
if (textStatus != 'success') {
setTimeout(function() { refresh(); }, 5 * 1000);
return;
}
if (remote.queue == 0) {
$('#cardMaking').html('DONE!');
}
else if (remote.queue == 1) {
$('#cardMaking').html('< 1 minute');
}
else if (remote.queue > 1) {
$('#cardMaking').html('< ' + remote.queue + ' minutes');
}
if (remote.status == 'finished') {
clearInterval(interval);
$('#final_url').html( '<i>' + (remote.final_url || $('#url').val()) + '</i>');
$('#summary').show();
link = 'SOME_HTML_HERE';
$('#cardMaking').html(link);
// now fetch image
$.post('reports_scorecard_blank_IMAGE_FETCHER.php',
{
'remote_id': remote.id,
'CTN': $('#CTN').val()
}, function(data) {
console.log("Fetcher response: " + data);
}
);
}
}, 'json'
);
}
You've included callback=? in the URL which overrides everything else and causes jQuery to make a JSONP GET request instead of an XHR POST request.
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);
Below is an existing jquery code in our code base.
$("#download_").click( function() {
$("#error").html('');
$.ajax({
type : "GET",
cache : false,
async : false,
url : "/download",
success : function(data) {
var json_obj = $.parseJSON(data);
if(json_obj !== undefined && json_obj != null){
if(json_obj.download=="success"){
location=json_obj.url;
}
}
},
error : function(data) {
// TODO
$("#error").html(failed);
}
});
});
Here, In case of error (marked as TODO), I want to check if the http status is 404, then I need to redirect user to different url.
Can any one tell me how do I get the http status in this error: function(data) method?
Thanks!
Did you even look at the docs?
$.ajax({
...
statusCode: {
404: function() {
alert('page not found');
}
}
});
try: statusCode
from documentation:
$.ajax({
statusCode: {
404: function() {
alert('page not found');
}
}
});
http://api.jquery.com/jQuery.ajax/
EDIT:
Now that I think about it, do you only want to redirect if it's a 404? What about other error codes?