ASP.net MVC retornando JSONP

Eu estou olhando para retornar alguns JSON entre domínios e eu entendo que a maneira de fazer isso é através de JSONP em vez de puro JSON. Eu estou usando o asp.net MVC, então eu estava pensando em apenas estender o tipo JSONResult e, em seguida, estender o controlador para que ele também implementou um método Jsonp. Esta é a melhor maneira de fazê-lo ou há um construído em ActionResult que pode ser melhor?

Edit: Eu fui em frente e fiz isso. Apenas por referência, eu adicionei um novo resultado:

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

e também alguns methods para uma superclass de todos os meus controladores:

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

Funciona como um encanto.

Aqui está uma solução simples, se você não quiser definir um filtro de ação

Código do lado do cliente usando jQuery:

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

Ação do controlador MVC. Retorna o resultado do conteúdo com o código JavaScript executando a function de retorno de chamada fornecida com a string de consulta. Também define o tipo MIME do JavaScript para resposta.

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

Em vez de subclassificar meus controladores com os methods Jsonp (), eu segui a rota do método de extensão, já que me parece um pouco mais limpo. O bom sobre o JsonpResult é que você pode testá-lo exatamente da mesma maneira que faria com um JsonResult.

Eu fiz:

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

Dessa forma, você não precisa se preocupar com a criação de todas as sobrecargas diferentes do Jsonp (), basta converter seu JsonResult para um Jsonp.

A postagem do blog de Ranju (também conhecida como “Este post do blog que encontrei”) é excelente, e a leitura permitirá que você continue a solução abaixo para que seu controlador possa manipular elegantemente as solicitações JSONP de domínio compartilhado e JSONP entre domínios na mesma ação do controlador sem código adicional [na ação].

Independentemente disso, para os tipos “dê-me o código”, aqui está, caso o blog desapareça novamente.

No seu controlador (esse snippet é novo / código sem blog):

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

JsonpResult encontrado neste excelente post :

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

Nota: Continuando com os comentários para o OP por @Ranju e outros , achei que valia a pena postar o código funcional “mínimo” da postagem do blog de Ranju como um wiki da comunidade. Embora seja seguro dizer que Ranju adicionou o código acima e outro em seu blog para ser usado livremente, não vou copiar suas palavras aqui.

Os artigos referenciados por stimms e ranju v foram muito úteis e tornaram a situação clara.

No entanto, fiquei coçando a cabeça sobre o uso de extensões, subsorting no contexto do código MVC que encontrei online.

Houve dois pontos-chave que me pegaram:

  1. O código que eu tinha derivado de ActionResult, mas no ExecuteResult havia algum código para retornar XML ou JSON.
  2. Eu tinha então criado um ActionResult baseado em Generics, para garantir que o mesmo ExecuteResults fosse usado independentemente do tipo de dado que eu retornei.

Então, combinando os dois – eu não precisei de extensões adicionais ou subclasss para adicionar o mecanismo para retornar JSONP, simplesmente mude meus ExecuteResults existentes.

O que me confundiu é que realmente eu estava procurando uma maneira de derivar ou estender o JsonResult, sem recodificar o ExecuteResult. Como JSONP é efetivamente uma string JSON com prefixo e sufixo, isso parecia um desperdício. No entanto, o sublinhado ExecuteResult usa respone.write – assim, a maneira mais segura de alterar é recodificar ExecuteResults como fornecido por várias postagens!

Eu posso postar algum código se isso for útil, mas já existe bastante código neste 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); } 

a solução acima é uma boa maneira de trabalhar, mas deve ser estendida com um novo tipo de resultado, em vez de ter um método que retorna um JsonResult. Você deve escrever methods que retornam seus próprios tipos de resultados.

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