nik codes

Archive for the day “February 29, 2012”

An ASP.NET MVC JSONP ActionResult

JSON with Padding (JSONP) is a slight “hack” to allow for cross origin ajax requests. Instead of making requests the standard ajax way, leveraging XMLHttpRequest, JSONP techniques make requests by writing a <script> tag into the DOM with the src attribute set to the appropriate URL. Because browsers do not limit the origin of scripts loaded via the <script> tag, data, via JavaScript, can be loaded from any of the hundreds of API’s and endpoints that support JSONP.

Consuming JSONP services is ridiculously easy and has had support built into jQuery since version 1.2.

//gist: https://gist.github.com/1944712
$(function () {
    $.ajax({
        dataType: 'jsonp',
        url: 'http://example.com/JSONP/API/',
        success: function (data) {
            console.log(data);
        }
    });
});

In this case, the data that is returned from the server is logged to the browser console, but any JavaScript could be executed.

Enabling JSONP on top of an existing JSON endpoint (server side) is fairly simple. It requires changing the HTTP response content type, and wrapping the resultant JSON string in a JavaScript function call (the padding part).

Unfortunately, ASP.NET MVC does not ship with JSONP support out of the box, but it is easy to add with the great extensibility points available in MVC. Here is an implementation of an MVC ActionResult which would enable any controller to begin to return proper JSONP results.

//gist: https://gist.github.com/1944346
using System;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;

namespace NikCodes.ActionResults
{
    public class JsonpResult : ActionResult
    {
        public string CallbackFunction { get; set; }
        public Encoding ContentEncoding { get; set; }
        public string ContentType { get; set; }
        public object Data { get; set; }

        public JsonpResult(object data):this(data, null){}
        public JsonpResult(object data, string callbackFunction)
        {
            Data = data;
            CallbackFunction = callbackFunction;
        }


        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null) throw new ArgumentNullException("context");

            HttpResponseBase response = context.HttpContext.Response;

            response.ContentType = string.IsNullOrEmpty(ContentType) ? "application/x-javascript" : ContentType;

            if (ContentEncoding != null) response.ContentEncoding = ContentEncoding;

            if (Data != null)
            {
                HttpRequestBase request = context.HttpContext.Request;

                var callback = CallbackFunction ?? request.Params["callback"] ?? "callback";

#pragma warning disable 0618 // JavaScriptSerializer is no longer obsolete
                var serializer = new JavaScriptSerializer();
                response.Write(string.Format("{0}({1});", callback, serializer.Serialize(Data)));
#pragma warning restore 0618
            }
        }
    }
}

This action result simply takes in an object, serializes it using the standard MVC serialization method and wraps it up in a callback function. It supports jQuery’s JSONP implementation transparently. Using it couldn’t be simpler:

//gist: https://gist.github.com/1944913
public ActionResult ActionMethod()
{
    var data = GetDataSomehow();
    return new JsonpResult(data);
    //constructor overload for overriding callback function
    //return new JsonpResult(data, "callback function"); 
}

Of course, cross origin resource sharing (CORS) is much more powerful that JSONP, and will ultimately replace it – but until then, or as long as we have < IE 10, we will still have to leverage this technique.

Download the code, try it out. Let me know if this is valuable enough to put on NuGet.

- By

Post Navigation

Follow

Get every new post delivered to your Inbox.