Cross-domain JsonP using Asp.net MVC and jQuery

So there are a couple great walkthroughs out there that talk about using jquery jsonp in cross-domain scenarios.  They talk a lot about the requesting and server side, but not so much about how to use the callback. So to clarify, I’ll step through what I did to make this work including providing my implementation for a new ActionResult called JsonPResult for MVC.

Starting out, client-side on the calling page, I setup my script. This function makes a request to the server and expects a jsonp response back. The default “success” handler is optional here and will be invoked along-side the one you specify for jsonp.

$.ajax({  
    url: 'http://other-domain/api/GetInformation',
    data: { key: 'some data key, this parameter is optional' },
    type: "GET",
    dataType: "jsonp",
    jsonpCallback: "localJsonpCallback"
});

function localJsonpCallback(json) {  
    //do stuff...
}

We need to setup the server side.  I’m using Asp.Net MVC, so I built this new ActionResult for handling Jsonp GET requests. I copied most of the code from the original JsonResult class in System.Web.MVC Ms-Pl code. Before I show you that, you should see it in action. Here I have a controller action that uses JsonpResult:

public JsonpResult GetInformation(string key)  
{
    var resp = new Core.Model.CustomObject();
    if (validateKey(key))
    {
        resp.Data = "some custom message";
        resp.Success = true;
    }
    else
        resp.Message = "unauthorized";

    return this.Jsonp(resp); //using extension method
}

This automatically handles all the fancy Jsonp work that goes on behind the scenes.  It serializes the custom object into Json and wraps it with the callback function. So your response appears something like this:

localJsonpCallback({"Data":{"Success":true, Message: "some custom message"}});

I setup an extension method to use controller.Jsonp(object) just as you do the existing controller.Json(object) method already available. This should make it a lot easier to handle. It will inspect the call for the json callback itself so you also don’t have to manually pull it from the request.  It will also work with the two different types of callback requests parameters in use out there like: “jsoncallback” and “callback.”

/* ****************************************************************************  
 *
 * Copyright (c) Microsoft Corporation. All rights reserved.
 *
 * Content of this class was mostly derived from the original
 * JsonResult class in the System.Web.Mvc 2.0 RTM Assembly. This
 * has beeen slightly extended for use with JSONP calls.
 *
 * This software is subject to the Microsoft Public License (Ms-PL).
 * A copy of the license can be found in the license.htm file included
 * in this distribution.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * ***************************************************************************/

namespace System.Web.Mvc  
{
    using System;
    using System.Text;
    using System.Web;
    using System.Web.Mvc.Resources;
    using System.Web.Script.Serialization;

    public class JsonpResult : ActionResult
    {

        public JsonpResult()
        {
        }

        public Encoding ContentEncoding
        {
            get;
            set;
        }

        public string ContentType
        {
            get;
            set;
        }

        public object Data
        {
            get;
            set;
        }

        public string JsonCallback { get; set; }

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

            this.JsonCallback = context.HttpContext.Request["jsoncallback"];

            if (string.IsNullOrEmpty(this.JsonCallback))
                this.JsonCallback = context.HttpContext.Request["callback"];

            if (string.IsNullOrEmpty(this.JsonCallback))
                throw new ArgumentNullException("JsonCallback required for JSONP response.");

            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)
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                response.Write(string.Format("{0}({1});", this.JsonCallback, serializer.Serialize(Data)));
            }
        }
    }

    //extension methods for the controller to allow jsonp.
    public static class ContollerExtensions
    {
        public static JsonpResult Jsonp(this Controller controller, object data)
        {
            JsonpResult result = new JsonpResult();
            result.Data = data;
            return result;
        }
    }
}

When the response is downloaded from the client, it’s invoked and you handle it just as you normally would with any other getJSON success handler. So hopefully this helps clear up any confusion with using JsonP.  There’s about a thousand suggestions I found on doing cross domain JSON and this was just one of the many. I also wrote a simple JsonWebClient class in .Net that will permit the server-side proxy POST to another domain if anyone is interested. I did that before I learned about CORS (preflight post) or this, JsonP. Comments are welcome!

Sample Code

Additionally, here is a simple, working demo showing how this works. Be sure to start both websites. Then browse the ClientWebsite and click Test JSONP. This will perform a JSONP GET to the second website.

Download the sample

This project is also in the IWS Snippets project.

comments powered by Disqus