jQuery getJSON works locally, but not cross domain
Asked Answered
C

6

12

I've searched FOREVER and can't come up with a definitive answer to my problem. So here it is. I have a JSON file (I went to jsonlint to validate and it says its good) that looks like this (some information modified):

[{
        "position":"1",
        "category":"A",
        "title":"Title to first story",
        "description":"The first story."
    },
    {
        "position":"2",
        "category":"B",
        "title":"Title to second story",
        "description":"The second story"
    },
    {
        "position":"3",
        "category":"B",
        "title":"Title to third story",
        "description":"The third story"
    }
]

I used jQuery to parse through and put on an html page using this function:

$.getJSON('page.json', function(data) {
  var items = [];

  $.each(data.reponse, function(item, i) {
    items.push('<li id="' + i.position + '">' + i.title + ' - ' + i.description + '</li>');
  });

  $('<ul/>', {
    'class': 'my-new-list',
    html: items.join('')
  }).appendTo('body');
});

It works perfectly! Now comes my issue, the JSON file will not be hosted locally, and will in fact be hosted on a separate domain. So I modified my code as follows (after some reading) hoping to get it working:

$.getJSON('http://www.otherdomain.com/page.json?format=json&callback=?', function(data) {
  var items = [];

  $.each(data.reponse, function(item, i) {
    items.push('<li id="' + i.position + '">' + i.title + ' - ' + i.description + '</li>');
  });

  $('<ul/>', {
    'class': 'my-new-list',
    html: items.join('')
  }).appendTo('body');
});

By adding the 'callback' line I stopped getting a "Failed to load resource" error. However, nothing is happening. It is like the function I added isn't even there. I tried to take all of it out and add a simple 'alert(data)', but that didn't even fire. What am I doing wrong? A big problem is that I am 100% limited to just HTML and JavaScript to work (not my choice). Thanks for any help!

EDIT Ok, I don't have ability for the other server to dynamically add anything to the json file. So I modified by hardcoding a function around the json (smaller sample):

storyData(
[{
        "position":"1",
        "category":"A",
        "title":"Title to first story",
        "description":"The first story."
    }
])

Everything now works! Thanks for all the help!

Cane answered 27/7, 2011 at 19:5 Comment(3)
Does the server that your calling support JSONP callbacks? Sounds like it doesn't. Just by appending callback=? to your querystring doesn't make it work. Your server has to return the data in a callback-able format (if that's a word).Inflict
See my answer below. You're going to need access to the service call that you're invoking to get JSONP working. It's not automatic - although I admit that it's made to sound that way from a lot of the documentation out there. :)Inflict
en.wikipedia.org/wiki/Same-origin_policyMila
G
15

You need to look in to JSONP.

Essentially, when you try to load JSON from another domain, it fails because there is a domain boundary you can not cross. To avoid this, you have to PAD it (P in JSONP). Padding it is essentially wrapping it in a function call (where the function name resides on your client.) e.g. a "normal" JSON response (say, for example, getjson.php):

{foo:'bar'}

JSON with a callback of parseJSON becomes (Say, for example, getjson.php?callback=parseJSON):

parseJSON({foo:'bar'})

Notice how the value that was supplied in callback becomes the name of the function your JSON response is now wrapped in.

