ASP.net MVC返回JSONP

我正在寻找跨域返回一些JSON,我知道这样做是通过JSONP,而不是纯粹的JSON。 我正在使用ASP.net MVC所以我只想扩展JSONResulttypes,然后extendig控制器,以便它也实现了Jsonp方法。 这是最好的方式去做,或者有一个内置的ActionResult,可能会更好?

编辑:我继续做了。 仅供参考,我添加了一个新的结果:

public class JsonpResult : System.Web.Mvc.JsonResult { 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/javascript"; } if (ContentEncoding != null) { response.ContentEncoding = ContentEncoding; } if (Data != null) { // The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1 #pragma warning disable 0618 HttpRequestBase request = context.HttpContext.Request; JavaScriptSerializer serializer = new JavaScriptSerializer(); response.Write(request.Params["jsoncallback"] + "(" + serializer.Serialize(Data) + ")"); #pragma warning restore 0618 } } } 

还有几个方法来我的所有控制器的超类:

 protected internal JsonpResult Jsonp(object data) { return Jsonp(data, null /* contentType */); } protected internal JsonpResult Jsonp(object data, string contentType) { return Jsonp(data, contentType, null); } protected internal virtual JsonpResult Jsonp(object data, string contentType, Encoding contentEncoding) { return new JsonpResult { Data = data, ContentType = contentType, ContentEncoding = contentEncoding }; } 

奇迹般有效。

这是一个简单的解决scheme,如果你不想定义一个动作filter

客户端代码使用jQuery:

  $.ajax("http://www.myserver.com/Home/JsonpCall", { dataType: "jsonp" }).done(function (result) {}); 

MVC控制器的行动。 用执行查询string提供的callback函数的JavaScript代码返回内容结果。 还为响应设置JavaScript MIMEtypes。

  public ContentResult JsonpCall(string callback) { return Content(String.Format("{0}({1});", callback, new JavaScriptSerializer().Serialize(new { a = 1 })), "application/javascript"); } 

我没有用Jsonp()方法inheritance我的控制器,而是使用了扩展方法route,因为它感觉触摸更清洁。 JsonpResult的好处是可以像testingJsonResult一样testing它。

我做了:

 public static class JsonResultExtensions { public static JsonpResult ToJsonp(this JsonResult json) { return new JsonpResult { ContentEncoding = json.ContentEncoding, ContentType = json.ContentType, Data = json.Data, JsonRequestBehavior = json.JsonRequestBehavior}; } } 

这样你就不必担心创build所有不同的Jsonp()重载,只要把你的JsonResult转换成Jsonp就可以了。

Ranju的博客文章 (又名“我发现这个博客文章”)非常好,阅读它可以让你进一步解决下面的解决scheme,使您的控制器可以在相同的控制器动作中优雅地处理同域JSON和跨域JSONP请求额外的代码[在行动]。

无论如何,对于“给我的代码”types,这是博客消失的情况。

在你的控制器(这个片段是新/非博客代码):

 [AllowCrossSiteJson] public ActionResult JsonpTime(string callback) { string msg = DateTime.UtcNow.ToString("o"); return new JsonpResult { Data = (new { time = msg }) }; } 

JsonpResult发现在这个优秀的博客文章 :

 /// <summary> /// Renders result as JSON and also wraps the JSON in a call /// to the callback function specified in "JsonpResult.Callback". /// http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx /// </summary> public class JsonpResult : JsonResult { /// <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> /// 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/javascript"; if (ContentEncoding != null) response.ContentEncoding = ContentEncoding; if (Callback == null || Callback.Length == 0) Callback = context.HttpContext.Request.QueryString["callback"]; if (Data != null) { // The JavaScriptSerializer type was marked as obsolete // prior to .NET Framework 3.5 SP1 #pragma warning disable 0618 JavaScriptSerializer serializer = new JavaScriptSerializer(); string ser = serializer.Serialize(Data); response.Write(Callback + "(" + ser + ");"); #pragma warning restore 0618 } } } 

注意: 通过@Ranju等人对OP的评论 ,我认为值得把Ranju的博客文章中的“最低限度”function代码作为社区维基发布。 虽然可以肯定地说Ranju在自己的博客上添加了上面的代码和其他代码以供自由使用,但我不打算在这里复制他的话。

参考文献的刺激和ranju五都是非常有用的,并使情况明确。

然而,我在使用扩展的时候,还在摸索着我的头脑,在我在网上find的MVC代码的背景下进行了分类。

有两个关键点把我抓出来了:

  1. 我从ActionResult派生的代码,但在ExecuteResult中有一些代码返回XML或JSON。
  2. 然后我创build了一个基于generics的ActionResult,以确保ExecuteResults的使用与我返回的数据types无关。

所以,结合两者 – 我不需要进一步的扩展或子分类来添加返回JSONP的机制,只需更改我现有的ExecuteResults。

令我困惑的是,我正在寻找一种方法来派生或扩展JsonResult,而不用重新编写ExecuteResult。 由于JSONP实际上是一个带前缀和后缀的JSONstring,这似乎是一种浪费。 但是,下层的ExecuteResult使用了respone.write – 所以最安全的方法就是将各个post所提供的ExecuteResults重新编码!

我可以发布一些代码,如果这将是有用的,但有相当多的代码已经在这个线程。

  using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Script.Serialization; namespace Template.Web.Helpers { public class JsonpResult : JsonResult { public JsonpResult(string callbackName) { CallbackName = callbackName; } public JsonpResult() : this("jsoncallback") { } public string CallbackName { get; set; } public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } var request = context.HttpContext.Request; var response = context.HttpContext.Response; string jsoncallback = ((context.RouteData.Values[CallbackName] as string) ?? request[CallbackName]) ?? CallbackName; if (!string.IsNullOrEmpty(jsoncallback)) { if (string.IsNullOrEmpty(base.ContentType)) { base.ContentType = "application/x-javascript"; } response.Write(string.Format("{0}(", jsoncallback)); } base.ExecuteResult(context); if (!string.IsNullOrEmpty(jsoncallback)) { response.Write(")"); } } } public static class ControllerExtensions { public static JsonpResult Jsonp(this Controller controller, object data, string callbackName = "callback") { return new JsonpResult(callbackName) { Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } public static T DeserializeObject<T>(this Controller controller, string key) where T : class { var value = controller.HttpContext.Request.QueryString.Get(key); if (string.IsNullOrEmpty(value)) { return null; } JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); return javaScriptSerializer.Deserialize<T>(value); } } } //Example of using the Jsonp function:: // 1- public JsonResult Read() { IEnumerable<User> result = context.All(); return this.Jsonp(result); } //2- public JsonResult Update() { var models = this.DeserializeObject<IEnumerable<User>>("models"); if (models != null) { Update(models); //Update properties & save change in database } return this.Jsonp(models); } 

上面的解决scheme是一个很好的工作方式,但它应该是一个新types的结果extendend而不是有一个方法返回一个JsonResult你应该写方法返回你自己的结果types

 public JsonPResult testMethod() { // use the other guys code to write a method that returns something } public class JsonPResult : JsonResult { public FileUploadJsonResult(JsonResult data) { this.Data = data; } public override void ExecuteResult(ControllerContext context) { this.ContentType = "text/html"; context.HttpContext.Response.Write("<textarea>"); base.ExecuteResult(context); context.HttpContext.Response.Write("</textarea>"); } }