How to create a custom JSONP FilterAttribute in ASP.NET MVC
You all might have heard what is JSONP.
If not then lets know and learn what it is?
How the Filter Attribute in JSONP Work. With a Sample Code and understand what is the need of it.
How it is Implemented in MVC ?
JSONP is standard JSON with 'padding' - this padding is what enables client side AJAX calls to be sent to domains outside of the domain the call originated in without violating security policies.
This works by injecting a client script section with a src attribute of your JSONP endpoint - the return value then needs to be your JSON object wrapped in a call to a known method on the client.
Full details can be found here.
en.wikipedia.org/wiki/JSONP
If you currently have an MVC application that returns JSON you can easily adapt this to support JSONP as well.
This is what a typical controller action that returns JSON would look like:
public ActionResult SomeMethod(string value)
{
return Json(repository.GetData(value),JsonRequestBehavior.AllowGet);
}
Above we can see that we have a simple controller action that takes a string value and uses a local repository object to get some data, this data is then passed into a Json result object which takes care of serializing this object and returning it to the client.
*Note that the "Json" result object is the built in "System.Web.Mvc.JsonResult"
[msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult.aspx] in the MVC framework.
If we want the above controller action to also support JSONP we have a few different options.
The first being creating a new JsonpResult class.
This is rather simple (just inherit JsonResult and override execute) but would then require us to determine in the controller action which result to return -- do I return a JsonResult or a JsonpResult?
A cleaner option is to create a custom FilterAttribute[msdn.microsoft.com/en-us/library/system.web.mvc.filterattribute.aspx].
The filter attribute will override the "OnActionExecuted" method
which will allow us to hook into the pipeline after our action
returns some ActionResult (in the case above a JsonResult object).
It would look something like this:
public class JsonpFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext) {
if(filterContext == null)
throw new ArgumentNullException("filterContext");
string callback = filterContext.HttpContext.Request["callback"];
// check for a callback parameter, this signifies that we want to wrap our json response // 'callback' is the standard parameter jQuery sends for jsonp - adjust to what you need if (!string.IsNullOrEmpty(callback))
{ // check for a "JsonResult", could also throw an exception here if it is not a JsonResult type
JsonResult result = filterContext.Result as JsonResult;
if (result != null)
{
JavaScriptSerializer serializer = new JavaScriptSerializer(); filterContext.Result = new JavaScriptResult()
{
Script = string.Format("{0}({1})", callback, serializer.Serialize(result.Data)) };
}
}
}
}
You will notice that we are inheriting from "ActionFilterAttribute" and just overriding a single method.
In this method we look for a request parameter named "callback" (this is what jQuery uses for jsonp AJAX requests) -- if this parameter is found we then check to see if the result returned from the action was a JsonResult, if this is also true we then return a "JavaScriptResult" object (also part of the MVC framework) with it's "Script" property set to our callback function's name and passing it the serialized JSON object which we pull from the original JsonResult object.
To put this all together now we just need to decorate our controller (if we want this attribute to apply to all actions) or any individual action methods.
[MyProject.MyApp.Web.FilterAttributes.JsonpFilter]
public class MyController : Controller
{
....
}
And that's all there is to it, your controller action will now respond with the proper JSON output based on whether or not the client has specified a callback function.