Backbone.js won't make cross-host requests? - javascript

I've been playing with Backbone in my Chrome console and running into a cross-domain problem that I can't figure out.
The host I'm connecting to presumably correctly implements CORS because a raw XHR request returns the expected JSON:
var http = new XMLHttpRequest();
http.open('GET', 'http://example.com:3000/entities/item/15.json', true);
http.onreadystatechange = function(evt) { console.log(evt); }
http.send();
(logs 3 XHR progress events on the console with the correct data in the response)
But when I do the following with Backbone the browser doesn't like it:
var Item = Backbone.Model.extend({});
var ItemsCollection = Backbone.Collection.extend({
model: Item,
url: 'http://example.com:3000/entities/item/'
});
var items = new ItemsCollection();
items.fetch();
(returns XMLHttpRequest cannot load http://example.com:3000/entities/item/. Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin.)
Is there anything I need to do to tell Backbone to work with CORS? This error seems to have come before the browser even made a request, so I don't think it's a server config error.

I hope one of these helps (I didn't try yet):
1. Overriding Backbone.js sync to allow Cross Origin
(function() {
var proxiedSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
options || (options = {});
if (!options.crossDomain) {
options.crossDomain = true;
}
if (!options.xhrFields) {
options.xhrFields = {withCredentials:true};
}
return proxiedSync(method, model, options);
};
})();
2. Cross domain CORS support for backbone.js
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
options.crossDomain ={
crossDomain: true
};
options.xhrFields = {
withCredentials: true
};
});

hey you can use something like:
var PostsCollection = Backbone.Collection.extend({
initialize: function(models, options) {
//this.id = options.id;
},
url: 'http://myapi/api/get_posts/?count=8',
});
posts = new PostsCollection();
posts.fetch({
dataType: 'jsonp',
success : function (data) {
console.log(data);
}
});
the key is we need to use 'jsonp'

It turns out the URLs I was requesting with Backbone were slightly different than those requested via the XHR (they were missing a queryarg). This caused the server to 500, which doesn't have the CORS headers. Chrome didn't show the HTTP request at all in the network tab of the debug panel, so I was extremely confused.
Fixing the 500 on the server made this work again.

Related

Javascript send post data in asynchronous environment

So my Problem is, that I get data from another server and im trying to send this received data to the same url to be able to retreive the data in PHP. I tried it with fetch(), $.post(), $.ajax(). Every time my xhr request is being cancelled by the browser for some unknown reason. Here is my current code:
function generateCodes()
{
var someVAR = document.getElementById("length").value;
var someResult = createDataPromise("someVAR=" + someVAR);
someResult.then((data) =>
{
var jsonData = JSON.stringify(data);
$.ajax(
{
url: "sameURL",
method: "post",
data: jsonData,
success: function(data)
{
console.log(data);
}
});
});
someResult.catch((error) =>
{
console.error("Server responded with error: " + error);
});
}
function createDataPromise(data)
{
return new Promise((resolve, reject) =>
{
const xhr = new XMLHttpRequest();
xhr.open("POST", "externalServer");
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = () => resolve(JSON.parse(xhr.responseText));
xhr.onerror = () => reject(xhr.statusText);
xhr.send(data);
});
}
Is there any other possible solution to access the javascript object in PHP? (It is not possible to convert the varible because the javascript is in a pure js file)
It's possible that the browser is blocking your request because of CORS. Modifying your request to be CORS compliant or making sure the remote response includes an Access-Control-Allow-Origin: * header would fix this.
Making sure your request is considered a simple request would eliminate the possibility that it is being denied by a preflight request since you only need to send data and not receive it back in the javascript.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

How can I upload files to Amazon S3 using Cordova FileTransfer?

