2009-04-18

ASP.net MVC returning JSONP

I’ve been working on a piece of code which returns JSON in response to some request. It has all being going fine on the local host but I’ve just deployed it up to my new test server and started to access it using JQuery and it stooped working. This is, of course, typical. This time however, my problem was ignorance rather than a programming blunder. As it turns out when returning JSON across domains you need to actually return JSONP in order to get call backs working. What is JSONP? I’m glad you asked, because I had no idea either. Basically it is the same JSON you love but wrapped with a bit of text which specifies the name of the function to call upon returning.

JSON:

{“userID”:”00000000-0000-0000-0000-000000000000”³,”success”:”false”}

JSONP:

somefunction({“userID”:”00000000-0000-0000-0000-000000000000”³,”success”:”false”})

Easy enough. In JQuery you need to just add another parameter to the JSON call in order to pass the name of the function to the server

$.getJSON(URL +“/json/Message/sendMessage?userName=”+ $(“#userName3”).val()+
“&messageText=”+ $(“#message”).val()+
“&userKey=”+ key +
“&jsoncallback=?”,
function(json)
{
}
);

JQuery will automatically replace the ? with the name of your callback function, in this case an anonymous function.

Having discovered all of this I realized that I now had a ton of code returning JsonResults which needed to be changed. I figured the best way to do this was to actually create a JsonpResult which was based off of the JsonResult. So I did just that, basing it off of the now open sourced ASP.net MVC JsonResult

publicclass JsonpResult : System.Web.Mvc.JsonResult
{
publicoverridevoid ExecuteResult(ControllerContext context)
{
if(context ==null)
{
thrownew ArgumentNullException(context);
}

HttpResponseBase response = context.HttpContext.Response;

if(!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType =application/json;
}
if(ContentEncoding !=null)
{
response.ContentEncoding = ContentEncoding;
}
if(Data !=null)
{
// The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1

#pragma warning disable 0618
HttpRequestBase request = context.HttpContext.Request;

JavaScriptSerializer serializer =new JavaScriptSerializer();
if(null!= request.Params[jsoncallback])
response.Write(request.Params[jsoncallback]+(+ serializer.Serialize(Data)+));
else
response.Write(serializer.Serialize(Data));

#pragma warning restore 0618
}
}
}

I then extended Controller

publicclass WopsleController : Controller
{
protectedinternal JsonpResult Jsonp(object data)
{
return Jsonp(data,null/ contentType /);
}

protectedinternal JsonpResult Jsonp(object data,string contentType)
{
return Jsonp(data, contentType,null);
}

protectedinternalvirtual JsonpResult Jsonp(object data,string contentType, Encoding contentEncoding)
{
returnnew JsonpResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding
};
}

}

and altered all my controllers to extend WopsleController rather than Controller. Seems to work pretty well.


comment: