PDA

View Full Version : Ext.data.JsonP.request and X-Requested-With = XMLHttpRequest



IAmCoder
24 Jun 2012, 4:46 AM
I have the following code returning JSON from various servers just fine:


Ext.data.JsonP.request({
url: 'http://search.twitter.com/search.json',
callbackKey: 'callback',
params: {
q: 'Sencha'
},
success: function(result, request) {
if (result.results)
{
alert(result.results[0].from_user_name + ': ' + result.results[0].text);
}
},
failure : function(response) {
alert('failure ');
}
});

But the server I am currently working on requires the 'X-Requested-With' header to be set to 'XMLHttpRequest'. Adding the following code has not helped - it results in a timeout.

headers: {'X-Requested-With': 'XMLHttpRequest'}

I have also tried Ext.Ajax.request to no avail.

Animal
24 Jun 2012, 8:21 AM
JSONP is not an XMLHttpRequest. It's a <script> tag. You cannot do anything to the headers.

Do you really need JSONP?

IAmCoder
24 Jun 2012, 1:53 PM
Thanks! Perhaps not... what should I use instead?

I have also tried using the proxy of a store, something along the lines of:


Ext.define('App.store.dataStore', {
extend: 'Ext.data.Store',
requires: [
'App.model.dataModel'
],
config: {
autoLoad: true,
model: 'App.model.dataModel',
storeId: 'dataStore',
proxy: {
type: 'ajax',
extraParams: {
q: 'Sencha'
},
url: 'http://search.twitter.com/search.json',
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
reader: {
type: 'json'
}
}
}
});

Animal
24 Jun 2012, 2:01 PM
If it's cross-domain, then you will have to use JSONP, and you will not be able to affect the headers.

IAmCoder
24 Jun 2012, 2:35 PM
Ok, thanks. I might be able to get around this by running from file:\\ - what could I use then instead of JSONP?

skirtle
24 Jun 2012, 2:37 PM
The options for cross-domain requests are either JSON-P or CORS. You cannot do a normal AJAX request. For a JSON-P request you cannot include custom headers. These are browser security restrictions, not limitations of the library.

It should also be noted that both JSON-P and CORS require the server to be written to accept those kinds of requests. If you don't have control of the server then you are at the mercy of the communication mechanisms allowed by the third party that does. You cannot just point a JSON-P request at a JSON server, it won't work.

IAmCoder
24 Jun 2012, 4:27 PM
Thanks. I found the CORS flag under the Ext.Ajax.request, as follows:


Ext.Ajax.request({
url: 'http://search.twitter.com/search.json',
method: 'POST',
cors: true,
params: {
q: 'Sencha'
},
success: function(response) {
alert('ExtJS.Ajax, cross domain request: success!');
},
failure: function(response) {
alert('ExtJS.Ajax, cross domain request: failure!');
}
});

But I am using the Ext.Ajax.request incorrectly, as it always fails - even without the CORS flag. Any ideas?

skirtle
24 Jun 2012, 4:31 PM
Is the URL you give in your examples the real URL you're using? Does that Twitter service allow CORS requests? As I explained in my previous post, you can only make a cross-domain request if the service you are contacting allows it.

IAmCoder
24 Jun 2012, 5:47 PM
No, the Twitter service is just a sanity check and only works with the Ext.data.JsonP.request; the service must not allow CORS requests - that would explain it. Thanks for all the info!

They have now removed the requirement for the X-Requested-With header on the server, but I am still getting a timeout with JsonP.request and an error with Ajax.request. I get a valid response back from the REST Console plugin for Google Chrome, with all the default settings - I used to have to set X-Requested-With.

So I must still be doing something wrong. My data proxy is also not returning anything. What other method could I use?

skirtle
24 Jun 2012, 5:56 PM
I feel like we're running round in circles here. Can I just check...

1. You're trying to contact a service over HTTP that has a different origin to the page making the request?
2. The service supports either JSON-P or CORS? If yes, which one?

I strongly recommend that you do some background reading on the same origin policy and how JSON-P and CORS provide solutions to work with cross-origin requests. It sounds to me like you're trying to run before you can walk.

The only alternative to these techniques is to remove the cross-origin aspect altogether by proxying requests through your webserver.

IAmCoder
26 Jun 2012, 5:09 AM
1. Yes, the service is on another domain.
2. And it now supports CORS, according to these guidelines: http://enable-cors.org/#how-apache (https://connect.emailsrvr.com/owa/redir.aspx?C=HRBvDC6JIkGiATbcAJmZXIvF2vgSJ88I1hYPT2MmuCyjG4w-DiKdWEQ9miVDmDkBJIRu9DhKzM8.&URL=http%3a%2f%2fenable-cors.org%2f%23how-apache).

Ever since the X-Requested-With requirement was removed from the server, I have been getting the error: "Uncaught SyntaxError: Unexpected token" when using the datastore (with a JsonP Proxy). But I can see the valid JSON response in Google Chrome Developer Tools. If I save the JSON to file and load the datastore from there, it works and my list loads.

And when I use an Ajax proxy, I get the error: "Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers". We have definitely come full circle!

Here are the response headers:


Transfer-Encoding: chunked
Connection:Keep-Alive
Server:Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Access-Control-Allow-Origin:*
Access-Control-Allow-Credentials:true
Keep-Alive: timeout=15, max=97
Furthermore, I am now able to see the response in the app for the first time using:

var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', 'http://...', true);
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState == 4) {
alert(httpRequest.responseText);
}
};
httpRequest.send();

So, how do I load such a response into a datestore / list? A proxy of type ajax or jsonp and with what settings? I appreciate all the help in getting a running start.

skirtle
26 Jun 2012, 8:20 AM
Why would you expect a JSON-P proxy to work against a server that returns JSON? JSON and JSON-P are not the same thing. I repeat my earlier advice: do some background reading about how JSON-P works.

As for CORS, you can't send custom headers on the initial request. If you try to send such headers it will use an OPTIONS request to check with the server to see whether the headers are allowed. ExtJS adds some custom headers by default so the easiest way to get it to work is to turn them off:


Ext.Ajax.cors = true;
Ext.Ajax.useDefaultXhrHeader = false;

The CORS support in ExtJS is a little broken with IE. You'll need to use a patch a bit like this:


Ext.define('MyApp.data.Connection', {
override: 'Ext.data.Connection',

getXdrInstance: function() {
var xdr = new XDomainRequest();

xdr.readyState = 0;

xdr.getAllResponseHeaders = function() {
return '';
};

xdr.onload = function() {
xdr.readyState = 4;
xdr.status = 200;

if (xdr.onreadystatechange) {
xdr.onreadystatechange();
}
};

xdr.onerror = function() {
xdr.readyState = 4;
xdr.status = 'XDR';

if (xdr.onreadystatechange) {
xdr.onreadystatechange();
}
};

return xdr;
},

newRequest: function (options) {
if ((options.cors || this.cors) && Ext.isIE && Ext.ieVersion >= 8) {
return this.getXdrInstance();
}

return this.getXhrInstance();
}
});

For more info on CORS see:

https://developer.mozilla.org/En/HTTP_access_control