Then your client will want to pass it to parseJSON, a function that exists on your client (that you've defined). jQuery (and other libraries) try to take care of this for you by generating some "random" function and then sending the response back through your original callback (all this is done under the hood).

If you have control over the server page generating the JSON, implement a callback method so you can supply how the JSON should be wrapped so you can then work with it on your end. (This is only necessary when you're dealing with data from a domain other than the page the client is currently on).


UPDATE

To basically solve the problem you're having, you need to find a way to get your JSON information in to a JSONP call. Without knowing what language your "page.json" is in, here's the pseudo-code logic that it should contain:

if GET_VARIABLE("callback") is supplied

  print GET_VARIABLE("callback") and an open parenthesis
  print normal JSON data
  print closing parenthesis

else

  print normal JSON data

end if

If you decide to hard-code the function name instead of allow it to be supplied in the url as "callback", then you need to remember it. For the next example, let's imagine we named it MyJSONPCallback

Now, in your client code, you can go ahead of use:

$.ajax({
  url: 'http://anotherdomain.com/page.json?format=json',
  dataType: 'json',
  jsonpCallback: 'MyJSONPCallback', // specify the callback name if you're hard-coding it
  success: function(data){
    // we make a successful JSONP call!
  }
});
Godart answered 27/7, 2011 at 19:10 Comment(5)
So I added the callback=? to the function on the HTML page. Can you go into more detail on what needs to be done to the JSON file?Cane
@Dusty: Your file needs to (when supplied a callback parameter [or whatever you'd like to name it]) take the value of that parameter and add it to the beginning of the JSON, in addition to a parenthesis. Then, at the end, add a closing parenthesis. You're essentially turning JSON content in to an elaborate function call, with the JSON data being passed as an argument. (which only needs to be done when domain "xyz.com" is fetching JSON data from "abc.com" (or alike).)Godart
So I need to modify the json file and add like jsonFunction () around it, then in the getJSON function modify the callback to callback=jsonFunction?Cane
@Dusty: The best method is to implement a way to pass a callback parameter to your file. When it's there, make your JSON data wrap in a function with that name. Next best is to hard-code that callback in your file and remember it for use in your javascript code. If you name it "foo", for instance, your website must now have function foo(data){ } somewhere on it, ready and waiting for when the JSONP response comes back.Godart
wtf... josn it is not jsonp... No 'Access-Control-Allow-Origin' header is present on the requested resource.War
S
2

For those using MVC ActionResult to generate JSONP, ASP.NET MVC does not ship with JSONP support out of the box, but it is easy to add with:

http://nikcodes.com/2012/02/29/an-asp-net-mvc-jsonp-actionresult

Sidonie answered 25/5, 2012 at 9:17 Comment(0)
W
0

Browsers don't let this work as a security measure. You could check out JSONP as a way to get around this, though it is a HUGE security risk since it relies on running javascript supplied by the domain you are getting the JSON text from.

Wallace answered 27/7, 2011 at 19:9 Comment(5)
The question is using JSONP - which is why the URL has a callback=? at the end of the querystring.Inflict
Yeah, I thought the callback=? made it JSONP?Cane
@David: My impression (and appears others as well) is that s/he is supplying callback without knowing the effect of doing so (or why they need to). Maybe I am wrong, however.Godart
@Brad - I think you're right. The assumption is that JSONP 'just works', and that's not the case. You have to enable it on the server-side.Inflict
You need the server to format the JSON response by PADDing it (hence the P) in a functionLamontlamontagne
F
0

I have not looked deeply into this issue, but I believe your problem relates to the same-domain-policy... you might want to look into this though : http://james.padolsey.com/javascript/cross-domain-requests-with-jquery/

Forbid answered 27/7, 2011 at 19:10 Comment(0)
I
0

See this article -- you have to supply a valid javascript object wrapped in a function.

http://en.wikipedia.org/wiki/JSONP

You'd want to return something like:

parseResponse({"Name": "Cheeso", "Id" : 1823, "Rank": 7})

But your server-side method would need to know to return that, instead of just the JSON inside. All jQuery does is auto-generate a function name (the ? in the callback parameter) and then eval the "function" that's returned from the server. The server creates the function call with the JSON contained inside.

Inflict answered 27/7, 2011 at 19:16 Comment(0)
N
0

Brad Christie's answer helped me quickly get my code working. I am creating a new entry here since it is little simpler than the other solutions.

Following is the code that I run from http://localhost:5000 -

(function() {
        var api = "http://www.localhost:3000/auget_from_server?format=json";
        var request = $.getJSON( api, {
            secret : 'secret', 
            appId : 'app', 
            emailId : '[email protected]',
            async: false,
            dataType : 'json',
          },
          function(data, result){
            $("div.some_div").append(JSON.stringify(data));
          });

        request.complete(function(d, status){
            console.log('Complete the request and got the data - ' + JSON.stringify(d) + '/' + status, filename);
        });

        request.error(function(err){
            console.log('Error happened - ', filename);
            console.log(err);
        });

        request.success(function( data, status, jqXHR ) {
            $("div.some_div").append(data);
        });


        })();

From the location at http://localhost:3000/auget_from_server, I return the following JSON in response (this part is specific to meteor but it will work for non-meteor servers also) -

this.response.writeHead('200', {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'});
this.response.end(JSON.stringify([{'works_or_not' : 'works', 'name' : 'akaushik', 'application' : 'Qoll', 'message' : 'hello from server', 'currentTime' : dt+''}]));

This prints the following in the logs -

Complete the request and got the data - {"readyState":4,"responseText":"[{\"works_or_not\":\"works\",\"name\":\"akaushik\",\"application\":\"Qoll\",\"message\":\"hello from server\",\"currentTime\":\"Tue Dec 15 2015 23:59:14 GMT-0500 (EST)\"}]","responseJSON":[{"works_or_not":"works","name":"akaushik","application":"Qoll","message":"hello from server","currentTime":"Tue Dec 15 2015 23:59:14 GMT-0500 (EST)"}],"status":200,"statusText":"OK"}/success
Naylor answered 16/12, 2015 at 5:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.