Cross-domain JsonP using Asp.net MVC and jQuery
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.
This project is also in the IWS Snippets project.

18 Comments
@Patrick Wiseman
Oh hey! Thanks! I need to update that sample. Thanks for the reminder!
The GitHub version should always be current.
Still had some duplicate code in the example zip today. To keep it more generic, I removed the static class in JsonpResult. The overridden ExecuteResult(ControllerContext context) will still get executed auto-magically when you return a JsonpResult type. In the controller rather than passing the model type to the static function add a new JsonpResult constructor and just return new JsonpResult(resp). The subsequent call to ExecuteResult(…) will fill in the rest. Really good though, thanks for the post Nathan!
@ewart Fixed! I was executing result in an unnecessary place. Thanks for pointing it out. The code on github is updated as well as the sample above.
@ewart No, I didn’t notice that. Thanks for pointing that out. I’ll peek at it today and see if I can resolve it. Thanks for pointing it out!
Hi Nathan, your sample code works functionally but did you notice if you look closely at the response it actually doubles the json data it returns, returning everything twice – here is the actual network output from the sample.
localJsonpCallback({“Success”:true,”Data”:”you provided key: gfdgfdgfd”,”Message”:null});localJsonpCallback({“Success”:true,”Data”:”you provided key: gfdgfdgfd”,”Message”:null});
regards
@Sam Waite Thanks!
When you really get into Mvc after transitioning from web forms it’s a breath of fresh air. We produce stuff a lot faster now. It also puts you closer to the metal in regards to ajax, html, etc, which I really like.
This is fantastic stuff, very clever thinking on the Jsonp passing to MVC. I have managed to get a similar thing working in normal webservices, but will now consider moving to MVC as we are still in early stages of our project.
Thx for the solution Nathan.
Is there a way to POST a data the way as GET ?
@Suraj Shrestha
Thanks!
@darylhemeon: mine was also being called twice. so i removed call to ExecuteResult from ContollerExtensions and it worked.
@@darylhemeon
Make sure your
$.ajax({call is somewhere it cannot be invoked more than once. Maybe wrap it with a$(document).ready(function() {...});or put it in a button click event for testing. It should only make one request. The problem probably lies somewhere client-side. Wish I could help further.I have started using your code, very helpful thank you! My action is getting invoked twice and all i have is a simple call on the page and nothing else…any thoughts?
@Jimmy
I haven’t worked with that specifically. In WCF, as part of your operation attributes, you can instruct it to return a “bare” response that’s unwrapped. I’ll have to look up the API on a normal web service to see if it’s similar.
Nathan,
when I use JSONP to call my service, the jquery script gets added to the head tag, and the result from the web service gets dumped there as well. It is a correct result but wrapped in XML – as .NET web services always do. I am not using MVC, just plain old asmx, but I call them with the same jquery code as you do. Could you recommend a solution for getting “unwrapped” results?
Thank you.
@SC
I just posted a real simple demo showing this JSONP code in action. See the bottom of the article. Let me know if you have any questions.
@SC
Shouldn’t be a problem. I can put something together when I get a chance.
would love to see a full working example
Recent Posts
Tags