2009-04-17 19 views
69

Estoy buscando devolver algunos JSON entre dominios y entiendo que la forma de hacerlo es a través de JSONP en lugar de JSON puro. Estoy utilizando ASP.net MVC, así que estaba pensando en simplemente extender el tipo JSONResult y luego extender el controlador para que también implementara un método Jsonp. ¿Es esta la mejor manera de hacerlo o hay un ActionResult integrado que podría ser mejor?ASP.net MVC que devuelve JSONP

Editar: Seguí adelante y lo hice. Sólo por el bien de referencia he añadido un nuevo 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 
      } 
     } 
    } 

y también un par de métodos para una superclase de todos mis 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 un encanto.

+0

Gracias! ¡Solo lo implementé en nuestro proyecto! :) –

+3

¡Agradable! Pero JSONP debe servir como application/javascript http://stackoverflow.com/questions/111302/best-content-type-to-serve-jsonp –

+0

ver también http://support.github.com/discussions/api/18 -content-type-should-applicationjavascript-for-jsonp-request http://stackapps.com/questions/1668/wrong-content-type-in-jsonp-calls –

Respuesta

13

En lugar de crear subclases de mis controladores con métodos Jsonp(), seguí la ruta del método de extensión, ya que me parece un poco más limpio. Lo bueno de JsonpResult es que puedes probarlo exactamente de la misma manera que lo harías con un JsonResult.

que hice:

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

De esta manera usted no tiene que preocuparse de la creación de todos los diferentes sobrecargas jsonp(), simplemente convertir su JsonResult a un jsonp uno.

+2

¿Cuál es la clase JsonpResult? – Chookoos

+0

¿Puedes dar un ejemplo de una llamada a esto? –

+2

Para los otros comentaristas, para ser claros, mendicante se está construyendo fuera del código del PO. – ruffin

-2

la solución anterior es una buena manera de trabajar pero debe Extendend con un nuevo tipo de resultado en lugar de tener un método que devuelve un JsonResult debe escribir métodos que devuelven sus propios 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("<textarea>"); 
     base.ExecuteResult(context); 
     context.HttpContext.Response.Write("</textarea>"); 
    } 
} 
0

Los artículos mencionados por stimms y ranju v fueron muy útiles e hicieron que la situación fuera clara.

Sin embargo, me quedé rascándome la cabeza sobre el uso de extensiones, subclasificación en el contexto del código MVC que había encontrado en línea.

Hubo dos puntos clave que me llamaron a cabo:

  1. El código que había derivado de ActionResult, pero en ExecuteResult hubo algo de código para devolver XML o JSON.
  2. Luego creé un ActionResult basado en Generics, para garantizar que se utilizara el mismo ExecuteResults independientemente del tipo de datos que devolviera.

Por lo tanto, la combinación de los dos - No necesitaba más extensiones o subclases para agregar el mecanismo para devolver JSONP, simplemente cambiar mis ExecuteResults existentes.

Lo que me ha confundido es que realmente estaba buscando una manera de derivar o extender JsonResult, sin volver a codificar el ExecuteResult. Como JSONP es efectivamente una cadena JSON con el prefijo & sufijo, parecía un desperdicio. Sin embargo, el subordinado ExecuteResult utiliza respone.write, por lo que la forma más segura de cambiar es volver a codificar ExecuteResults como lo proporcionan varias publicaciones.

Puedo publicar un código si eso fuera útil, pero ya hay bastante código en este hilo.

15

Aquí es una solución simple, si no desea definir un filtro de acción

código del lado del cliente usando jQuery:

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

MVC acción del controlador. Devuelve el resultado del contenido con el código JavaScript que ejecuta la función de devolución de llamada proporcionada con la cadena de consulta. También establece el tipo JavaScript MIME para la respuesta.

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

Ranju's blog post (también conocido como "Este blog me encontré") es excelente, y la lectura que le permitirá aún más la solución a continuación para que su controlador puede manejar el mismo dominio JSON y JSONP entre dominios solicitudes elegantemente la misma acción del controlador sin código adicional [en la acción].

De todos modos, para los tipos "Dame el código", aquí está, en caso de que el blog desaparezca nuevamente.

En su controlador (este fragmento es nuevo código no-blog /):

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

JsonpResult encontrar en this excellent blog post:

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

Nota: seguimiento a la comments to the OP by @Ranju and others, me pensé que valía la pena publicar el código funcional "mínimo" de la publicación de blog de Ranju como wiki de la comunidad. Aunque es seguro decir que Ranju agregó el código anterior y otro en su blog para usarlo libremente, no voy a copiar sus palabras aquí.

+1

Gracias @ruffin! Quería hacer esto uno de estos días. Gracias por hacerlo! :) – Raj

0
 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); 
      } 
+2

¿me puede dar más detalles, no solo una respuesta de solo código? – Thomas

Cuestiones relacionadas