3

I'm having trouble retrieving JSONP data from one of my sites. On Site A data is being provided by the following MVC2 controller action:

public JsonResult JsonList(string key) {
    var consultants = rep.FindAll().Where(c => c.IsActive).Select(c => new ConsultantJsonItem { Firstname = c.Firstname, Surname = c.Surname });

    return Json(consultants, "application/json");
}

On Site B, I'm using jQuery to retrieve the JSON, like this:

$.ajax({
    url: 'http://www.siteA.com/controller/jsonaction/',
    dataType: 'JSONP',
    crossDomain: true,
    success: function (json) {
        alert("success"); // THIS DISPLAYS
        alert(json); // THIS IS ALWAYS EMPTY
    },
    error: function (xhr, status, error) {
        alert(status); // NOT CALLED
    }
});

I can see in the Firebug console that the response completed correctly with a 200 code, and I can see that the content length of the response is 11516 bytes, yet the response tab is completely empty and jQuery will not give me any data to work with.

Can anyone tell me why this is?

Note: This site is using jQuery 1.4.2

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Are you *sure* `alert('success')` fires? It should **not** fire because you're returning JSON, not JSONP; see my (now deleted) answer... – Matt Mar 08 '12 at 15:46
  • It definitely always hits the success handler - that's why it's confusing me so much. I am using jQuery 1.4.2 on this site, I'm not sure if that has some bearing on this. – Rory McCrossan Mar 08 '12 at 15:50
  • `@`Rory: The [`crossDomain` option was introduced in 1.5](http://api.jquery.com/jQuery.ajax/), so there's no point including it in your case. jQuery 1.4.2 also checks for `s.dataType === "jsonp"`, rather than "JSONP"... I'm not sure what that'll change your end? – Matt Mar 08 '12 at 15:55
  • @Matt You're right, I've removed `crossDomain`, to be honest I only added that as I was clutching at straws reading through the API. Originally I has `jsonp` set, however this gave me nothing back at all (for reasons now obvious), changing to `JSONP` gave me a response back. I assume the change caused jQuery to use it's 'best guess' algorithm as to the datatype being returned. – Rory McCrossan Mar 08 '12 at 16:00
  • `@`Rory: I dug deep to curb my own curiosity and worked out how it all happened (and explained it in my answer :P) – Matt Mar 08 '12 at 16:12

2 Answers2

3

You are returning JSON which is not the same as JSONP:

return Json(consultants, "application/json");

The fact that you set dataType: 'JSONP' on the client is only half of the work that you need to do. The second half is on the server.

Checkout the following answer which illustrates how you could create a custom JsonpResult which will use the callback query string parameter to wrap the response into JSONP.

So:

public class JsonpResult : ActionResult
{
    private readonly object _obj;

    public JsonpResult(object obj)
    {
        _obj = obj;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var serializer = new JavaScriptSerializer();
        var callbackname = context.HttpContext.Request["callback"];
        var jsonp = string.Format("{0}({1})", callbackname, serializer.Serialize(_obj));
        var response = context.HttpContext.Response;
        response.ContentType = "application/json";
        response.Write(jsonp);
    }
}

and then:

public ActionResult JsonList(string key) 
{
    var consultants = rep.FindAll().Where(c => c.IsActive).Select(c => new ConsultantJsonItem { Firstname = c.Firstname, Surname = c.Surname });
    return new JsonpResult(consultants);
}

and on the client:

$.ajax({
    url: 'http://www.siteA.com/controller/jsonaction/',
    jsonp: 'callback',
    dataType: 'jsonp',
    success: function (json) {
        alert(json);
    }
});
Community
  • 1
  • 1
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
2

The response you're giving is not JSONP.. it's JSON. JSONP needs to be wrapped in a function name;

foo({ "valid": "json" });

More details on why JSONP needs to be surrounded in a function name (i.e. the padding), and how it bypasses the SOP restrictions can be seen in this answer.

To return JSONP in ASP.NET it seems you need to do it manually, such as in this question; ASP.net MVC returning JSONP

In your case, you shouldn't have even seen the "success" alert; but this was down to a bug in the version of jQuery you're using (1.4.2). By jQuery not supporting crossDomain until 1.5, and it checking specifically for a dataType of jsonp (rather than JSONP), you ultimately ended up making a standard JSON request over XMLHttpRequest, which the browser was aborting to enforce SOP. However, jQuery was misinterpreting the state of the aborted object as completed.

Ultimately, this led to the success handler being called, whilst data was uninitialized ("").

Community
  • 1
  • 1
Matt
  • 74,352
  • 26
  • 153
  • 180