2009-05-27 17 views
22

Tengo problemas para decidir si una acción del controlador, que es llamado por AJAX, debe devolver una vista parcial, o el JSON "en bruto".ASP.NET MVC: ¿Deberían los Controladores llamados por AJAX devolver JSON o rendir html?

Devolviendo una vista parcial, con HTML renderizado hace que sea más fácil para el Javascript simplemente actualizar el DOM actual con el HTML devuelto. Sin embargo, limita lo que puede hacer el cliente de JavaScript que consume el servicio web con el HTML devuelto.

Por otro lado, tener la acción del controlador devolver JSON requeriría el javascript que realiza la llamada para crear "manualmente" el marcado basado en el JSON que se devuelve.

Como siempre, cada enfoque tiene sus ventajas y debilidades. ¿Hay otros pros/contras para cada enfoque?

+0

Sobre la discusión en los comentarios a las respuestas: - En total, hay muchas maneras disponibles para usted. Usa lo que quieras MVC rocas !!!! – IsmailS

Respuesta

16

Si está utilizando el paradigma MVC, el controlador debe devolver datos JSON() y dejar que la vista a solucionar el problema por sí mismo, al igual que su trabajo es encontrar/adaptar los datos en el modelo y pasarlos a la vista en el lado del servidor.

la obtención de puntos para browny

  • preservar la separación de intereses entre la lógica y la interfaz de usuario

  • que hacen sus acciones ajax comprobables (buena suerte probar el HTML devuelto de esa acción ...)

Quizás sea un poco más complicado, pero cabe.

Puede utilizar sistemas de plantillas de clientes como lo que ahora está disponible en MS Ajax Toolkit para ayudar a tomar parte de la carga y preservar la separación de lógica/representación en el lado del cliente.

Así que yo diría JSON, definitivamente. Pero bueno, YMMV como de costumbre ...

+0

Lo que dijo. Incluso si no le importa el patrón MVC, JSON será más fácil de manipular y verificar en el lado del cliente. – Gromer

+0

Casi todas las acciones de mi controlador devuelven json/xml. Sin embargo, hay una acción por página que devuelve una página html. Puede ir a un cliente completamente estático y hacer que todas sus acciones regresen json, pero prefiero enviar datos iniciales incrustados en el html como json para guardar un viaje de ida y vuelta, ya que el cliente tiene que establecer una conexión con el servidor para ver si el archivo ha sido modificado de todos modos. –

0

Si usa JQuery, debe usar JSON o XML porque es más fácil de modificar, pero si su llamada ajax solo devuelve elementos para un listbox, por ejemplo, también podría usar html.

Mi preferencia personal es JSON o XML ya que el uso de jQuery muy a menudo

2

Para mantener la separación de las preocupaciones, debe devolver JSON.

Cuando devuelve html, limita lo que puede hacer con los datos devueltos. Si necesita una lista de datos y desea presentarla de diferentes maneras, use JSON, de lo contrario, deberá tener diferentes métodos en el servidor para obtener las diferentes representaciones de los mismos datos.

18

En mi opinión, volviendo JSON y luego dejar que la vista del cliente a solucionar el problema puede ser un poco incómodo debido a las siguientes limitaciones:

  1. Sin lenguaje de plantillas estándar para JavaScript. En el peor de los casos, tendrá la tentación de concatenar cadenas para formar el HTML que necesita.
  2. No es una forma fácil de probar el código HTML generado por el código de concatenación.
  3. La falta de IntelliSense para su JavaScript significa que también es propenso a cometer más errores.

La forma en que he manejado esto es devolver el HTML renderizado, PERO devuelvo este HTML renderizado usando una vista parcial. Esto te da lo mejor de ambos mundos. Tienes plantillas del lado del servidor y soporte IntelliSense.

He aquí un ejemplo:

Aquí es mi llamada Ajax, como se puede ver todo lo que hace es reemplazar el código HTML para mi lista desordenada:

FilterRequests: function() { 
    $.post("/Request.aspx/GetFilteredRequests", { }, function(data) { 
     $('ul.requests').html(data); 
    }); 
}, 

Aquí está mi acción de mi controlador:

public ActionResult GetFilteredRequests(string filterJson) 
{ 
    var requests = _requestDao.LoadAll(); 

    return PartialView("FilteredRequests", requests); 
} 