I'm following Heroku's tutorial on direct uploads to Amazon S3.
After getting a signed request from AWS through the Node.js app, they use a "normal" XMLHttpRequest to send the file.
This is their function:
function upload_file(file, signed_request, url){
var xhr = new XMLHttpRequest();
xhr.open("PUT", signed_request);
xhr.setRequestHeader('x-amz-acl', 'public-read');
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById("preview").src = url;
document.getElementById("avatar_url").value = url;
}
};
xhr.onerror = function() {
alert("Could not upload file.");
};
xhr.send(file);
}
Now, I'm working with Cordova and, since I don't get a File object from the camera plugin, but only the file URI, I used Cordova's FileTransfer to upload pictures to my Node.js app with multipart/form-data and it worked fine.
However, I can't manage to make it work for Amazon S3.
Here's what I have:
$scope.uploadPhoto = function () {
$scope.getSignedRequest(function (signedRequest) {
if (!signedRequest)
return;
var options = new FileUploadOptions();
options.fileKey = 'file';
options.httpMethod = 'PUT';
options.mimeType = 'image/jpeg';
options.headers = {
'x-amz-acl': 'public-read'
};
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload($scope.photoURI, encodeURI(signedRequest.signed_request), function () {
// success
}, function () {
// error
}, options);
});
};
I've tried both chunkedMode = true and chunkedMode = false, but neither the success nor the error callback is called.
So, is there a way to upload a file to S3 with FileTransfer?
Do I actually need the signed request or is it only necessary if I use XHR?
Any hint is appreciated.
I ended up with this function in Cordova:
$scope.uploadPhoto = function () {
$scope.getSignedRequest(function (signedRequest) {
if (!signedRequest)
return;
var options = new FileUploadOptions();
options.chunkedMode = false;
options.httpMethod = 'PUT';
options.headers = {
'Content-Type': 'image/jpeg',
'X-Amz-Acl': 'public-read'
};
var ft = new FileTransfer();
ft.upload($scope.photoURI, signedRequest.signedUrl, function () {
$scope.$apply(function () {
// success
});
}, function () {
$scope.$apply(function () {
// failure
});
}, options);
});
};
The important bits are setting the Content-Type header, so that multipart/form-data won't be used, and chunkedMode = false to send the file with a single request.
EDIT: Removed changes to the plugin code which were, in hindsight, useless (outdated plugin).
Not able to add comment:
Strange. Works for me using $cordovaFileTransfer.upload. I don't have the 'x-amz-acl': 'public-read' header. Also I don't use encodeURI on the signed url. Have you been able to debug it? See any errors? I used chrome://inspect and port forwarding to connect to my app running on the phone, so I was able to debug the response from Amazon. Might be another reason why it's failing.

AngularJS injector issue

I'm trying to call a WebAPI from AngularJS. It is a simple GET request, and I have enabled CORS on the server.
I am getting the $injector:unpr Unknown Provider error.
I have an angular module called raterModule.js:
var raterModule = angular.module('rater', []);
a service called corsService.js that uses the snippet from enable-cors.org:
raterModule.factory("corsService",
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
if ("withCredentials" in xhr) {
xhr.withCredentials = true;
xhr.open(method, url, true);
}
// Otherwise, check if XDomainRequest. XDomainRequest only exists in IE, and is IE's way of making CORS requests.
else if (typeof XDomainRequest != "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
}
// Otherwise, CORS is not supported by the browser.
else {
xhr = null;
}
return xhr;
}
)
and finally a controller called menuController.js:
raterModule.controller("menuCtrl",["$scope","$http","corsService",
function menuCtrl($scope, $http, corsService) {
var xhr = corsService.createCORSRequest('GET', 'http://localhost:50942/api/menu/items');
if (!xhr) {
throw new Error('CORS not supported');
}
// Getting menu items
$http.get(xhr)
.success(function (data, status, headers, config) {
$scope.menu = data;
})
.error(function (data, status, headers, config) {
alert("error getting menu items");
});
}
]);
These are all included at the bottom of an HTML index page. What I was hoping to happen would be that corsService is injected into menuCtrl, and then menuCtrl is added to the raterModule.
The menuCtrl would use corsService to create a request which is then sent to the WebAPI, avoiding the same-origin policy problem.
I'm sure it is something simple but in my ignorance I cannot see it.
You've got an error because Angular expect that you gonna inject providers named method and url in your createCORSRequest, not function parameters.
So, you can review your corsService in following way:
raterModule.factory(`corsService`, function() {
var service = {};
service.createCORSRequest = fucntion(method, url) {
// your logic here
};
return service;
})

