ASP.net MVC, das JSONP zurückgibt

Ich möchte einige JSON-Domains über mehrere Domains hinweg zurückgeben, und ich verstehe, dass dies durch JSONP und nicht durch reines JSON geschieht. Ich benutze ASP.net MVC, also habe ich darüber nachgedacht, einfach den JSONResult-Typ zu erweitern und dann Controller zu erweitern, so dass es auch eine Jsonp-Methode implementiert. Ist das der beste Weg dies zu tun oder gibt es ein eingebautes ActionResult, das vielleicht besser ist?

Edit: Ich ging voran und tat das. Nur als Referenz habe ich ein neues Ergebnis hinzugefügt:

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 } } } 

und auch ein paar Methoden zu einer Superklasse aller meiner Controller:

 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 }; } 

Klappt wunderbar.

Hier ist eine einfache Lösung, wenn Sie keinen Aktionsfilter definieren möchten

Clientseitiger Code mit jQuery:

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

MVC-Controller-Aktion. Gibt das Inhaltsergebnis mit dem JavaScript-Code zurück, der die Callback-function ausführt, die mit der Abfragezeichenfolge bereitgestellt wird. Legt auch den JavaScript-MIME-Typ für die Antwort fest.

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

Anstatt meine Controller mit Jsonp () – Methoden zu unterklassifizieren, entschied ich mich für die Methode der Erweiterungsmethode, da sie sich für mich sauberer anfühlt. Das Schöne am JsonpResult ist, dass Sie es genau so testen können wie ein JsonResult.

Ich tat:

 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}; } } 

Auf diese Weise müssen Sie sich nicht darum kümmern, alle Jsonp () – Überladungen zu erstellen, sondern konvertieren Sie einfach Ihr JsonResult in ein Jsonp ().

Ranjus Blogbeitrag (auch bekannt als “Dieser Blogbeitrag, den ich gefunden habe”) ist exzellent. Wenn Sie ihn lesen, können Sie die folgende Lösung weiterführen, sodass Ihr Controller JSON- und domainübergreifende JSONP-Anfragen mit derselben Domain elegant in der gleichen Controlleraktion bearbeiten kann zusätzlicher Code [in der Aktion].

Egal, für die “gib mir den Code” Typen, hier ist es, falls der Blog wieder verschwindet.

In Ihrem Controller (dieser Code ist neu / nicht-Blog-Code):

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

JsonpResult auf diesem ausgezeichneten Blog- Eintrag gefunden:

 ///  /// 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 ///  public class JsonpResult : JsonResult { ///  /// Gets or sets the javascript callback function that is /// to be invoked in the resulting script output. ///  /// The callback function name. public string Callback { get; set; } ///  /// Enables processing of the result of an action method by a /// custom type that inherits from . ///  /// The context within which the /// result is executed. 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 } } } 

Hinweis: Nachdem ich die Kommentare von @Ranju und anderen an das OP gelesen hatte , dachte ich, dass es sich lohnt, den functionscode “nichtiges Minimum” aus Ranjus Blogpost als Community-Wiki zu veröffentlichen. Obwohl es sicher ist, dass Ranju den obigen und anderen Code in seinem Blog hinzugefügt hat, um ihn frei zu benutzen, werde ich seine Worte hier nicht kopieren.

Die referierten Artikel von stimms und ranju v waren beide sehr nützlich und machten die Situation klar.

Allerdings war ich am Kopf über die Verwendung von Erweiterungen, Sub-Classing im Zusammenhang mit dem MVC-Code, den ich online gefunden hatte.

Es gab zwei Schlüsselpunkte, die mich erwischt haben:

  1. Der Code, den ich von ActionResult abgeleitet hatte, aber in ExecuteResult gab es Code, um entweder XML oder JSON zurückzugeben.
  2. Ich hatte dann ein Generics-basiertes ActionResult erstellt, um sicherzustellen, dass das gleiche ExecuteResults unabhängig von dem Datentyp verwendet wurde, den ich zurückgegeben habe.

Also, die Kombination der beiden – ich brauchte keine weiteren Erweiterungen oder Sub-Classing, um den Mechanismus zur Rückgabe von JSONP hinzuzufügen, ändern Sie einfach meine vorhandenen ExecuteResults.

Was mich verwirrt hatte, war, dass ich wirklich nach einer Möglichkeit suchte, JsonResult abzuleiten oder zu erweitern, ohne das ExecuteResult neu zu codieren. Da JSONP effektiv eine JSON-Zeichenfolge mit Präfix und Suffix ist, schien es eine Verschwendung. Der Underleer ExecuteResult verwendet jedoch respone.write – die sicherste Art des Änderns ist es, ExecuteResults so umzuschreiben, wie es von verschiedenen Postings zur Verfügung gestellt wird!

Ich könnte etwas Code posten, wenn das nützlich wäre, aber es gibt bereits eine Menge Code in diesem Thread.

  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(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(value); } } } //Example of using the Jsonp function:: // 1- public JsonResult Read() { IEnumerable result = context.All(); return this.Jsonp(result); } //2- public JsonResult Update() { var models = this.DeserializeObject>("models"); if (models != null) { Update(models); //Update properties & save change in database } return this.Jsonp(models); } 

Die obige Lösung ist eine gute Art zu arbeiten, aber es sollte mit einem neuen Ergebnistyp erweitert werden, anstatt eine Methode zu haben, die ein JsonResult zurückgibt, sollten Sie Methoden schreiben, die Ihre eigenen Ergebnistypen zurückgeben

 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(""); } }