Finalmente, esta es mi vista parcial (no hay necesidad de entender esto, solo te estoy mostrando cuán compleja puede ser la renderización en una aplicación del mundo real. Me daría miedo hacerlo en JavaScript. También se dará cuenta de que mi vista parcial a su vez llama a otros puntos de vista parciales también):.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Request>>" %> 
<%@ Import Namespace="Diangy.HelpDesk.Models.Lookups"%> 
<%@ Import Namespace="Diangy.HelpDesk.Models.Requests"%> 
<%@ Import Namespace="System.Web.Mvc.Html"%> 

<% foreach (var request in ViewData.Model) %> 
<%{%> 
    <li class="request"> 
     <h2>#<%= request.Id %>: <%= request.InitialInteraction().Description %></h2> 
     <p>from <%= request.Customer.FullName %> (<%= request.Customer.EmailAddress %>), <%= request.InitialInteraction().UsableTimeStamp %></p> 

     <h3>Custom Fields & Lookups</h3> 
     <div class="tabs"> 
      <ul> 
      <li><a href="#customfields<%= request.Id %>">Custom Fields</a></li> 
      <% foreach (var lookupDefinition in (List<LookupDefinition>)ViewData["LookupDefinitions"]) %> 
      <%{%> 
      <li><a href="#<%= lookupDefinition.Name.ToLowerInvariant().Replace(" ", "") + request.Id %>"><%= lookupDefinition.Name %></a></li> 
      <%}%> 
     </ul> 
     <% Html.RenderPartial("CustomFields", request); %> 
    </div> 

    <% Html.RenderPartial("Discussion", request); %> 
    <% Html.RenderPartial("Attachment", request); %> 
    </li> 
<%}%> 
+0

totalmente no de acuerdo. Con json tiene poco trabajo por hacer, pero el tráfico de red es mucho menor, y la lógica de representación/presentación permanece en el cliente "inteligente" (jquery, extjs, ...). El nuevo MS AJAX tendrá un motor de plantillas –

+7

@hrvoje. De acuerdo con esta lógica, deberíamos hacer que todas las acciones del controlador devuelvan solo los datos de la vista. Los motores de vista son inútiles en ese mundo. Creo que Praveen dice que devolver html generado por el motor de vistas es ideal cuando se actualizan más de 1 elemento DOM. Este enfoque es exactamente lo mismo que llamar "Ver (...)" en su acción - "ParitialView()" representa un fragmento de html y "View()" representa un documento html. A menos que quiera descartar el concepto de motores de vista, no estoy seguro de cómo puede estar tan en desacuerdo. –

+0

Completamente en desacuerdo. Mire http://code.google.com/closure/templates/docs/helloworld_js.html. Es un sistema de plantillas muy maduro y rápido, sin necesidad de una fea concatenación de cadenas. Mantener esta lógica separada le permite escribir otro cliente (iphone, swing, flash) sin reescribir el código del lado del servidor porque se lo relegó al servidor para hacer lo que mejor hace, proporcionar datos y mover la parte de la vista al cliente. El sistema de plantillas de Google también le permite ejecutar su lado del servidor de plantillas en caso de que necesite servir a un cliente que no sea compatible con JavaScript. –

2

Por qué no ambos JSON y html? En el proyecto actual estamos haciendo rutas para que pueda elegir desde la interfaz, qué formato es el más adecuado en algún caso ... entonces ¿por qué no hacer dos controladores, primero devolverá los datos esperados en json y el otro controlador regresará mismos datos pero con html ... de esta manera usted puede elegir, digamos jQuery, qué y cuándo quiere y en qué formato lo quiere ... y lo mejor es que, para un formato diferente, solo necesita llamar a una dirección diferente.

en otras palabras, que sea relajante, bebé! :)

aplausos

+0

Eso también podría resolverse utilizando el tipo de contenido de aceptación de la solicitud, ya que en realidad está buscando el mismo recurso (uri), pero una representación diferente. No sé si podría hacerse ASP.net MVC para encaminar a una acción diferente en función de eso, lo que me parece mejor que una caja de conmutación gigante en el tipo de contenido en el controlador de acción ... –

+0

Creo es más limpio que hizo algo como esto: {controlador}/{acción}/{...} y la de jQuery si necesitan formato JSON: JSON/lista/.... y si necesita html: html/list/... aplausos – Marko

2

En cuanto a mí, me eligen enfoque impulsado por los datos. Aquí es un pequeño conjunto de características para ambos:

basadas en datos:

  1. JSON desde el servidor (controlador envía modelo directamente a la respuesta)
  2. plantilla JS vinculante (puede tardar más tiempo en el lado del cliente)
  3. Ancho de banda bajo carga
  4. ¡Es tan sexy!