Force "charset=x-user-defined'" on jQuery Ajax Post

I am trying to call a Hessian web service from a Javascript application, but I'm having issues parsing the response, since jQuery is treating the response as text and stripping the first bytes of it.
In my research, I have found out that you need to set the charset as 'charset=x-user-defined' in order to the browser leave my bytes as is. But, according the ajax docs:
Sending Data to the Server
By default, Ajax requests are sent using the GET HTTP method. If the
POST method is required, the method can be specified by setting a
value for the type option. This option affects how the contents of the
data option are sent to the server. POST data will always be
transmitted to the server using UTF-8 charset, per the W3C
XMLHTTPRequest standard.
And indeed, the charset is not changing regardless of the settings I used. I have tried the following, separately and all at once, with no luck
$.ajax({
type : 'POST',
url : url,
timeout : 3000,
data : parameters,
contentType : "x-application/hessian; charset=x-user-defined'",
mimeType: 'text/plain; charset=x-user-defined',
headers: {
Accept : "text/plain; charset=x-user-defined",
"Content-Type": "text/plain; charset=x-user-defined"
},
beforeSend : function(xhr) {
xhr.overrideMimeType("text/plain; charset=x-user-defined");
}
})
Also I tried to mess around with the data converters and custom contenttypes defined in jQuery, with no succes.
It appears that as per the standard, I will not be able to do this. It works with GET but not with POST, and the Hessian protocol requires POST.
Do you have any ideas? Or do I need to start to build my XHR method form scratch?
Turns out that I was making a silly mistake somewhere else. But anyhow, I found a sweet way for handling binary data on request and responses, from here.
define(function() {
// Do setup work here
function configurationException(message) {
throw new Error(message + " missing from configuration object");
}
return {
post : function(config) {
if (config) {
var url = config.url || configurationException("url");
var done = config.done || configurationException("callback function");
var timeout = config.timeout || 10000;
var data;
if (config.data) {
data = config.data;
} else {
data = null;
console.warn('No data is specified in binaryPost');
}
var request = new XMLHttpRequest();
request.open("POST", url, true);
request.responseType = "arraybuffer";
request.setRequestHeader("Content-Type", "x-application/hessian;");
request.onload = function(oEvent) {
var arrayBuffer = request.response; // Note: not oReq.responseText
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
done(byteArray);
}
};
request.send(data);
} else {
throw new Error("Configuration object is missing");
}
}
};
});
Hope you find it useful

How do I check if file exists in jQuery or pure JavaScript?

