Reputation: 3397
I'm trying to find a way to wrap a a json response with a callback for jsonp. The problem I am running into though is I don't want to use the 'JsonResult' class to construct the response as it wraps it with its own object where as if I return the model directly it is properly serialized to json.
So far I have tried using the a 'ActionFilter' but couldn't find out how I could wrap the result after the action executed.
Any direction at all would be appreciated
Upvotes: 0
Views: 865
Reputation: 1536
I've been down this road, and can offer the following code which encapsulates JsonP calls into an ActionResult, adds a method to your Controller which allows you to prioritize the type of ActionResult you want, and a couple of extension methods to glue it all together. The only requirement is to consistently name your callback parameter, so it can be reliably culled from the Request.
First, the derived ActionResult:
using System;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace CL.Enterprise.Components.Web.Mvc
{
/// <summary>
/// Extension of JsonResult to handle JsonP requests
/// </summary>
public class JsonPResult : ActionResult
{
private JavaScriptSerializer _jser = new JavaScriptSerializer();
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public string JsonCallback { get; set; }
public JsonPResult() { }
/// <summary>
/// Package and return the result
/// </summary>
public override void ExecuteResult(ControllerContext Context)
{
//Context.IsChildAction
if (Context == null)
{
throw new ArgumentNullException("Context");
}
JsonCallback = Context.HttpContext.Request["callback"];
if (string.IsNullOrEmpty(JsonCallback))
{
JsonCallback = Context.HttpContext.Request["jsoncallback"];
}
if (string.IsNullOrEmpty(JsonCallback))
{
throw new ArgumentNullException("JsonP callback parameter required for JsonP response.");
}
HttpResponseBase CurrentResponse = Context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
CurrentResponse.ContentType = ContentType;
}
else
{
CurrentResponse.ContentType = "application/json";
}
if (ContentEncoding != null)
{
CurrentResponse.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
CurrentResponse.Write(string.Format("{0}({1});", JsonCallback, _jser.Serialize(Data)));
}
}
}
}
Next, the Controller extension methods:
using System.IO;
using System.Web.Mvc;
namespace CL.Enterprise.Components.Web.Mvc
{
/// <summary>
/// Extension methods for System.Web.Mvc.Controller
/// </summary>
public static class ContollerExtensions
{
/// <summary>
/// Method to return a JsonPResult
/// </summary>
public static JsonPResult JsonP(this Controller controller, object data)
{
JsonPResult result = new JsonPResult();
result.Data = data;
//result.ExecuteResult(controller.ControllerContext);
return result;
}
/// <summary>
/// Get the currently named jsonp QS parameter value
/// </summary>
public static string GetJsonPCallback(this Controller controller)
{
return
controller.ControllerContext.RequestContext.HttpContext.Request.QueryString["callback"] ??
controller.ControllerContext.RequestContext.HttpContext.Request.QueryString["jsoncallback"] ??
string.Empty;
}
}
}
Finally, add this method to your Controller:
/// <summary>
/// Gets an ActionResult, either as a jsonified string, or rendered as normally
/// </summary>
/// <typeparam name="TModel">Type of the Model class</typeparam>
/// <param name="UsingJson">Do you want a JsonResult?</param>
/// <param name="UsingJsonP">Do you want a JsonPResult? (takes priority over UsingJson)</param>
/// <param name="Model">The model class instance</param>
/// <param name="RelativePathToView">Where in this webapp is the view being requested?</param>
/// <returns>An ActionResult</returns>
public ActionResult GetActionResult<T>(T Model, bool UsingJsonP, bool UsingJson, string RelativePathToView)
{
string ViewAsString =
this.RenderView<T>(
RelativePathToView,
Model
);
if (UsingJsonP) //takes priority
{
string Callback = this.GetJsonPCallback();
JsonPResult Result = this.JsonP(ViewAsString.Trim());
return Result;
}
if (UsingJson)//secondary
{
return Json(ViewAsString.Trim(), JsonRequestBehavior.AllowGet);
}
return View(Model); //tertiary
}
Upvotes: 3