HTML-Driven:

  1. HTML de servidor (controlador envía el modelo a algún Vista parcial, que presta resultado y la devuelve - puede tardar más tiempo en el lado del servidor)
  2. No hay sobrecarga en JS vinculante
  3. carga de gran ancho de banda
  4. no es atractivo, no no :)

Así puede mantener MVC incluso con un enfoque basado en HTML, aunque será un poco más difícil.

4

Me gusta permitir que la aplicación de llamadas decida. He peiced juntos un MultiViewController (gran parte del código que encontré en línea, intentaré actualizar con el crédito cuando lo encuentre) que, en función de la extensión de la acción, devolverá el formato apropiado.por ejemplo:

myapp.com/api/Users/1 - defaults to html based on route 
myapp.com/api/Users.html/1 - html 
myapp.com/api/Users.json/1 - json 
myapp.com/api/Users.xml/1 - xml 
myapp.com/api/Users.partial/1 - returns a partial view of action name (see code) 
myapp.com/api/Users.clean/1 - partial html without styling, etc... 

Mis controladores heredan de MultiViewController y en lugar de "vista de retorno (Modelo);" Simplemente llamo "return FormatView (Model); o FormatView (" ViewName ", Model);". El segundo si necesito aplicar una vista específica al resultado, no la vista implícita.

El MultiViewController tiene este aspecto. Prestar especial atención a FormatView, générale devuelve un resultado de la acción:

public abstract class MultiViewController : Controller 
{ 
    private const string FORMAT_KEY = "format"; 
    public enum FileFormat {Html, Json, Xml, Partial, Clean} 

    protected MultiViewController() 
    { 
     RequestedFormat = FileFormat.Html; 
    } 
    protected FileFormat RequestedFormat { get; private set; } 

    protected override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 
     var routeValues = filterContext.RouteData.Values; 
     if (routeValues.ContainsKey(FORMAT_KEY)) 
     { 
      var requestedFormat = routeValues[FORMAT_KEY].ToString(); 
      if (isValidFormat(requestedFormat)) 
      { 
       RequestedFormat = (FileFormat)Enum.Parse(typeof(FileFormat), requestedFormat, true); 
      } 
     } 
    } 

    private bool isValidFormat(string requestedFormat) 
    { 
     return Enum.GetNames(typeof (FileFormat)).Any(format => format.ToLower() == requestedFormat.ToLower()); 
    } 

    protected ActionResult FormatView(string viewName, object viewModel) 
    { 
     switch (RequestedFormat) 
     { 
      case FileFormat.Html: 
       if (viewName != string.Empty) 
       { 
        return View(viewName,viewModel); 
       } 
       return View(viewModel); 
      case FileFormat.Json: 
       return Json(viewModel); 
      case FileFormat.Xml: 
       return new XmlResult(viewModel); 
      case FileFormat.Partial: 
      //return View(this.ControllerContext.RouteData.Values["action"] + "Partial"); 
      return PartialView(this.ControllerContext.RouteData.Values["action"] + "Partial"); 
      case FileFormat.Clean: 
       if (viewName != string.Empty) 
       { 
        return View(viewName, "~/Views/Shared/Clean.master", viewModel); 
       } 
       var v = View(viewModel); 
       v.MasterName = "~/Views/Shared/Clean.Master"; 
       return v; 
      default: 
       throw new FormatException(string.Concat("Cannot server the content in the request format: ", RequestedFormat)); 
     } 
    } 
    protected ActionResult FormatView(object viewModel) 
    { 
     return FormatView("", viewModel); 
    } 




} 

Clean.master es simplemente una página maestra que no contiene ningún tipo de HTML adicional - que toma la vista (para que pueda consolidar las clases parciales) y lo renderiza con html limpio que se puede colocar directamente.

Si quiero json - el controlador construye mi modelo de vista y luego devuelve ese modelo de vista como json, en lugar de enviar a la vista predeterminada - lo mismo con .xml.

Las vistas parciales son un poco interesantes ya que, por convención, todas mis vistas principales se dividen en parciales, por lo que ese parcial en particular se puede solicitar por sí mismo; esto es útil para imitar la funcionalidad de un panel de actualización usando jquery sin toda la basura asociada con el panel de actualización.

+0

Bastante interesante. Muy parecido a lo que Rails te permite hacer. Tiene sentido para muchas situaciones. –

Cuestiones relacionadas