Using JSONP with ASP.NET MVC

on Thursday, December 17, 2009

(I am using the Beta 2 of ASP.NET MVC. It isn’t supposed to be ready for production; but it seems pretty stable.)

Earlier today I needed create some Cross Domain javascript requests. At first I tried to use the ASP.NET MVC JsonResult to provide data for these requests, but the serialized objects that were returned were unable to be used through JSONP (the cross domain javascript).

After a little research on StackOverflow (what a great site), I was able to find some JSONP code to use. This was a real timesaver, and it was documented to boot.

The creators of this code posted their code at:

http://stackoverflow.com/questions/758879/asp-net-mvc-returning-jsonp

http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx

I’ve added a small update. Just enough to make the Controller handle the new ASP.NET MVC 2 JsonRequestBehavior. I hope someone will improve on this code and add an “extensions” library to allow for Jsonp to be available in all controllers without using the JsonpController or JsonpFilterAttribute.

JsonpResult.cs:

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;

namespace Ucsb.Sa.Enterprise.Utility.Web.Mvc
{
/// <summary>
/// A Jsonp wrapper for the JsonResult.
///
/// Created by:
/// stimms (http://stackoverflow.com/questions/758879/asp-net-mvc-returning-jsonp)
/// Ranju V (http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx)
/// </summary>
public class JsonpResult : JsonResult
{

#region constructor

/// <summary>
/// Initializes a new instance of <see cref="JsonpResult" />.
/// </summary>
public JsonpResult()
{
// http://compactprivacypolicy.org/compact_token_reference.htm
CompactPolicy = "NON DSP COR STP COM";
}

#endregion

#region properties

/// <summary>
/// Gets or sets the javascript callback function that is
/// to be invoked in the resulting script output.
/// </summary>
/// <value>The callback function name.</value>
public string Callback { get; set; }

/// <summary>
/// Gets or sets the compact policy values. By default the compact policy
/// says "We provide noinformation to 3rd party sources".
/// </summary>
public string CompactPolicy { get; set; }

#endregion

#region methods

/// <summary>
/// Enables processing of the result of an action method by a
/// custom type that inherits from <see cref="T:System.Web.Mvc.ActionResult"/>.
/// </summary>
/// <param name="context">The context within which the
/// result is executed.</param>
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new 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 ( string.IsNullOrEmpty( Callback ) )
{
Callback = context.HttpContext.Request.QueryString["callback"];

if ( string.IsNullOrEmpty( Callback ) )
{
Callback = context.HttpContext.Request.QueryString["jsoncallback"];
}
}

if( string.IsNullOrEmpty( CompactPolicy ) == false )
{
// TODO: uncomment when we get an IIS 7 server
//response.Headers.Add( "compact-policy", CompactPolicy );
}

if (Data != null)
{
// The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1
#pragma warning disable 0618
var serializer = new JavaScriptSerializer();
var ser = serializer.Serialize(Data);
response.Write(Callback + "(" + ser + ")");
#pragma warning restore 0618
}
}

#endregion

}

}


JsonFilterAttribute.cs:



using System;
using System.Web.Mvc;

namespace Ucsb.Sa.Enterprise.Utility.Web.Mvc
{
/// <summary>
/// A Jsonp attribute to override older JsonResult objects.
///
/// Created by:
/// stimms (http://stackoverflow.com/questions/758879/asp-net-mvc-returning-jsonp)
/// Ranju V (http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx)
/// </summary>
public class JsonpFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if(filterContext == null)
throw new ArgumentNullException("filterContext");

//
// see if this request included a "callback" querystring parameter
//
string callback = filterContext.HttpContext.Request.QueryString["callback"];
if (callback != null && callback.Length > 0)
{
//
// ensure that the result is a "JsonResult"
//
JsonResult result = filterContext.Result as JsonResult;
if (result == null)
{
throw new InvalidOperationException("JsonpFilterAttribute must be applied only " +
"on controllers and actions that return a JsonResult object.");
}

filterContext.Result = new JsonpResult
{
ContentEncoding = result.ContentEncoding,
ContentType = result.ContentType,
Data = result.Data,
Callback = callback
};
}
}
}
}


JsonController.cs:



using System.Text;
using System.Web.Mvc;

namespace Ucsb.Sa.Enterprise.Utility.Web.Mvc
{
/// <summary>
/// Controller which adds Jsonp ActionResults to the base Controller.
///
/// Created by:
/// stimms (http://stackoverflow.com/questions/758879/asp-net-mvc-returning-jsonp)
/// Ranju V (http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx)
/// </summary>
public abstract class JsonpController : Controller
{

/// <summary>
/// Creates and returns a Jsonp result.
/// </summary>
/// <param name="data">The data/object to serialize.</param>
/// <returns>A Jsonp ActionResult.</returns>
protected internal JsonpResult Jsonp(object data)
{
return Jsonp(data, null /* contentType */);
}

/// <summary>
/// Creates and returns a Jsonp result.
/// </summary>
/// <param name="data">The data/object to serialize.</param>
/// <param name="behavior">Allow Get requests.</param>
/// <returns>A Jsonp ActionResult.</returns>
protected internal JsonpResult Jsonp(object data, JsonRequestBehavior behavior )
{
return Jsonp(data, null /* contentType */, behavior );
}

/// <summary>
/// Creates and returns a Jsonp result.
/// </summary>
/// <param name="data">The data/object to serialize.</param>
/// <param name="contentType">The contentType to return.</param>
/// <returns>A Jsonp ActionResult.</returns>
protected internal JsonpResult Jsonp(object data, string contentType)
{
return Jsonp(data, contentType, null);
}

/// <summary>
/// Creates and returns a Jsonp result.
/// </summary>
/// <param name="data">The data/object to serialize.</param>
/// <param name="contentType">The contentType to return.</param>
/// <param name="behavior">Allow Get requests.</param>
/// <returns>A Jsonp ActionResult.</returns>
protected internal JsonpResult Jsonp(object data, string contentType, JsonRequestBehavior behavior)
{
return Jsonp(data, contentType, null, behavior);
}

/// <summary>
/// Creates and returns a Jsonp result.
/// </summary>
/// <param name="data">The data/object to serialize.</param>
/// <param name="contentType">The contentType to return.</param>
/// <param name="contentEncoding">The contentEncoding type.</param>
/// <returns>A Jsonp ActionResult.</returns>
protected internal virtual JsonpResult Jsonp(object data, string contentType, Encoding contentEncoding)
{
return Jsonp( data, contentType, contentEncoding, JsonRequestBehavior.DenyGet );
}

/// <summary>
/// Creates and returns a Jsonp result.
/// </summary>
/// <param name="data">The data/object to serialize.</param>
/// <param name="contentType">The contentType to return.</param>
/// <param name="contentEncoding">The contentEncoding type.</param>
/// <param name="behavior">Allow Get requests.</param>
/// <returns>A Jsonp ActionResult.</returns>
protected internal virtual JsonpResult Jsonp(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new JsonpResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
}

}
}


I hope this helps others get around the JSONP problem until ASP.NET MVC implements a standard method for this.

0 comments:

Post a Comment


Creative Commons License
This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.