How do I check if a file on my server exists in jQuery or pure JavaScript?
With jQuery:
$.ajax({
url:'http://www.example.com/somefile.ext',
type:'HEAD',
error: function()
{
//file not exists
},
success: function()
{
//file exists
}
});
EDIT:
Here is the code for checking 404 status, without using jQuery
function UrlExists(url)
{
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status!=404;
}
Small changes and it could check for status HTTP status code 200 (success), instead.
EDIT 2: Since sync XMLHttpRequest is deprecated, you can add a utility method like this to do it async:
function executeIfFileExist(src, callback) {
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (this.readyState === this.DONE) {
callback()
}
}
xhr.open('HEAD', src)
}
A similar and more up-to-date approach.
$.get(url)
.done(function() {
// exists code
}).fail(function() {
// not exists code
})
This works for me:
function ImageExist(url)
{
var img = new Image();
img.src = url;
return img.height != 0;
}
i used this script to add alternative image
function imgError()
{
alert('The image could not be loaded.');
}
HTML:
<img src="image.gif" onerror="imgError()" />
http://wap.w3schools.com/jsref/event_onerror.asp
So long as you're testing files on the same domain this should work:
function fileExists(url) {
if(url){
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.send();
return req.status==200;
} else {
return false;
}
}
Please note, this example is using a GET request, which besides getting the headers (all you need to check weather the file exists) gets the whole file.
If the file is big enough this method can take a while to complete.
The better way to do this would be changing this line: req.open('GET', url, false); to req.open('HEAD', url, false);
Here's how to do it ES7 way, if you're using Babel transpiler or Typescript 2:
async function isUrlFound(url) {
try {
const response = await fetch(url, {
method: 'HEAD',
cache: 'no-cache'
});
return response.status === 200;
} catch(error) {
// console.log(error);
return false;
}
}
Then inside your other async scope, you can easily check whether url exist:
const isValidUrl = await isUrlFound('http://www.example.com/somefile.ext');
console.log(isValidUrl); // true || false
I was getting a cross domain permissions issue when trying to run the answer to this question so I went with:
function UrlExists(url) {
$('<img src="'+ url +'">').load(function() {
return true;
}).bind('error', function() {
return false;
});
}
It seems to work great, hope this helps someone!
All the other answers can fail due to cache!
Making a HTTP request to a file on server can be intercepted with HTTP cache and the cached response is then returned. But the file may be deleted on the server in the meantime, so ignoring cache may return false positive results.
Proper solution would be to create non-cached HTTP HEAD request. Nik Sumeiko's answer uses no-cache header which means that the response can be cached, but must be revalidated before reuse. In this case the server may return 304: Not Modified, which is not 200: OK and thus false negative.
To avoid cache, the correct header is Cache-Control: no-store
File can exist without HTTP 200 response
You should also keep in mind that redirection (301: Moved Permanently, 307: Temporary Redirect or 308: Permanent Redirect) may occur, so the file can exist elsewhere and may be returned from different location: depending on the use-case, one may choose to follow redirection instead of returning false in this case.
Also keep in mind that background requests will be blocked if you check file existence on different domain and its CORS policy is not opened to your server. In this case 403: Forbidden is usually returned, which doesn't mean file does not exist but file is unavailable. Last but not least, the same applies to 500: Internal Server Error response, which means that the HTTP server failed to handle the request, but the file can be available otherwise, like by FTP.
The following code will return true if the file exists, false if not or undefined if the file is unavailable or redirected:
const fileExists = file =>
fetch(file, {method: 'HEAD', cache: 'no-store'})
.then(response => ({200: true, 404: false})[response.status])
.catch(exception => undefined);
fileExists("yourFile.html").then(yes => yes && alert("yourFile.html exists"));
// or in the async scope...
let yourFileExists = await fileExists("yourFile.html");
if(yourFileExists) console.log("It is there!")
else if(yourFileExists===false) console.log("Nope, it was deleted.");
else console.log("You are not worthy the answer, puny human!");
Modern and obsolete approaches
Since we live in the future now, I would also recommend:
$.ajax() obsolete, don't use in new projects
XMLHttpRequest() obsolete, don't use in new projects
fetch() modern approach, use it if you are free to choose
Note GET/POST methods (like <img src...>) are not appropriate here as they waste network traffic by downloading the file (imagine the worst scenario with high resolution photo and user with paid mobile data in area with poor connectivity)
Note Modern PWA approach is to use Cache API with serviceWorker's fetch event which intercepts the communication between the client and HTTP cache. In the example in the link, there should be something like
if(event.request.cache=="no-store") {
// avoid cache storage and pass the request in the chain
// client - cache storage - HTTP cache - server
return fetch(event.request);
}
Without this, the cache settings may be ignored and there may be no way to detect the remote file existence from the main thread with the serviceWorker running - illustrated
here.
JavaScript function to check if a file exists:
function doesFileExist(urlToFile)
{
var xhr = new XMLHttpRequest();
xhr.open('HEAD', urlToFile, false);
xhr.send();
if (xhr.status == "404") {
console.log("File doesn't exist");
return false;
} else {
console.log("File exists");
return true;
}
}
I use this script to check if a file exists (also it handles the cross origin issue):
$.ajax(url, {
method: 'GET',
dataType: 'jsonp'
})
.done(function(response) {
// exists code
}).fail(function(response) {
// doesnt exist
})
Note that the following syntax error is thrown when the file being checked doesn't contain JSON.
Uncaught SyntaxError: Unexpected token <
For a client computer this can be achieved by:
try
{
var myObject, f;
myObject = new ActiveXObject("Scripting.FileSystemObject");
f = myObject.GetFile("C:\\img.txt");
f.Move("E:\\jarvis\\Images\\");
}
catch(err)
{
alert("file does not exist")
}
This is my program to transfer a file to a specific location and shows alert if it does not exist
An async call to see if a file exists is the better approach, because it doesn't degrade the user experience by waiting for a response from the server. If you make a call to .open with the third parameter set to false (as in many examples above, for example http.open('HEAD', url, false); ), this is a synchronous call, and you get a warning in the browser console.
A better approach is:
function fetchStatus( address ) {
var client = new XMLHttpRequest();
client.onload = function() {
// in case of network errors this might not give reliable results
returnStatus( this.status );
}
client.open( "HEAD", address, true );
client.send();
}
function returnStatus( status ) {
if ( status === 200 ) {
console.log( 'file exists!' );
}
else {
console.log( 'file does not exist! status: ' + status );
}
}
source: https://xhr.spec.whatwg.org/
This is an adaptation to the accepted answer, but I couldn't get what I needed from the answer, and had to test this worked as it was a hunch, so i'm putting my solution up here.
We needed to verify a local file existed, and only allow the file (a PDF) to open if it existed. If you omit the URL of the website, the browser will automatically determine the host name - making it work in localhost and on the server:
$.ajax({
url: 'YourFolderOnWebsite/' + SomeDynamicVariable + '.pdf',
type: 'HEAD',
error: function () {
//file not exists
alert('PDF does not exist');
},
success: function () {
//file exists
window.open('YourFolderOnWebsite/' + SomeDynamicVariable + '.pdf', "_blank", "fullscreen=yes");
}
});
First creates the function
$.UrlExists = function(url) {
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status!=404;
}
After using the function as follows
if($.UrlExists("urlimg")){
foto = "img1.jpg";
}else{
foto = "img2.jpg";
}
$('<img>').attr('src',foto);
Here's my working Async Pure Javascript from 2020
function testFileExists(src, successFunc, failFunc) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState === this.DONE) {
if (xhr.status === 200) {
successFunc(xhr);
} else {
failFunc(xhr);
}
}
}
// xhr.error = function() {
// failFunc(xhr);
// }
// xhr.onabort = function() {
// failFunc(xhr);
// }
// xhr.timeout = function() {
// failFunc(xhr);
// }
xhr.timeout = 5000; // TIMEOUT SET TO PREFERENCE (5 SEC)
xhr.open('HEAD', src, true);
xhr.send(null); // VERY IMPORTANT
}
function fileExists(xhr) {
alert("File exists !! Yay !!");
}
function fileNotFound(xhr) {
alert("Cannot find the file, bummer");
}
testFileExists("test.html", fileExists, fileNotFound);
I could not force it to come back with any of the abort, error, or timeout callbacks.
Each one of these returned a main status code of 0, in the test above, so
I removed them. You can experiment.
I set the timeout to 5 seconds as the default seems to be very excessive.
With the Async call, it doesn't seem to do anything without the send() command.
What you'd have to do is send a request to the server for it to do the check, and then send back the result to you.
What type of server are you trying to communicate with? You may need to write a small service to respond to the request.
This doesn't address the OP's question, but for anyone who is returning results from a database: here's a simple method I used.
If the user didn't upload an avatar the avatar field would be NULL, so I'd insert a default avatar image from the img directory.
function getAvatar(avatar) {
if(avatar == null) {
return '/img/avatar.jpg';
} else {
return '/avi/' + avatar;
}
}
then
<img src="' + getAvatar(data.user.avatar) + '" alt="">
It works for me, use iframe to ignore browsers show GET error message
var imgFrame = $('<iframe><img src="' + path + '" /></iframe>');
if ($(imgFrame).find('img').attr('width') > 0) {
// do something
} else {
// do something
}
I wanted a function that would return a boolean, I encountered problems related to closure and asynchronicity. I solved this way:
checkFileExistence= function (file){
result=false;
jQuery.ajaxSetup({async:false});
$.get(file)
.done(function() {
result=true;
})
.fail(function() {
result=false;
})
jQuery.ajaxSetup({async:true});
return(result);
},

Categories

Resources