Enviando JSON a una acción en ASP.NET MVC

por fernando  22. abril 2010

 

Descargar Código Fuente

El otro día estaba intentando enviar datos a una acción utilizando el AJAX de jQuery. En la mayoría de los casos, podrán utilizar este método y enviar datos a una acción sin problemas, ya que el default binder de MVC se encarga de interpretar los datos.

Mi problema surgió cuando intenté enviar datos más complejos. En el controlador tenía el siguiente método:

   1:          [AcceptVerbs(HttpVerbs.Post)]
   2:          public virtual JsonResult Actualizar(Producto producto)
   3:          {
   4:              //HACER ALGO 
   5:          }

Este método sólo acepta un POST, y tiene un argumento de tipo Producto. Veamos cómo está compuesta esta clase:

   1:      public class Producto
   2:      {
   3:          public int Id { get; set; }
   4:          public string Nombre { get; set; }
   5:          public IEnumerable<Promocion> Promociones { get; set; }
   6:   
   7:          public class Promocion
   8:          {
   9:              public string Id { get; set; }
  10:              public double Descuento { get; set; }
  11:              public DateTime FechaDesde { get; set; }
  12:              public DateTime FechaHasta { get; set; }
  13:          }
  14:      }

 

Para poder enviar esto utilizando jQuery y AJAX, lo más sencillo sería hacer:

   1:      var data = {
   2:          "Id": 1,
   3:          "Nombre": "Cualquier Nombre",
   4:          "Promociones": [
   5:         {
   6:             "Id": 1,
   7:             "Descuento": 0.15,
   8:             "FechaDesde": new Date(año, mes, día),
   9:             "FechaHasta": new Date(año, mes, día)
  10:         },
  11:         {
  12:             "Id": 2,
  13:             "Descuento": 0.05,
  14:             "FechaDesde": new Date(año, mes, día),
  15:             "FechaHasta": new Date(año, mes, día)
  16:         }
  17:       ]
  18:      };
  19:   
  20:      $.ajax({
  21:          type: "POST",
  22:          dataType: "json", // Tipo de dato que devuelve el server
  23:          url: 'Controller/Action',
  24:          data: data, // Datos a enviar
  25:          success: function(serverResponse) {
  26:              // Realizar algo cuando la llamada es exitosa
  27:          },
  28:          error: function() {
  29:              // Realizar algo cuando la llamada falla
  30:          }
  31:      });
 

Lo que estoy haciendo es crear un objeto con los miembro Id, Nombre y Promociones y asignándolo a la variable data.

Sin embargo, el miembro Promociones es un objeto de tipo Array. Por default, la función $.ajax() intentará convertir cualquier dato a un string. Como Promociones es un array, al intentar convertirlo a un string, terminará enviando al servidor lo siguiente:

IDDivision=6&IDBonificacion=LB6&Tipo=A&Vigencias=[object+Object]&Vigencias=[object+Object]

Es decir, jQuery serializa los valores de un mismo array con el mismo Key. Por ejemplo, {foo:["bar1", "bar2"]} se convierte en '&foo=bar1&foo=bar2'.

Sin embargo, como el valor de cada elemento de Promociones es otro objeto, lo que termina enviando es el .toString() del objeto, el cual es "[object Object]" justamente. El Default Model Binder de ASP.NET MVC, intentará hacer el bind al objeto Producto, pero debido a que jQuery serializo incorrectamente el objeto, el miembro Promociones terminará siendo null, en lugar de los datos que estábamos intentando enviar realmente.

Para resolver este problema, podemos convertir los objetos javascript a JSON. Para hacer esto, utilicé JSON for jQuery (link), pero se puede utilizar cualquier cosa que tome un objeto javascript y lo transforme a JSON. Al enviar los datos al server, debemos asegurarnos que el content type sea del tipo application/json, que es lo que estoy haciendo en la línea 5 del siguiente código:

 

   1:      var jsonString = $.toJSON(data);
   2:      $.ajax({
   3:          type: "POST",
   4:          dataType: "json", // Tipo de dato que devuelve el server
   5:          contentType: 'application/json', // Tipo de datos que envío
   6:          url: 'Controller/Action',
   7:          data: jsonString, // Datos a enviar
   8:          success: function(serverResponse) {
   9:              // Realizar algo cuando la llamada es exitosa
  10:          },
  11:          error: function() {
  12:              // Realizar algo cuando la llamada falla
  13:          }
  14:      });

Fíjense que antes de hacer la llamada a ajax(), estoy transformando la variable data a JSON, y pasando su resultado a la función $.ajax(), en lugar del objeto. La variable jsonString entonces quedará en:

'{"Id": 1, "Nombre": "Cualquier Nombre", "Promociones": [{ "Id": 1, "Descuento": 0.15, "FechaDesde": "2010-01-01T03:00:00.000Z", "FechaHasta": "2010-05-31T03:00:00.000Z" }, { "Id": 2, "Descuento": 0.05, "FechaDesde": "2010-06-02T03:00:00.000Z", "FechaHasta": "2010-08-01T03:00:00.000Z" } ] }'

Si prestan atención, podrán darse cuenta que el JSON string es exactamente igual que la declaración de la variable data, definida más arriba. La única diferencia es que jsonString es literalmente un string, y data es un objeto javascript.

Ahora, lo único que queda resolver es el Model Binding del lado del servidor. Lamentablemente, ASP.NET MVC 2 no interpreta JSON correctamente, por lo tanto vamos a tener que hacer algo para hacerlo funcionar.

El enfoque más común sería hacer un Custom Model Binder. Sin embargo, esto implica que en cada acción que recibe JSON, debemos indicar explícitamente a MVC que utilice nuestro Model Binder. Si se trata de una sola acción, no habría ningún problema, pero si hay muchas acciones que utilizan el Model Binder, y todas reciben distintos tipos de datos, se va a volver muy tediosa la implementación.

Por suerte, en MVC 2, tenemos algo llamado Value Providers. Mientras que los Model Binders son utilizados para bindear datos que recibe el servidor, Value Providers proveen una abstracción para los datos en sí.

Para resolver mi problema, creé un Custom Value Provider que recibe datos JSON y lo serializa a un diccionario, en lugar de al objeto. Luego, éste diccionario es pasado al Default Model Binder de MVC, el cual realiza el bind al objeto final, e incluso realiza cualquier validación que ustedes hayan indicado.

El Custom Value Provider es el siguiente:

   1:  public class JsonValueProviderFactory : ValueProviderFactory
   2:  {
   3:      public override IValueProvider GetValueProvider
   4:                                           (ControllerContext controllerContext)
   5:      {
   6:          object jsonData = GetDeserializedJson(controllerContext);
   7:   
   8:          if (jsonData == null)
   9:              return null;
  10:   
  11:          var dictionary = new Dictionary<string, object>
  12:                                             (StringComparer.OrdinalIgnoreCase);
  13:   
  14:          FlattenToDictionary(dictionary, string.Empty, jsonData);
  15:   
  16:          return new DictionaryValueProvider<object>
  17:                                       (dictionary, CultureInfo.CurrentCulture);
  18:      }
  19:   
  20:      private static object GetDeserializedJson
  21:                                           (ControllerContext controllerContext)
  22:      {
  23:          if (!controllerContext.HttpContext.Request.ContentType
  24:            .StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
  25:              return null;
  26:   
  27:          string bodyText = new StreamReader
  28:                (controllerContext.HttpContext.Request.InputStream).ReadToEnd();
  29:   
  30:          if (string.IsNullOrEmpty(bodyText))
  31:              return null;
  32:   
  33:          var serializer = new JavaScriptSerializer();
  34:   
  35:          return serializer.DeserializeObject(bodyText); 
  36:      }
  37:   
  38:      private static void FlattenToDictionary(
  39:            IDictionary<string, object> dictionary, string prefix, object value)
  40:      {
  41:          var dictionaryValue = value as IDictionary<string, object>;
  42:   
  43:          if (dictionaryValue != null)
  44:          {
  45:              foreach (KeyValuePair<string, object> entry in dictionaryValue)
  46:              {
  47:                  string propertyKey;
  48:   
  49:                  if (!string.IsNullOrEmpty(prefix))
  50:                      propertyKey = prefix + "." + entry.Key;
  51:                  else
  52:                      propertyKey = entry.Key;
  53:   
  54:                  FlattenToDictionary(dictionary, propertyKey, entry.Value);
  55:              }
  56:          }
  57:          else
  58:          {
  59:              var listValue = value as IList;
  60:              if (listValue != null)
  61:              {
  62:                  for (int i = 0; i < listValue.Count; i++)
  63:                      FlattenToDictionary(dictionary,
  64:                                          prefix + "[" + i + "]", listValue[i]);
  65:              }
  66:              else
  67:                  dictionary[prefix] = value;
  68:          }
  69:      }
  70:  }

Y luego, debemos agregar la siguiente línea en el Global.ajax de nuestra aplicación:

   1:  protected void Application_Start()
   2:  {
   3:          RegisterRoutes(RouteTable.Routes);
   4:          ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
   5:  }

Listo! Ahora al enviar JSON al servidor, nuestro Value Provider interceptará el request, y serializará los datos de tipo JSON a un diccionario que pueda ser interpretado por el Default Model Binder. Lo único que debemos recordar hacer es setear el ContentType a 'application/json', y éstos requests serán procesados por nuestro Value Provider.

Ahora, veamos un poco qué está haciendo el Value Provider.
Al único método que tenemos que hacerle override de la clase ValueProviderFactory es a GetValueProvider(). Lo que hace es deserializar JSON y transformarlo en un objeto .NET, luego lo serializa al diccionario y devuelve un DictionaryValueProvider, el cual es utilizado más tarde por el default model binder.

El método GetDeserializedJson() hace exactamente lo que dice, deserializa JSON. Hay que tener cuidado de pasarle un string que contenga JSON, porque puede arrojar una excepción si se pasa JSON invalido. Probablemente sería conveniente encerrar a la llamada DeserializeObject en un try/catch, y devolver null cuando falle.

Finalmente, el método FlattenToDictionary() es un método recursivo que recorre el objeto deserializado, que para este ejemplo es el siguiente:

clip_image002

Como verán, el objeto deserializado es un Dictionary<string, object>. Para Id y Nombre, el tipo de dato de Value es int y string respectivamente. Para Promociones, en cambio, es un Array de object, y cada elemento de ese Array es un Dictionary<string,object>. Lo que hace FlattenToDictionary() es recorrer todos estos elementos para 'aplanarlo' (flatten) a un diccionario. El método nos devuelve esto:

clip_image004

Si leyeron el post de Andres Stang probablemente esto les resulte familiar. Lo que hace el método es recorrer el objeto, y construir el Key de cada uno de los valores que contiene para poder insertarlo en un diccionario, donde todos los valores de cada elemento sea un tipo de dato primitivo. Este diccionario luego es interpretado por el default model binder. Es decir, lo que estamos haciendo es transformar JSON a un formato que pueda ser interpretado por el default model binder.

Les dejo el código fuente con una aplicación de ejemplo para que prueben:

JsonToMVCAction.rar (153,28 kb)

Tags: , , ,

.NET | ASP.NET | Desarrollo Web | JQuery

22 Librerias para gráficos y diagramas en la web

por Daniel Laco  17. marzo 2010

Siguiendo con la idea de tener reunido en el blog, las direcciones de plugins y librerias para gráficos en la web.

Me encontré con esta excelente lista http://www.onextrapixel.com/2009/11/24/22-awesome-visualization-libraries-charts-and-diagrams/

Aquí hay librerias para JQuery, pero tambien hechas en Flash, etc.

 

Tags: , ,

ASP.NET | Desarrollo Web | Javascript | JQuery

9 puntos para lograr posicionarse (SEO)

por Alejandra Federico  30. enero 2010


9 puntos para lograr posicionarse (seo)

 

El SEO (Search Engine Optimizer) consiste en aplicar diversas técnicas que tienden a lograr que los buscadores de internet sitúen nuestra página en una buena posición y categoría alta, para determinados términos y frases claves.

A la hora de querer posicionarnos primeros debemos tener en cuenta los siguientes puntos:

  • Para empezar debemos focalizarnos en el título. Es lo más importante, primero porque los buscadores le dan bastante importancia a las palabras del título (sobre todo google) y luego porque es lo primero que se ve en la página de resultados. Hay que redactar un título que contenga las palabras claves en las que queramos estar bien posicionados y que sea atractivo al usuario que ve el resultado de la búsqueda. Debemos evitar colocar artículos (el, la, un, una). No se recomienda que sea superior a 60 caracteres, debe poseer aproximadamente entre 4 a 6 palabras. Los buscadores le dan mucha importancia a las palabras en negrita y de mayor tamaño.
  • Otro punto a tener en cuenta son las palabras claves. Para poder aparecer en los buscadores es necesario que nos focalicemos en aquellas palabras por las cuales creemos que nos van a buscar. Estas palabras claves deben ir en los títulos, etiquetas alt de las imágenes, los metatags, las URLS de nuestra página y en los enlaces de otras web.
    Se deben medir la densidad de las palabras claves. La densidad es el ratio de apariciones de una palabra o frase clave en el total de palabras que componen un texto. Por ejemplo, para un texto de 200 palabras, si una palabra aparece 10 veces, su densidad será del 5%.
    La densidad de palabras clave es uno de los factores que tienen en cuenta los buscadores para ordenar los resultados de una búsqueda.
    Para poder chequear como se encuentra nuestro sitio con respecto a la densidad existen algunos programas que nos indican cuales son las palabras más relevantes que figuran en nuestra web: 
  • Nuestro sitio debe poseer contenido de calidad de esta manera vamos a lograr que otras web nos enlacen. El blog nos puede servir de gran utilidad para que otros realicen visitas. Como mínimo hay que publicar un artículo por mes. Un modo para poder mantenerlo es obtener de otro sitio algún artículo interesante y copiar el link de donde fue sacado.
  • Los enlaces funcionan como una especie de voto o adhesión a una web en particular. Si los enlaces son de calidad esa valoración será positiva, de lo contrario nos perjudicarán.
  • En las etiquetas se debe utilizar H1 H2 H3… El H1 es recomendable que haya uno sólo por página, H2
    y H3 se pueden repetir algunas veces, aunque siempre ubicados de forma
    correcta:

    H1 para el título
    H2 para el título de las entradas
    H3 para los comentarios y otras subcategorías
  • No se debe insertar contenido importante en pdf, medios gráficos o flash. El abuso de JavaScript y Flash dificulta que un buscador nos "entienda".
  • Participar en redes sociales, por ejemplo Linkedin, Facebook, Hi5, Orkut, y de esta manera poder obtener mas visitas a nuestro sitio web.
  • Se debe realizar un mapa del sitio (site map). Si no se tiene es conveniente confeccionar un mapa del sitio actual, que contenga todas las URL’s de la páginas publicadas, incluyendo sus títulos, descripciones, palabras clave, meta tag robots, fecha de última modificación y nivel o algún tipo de clasificación de importancia dentro del árbol.
  • Puntos que no hay que realizar para NO ser penalizados por los buscadores:
    1. Evitar utilizar textos ocultos
    2. Evitar utilizar enlaces ocultos.
    3. No cargar la página con palabras claves irrelevantes.
    4. No enviar consultas automatizadas a google.
    5. Venta y/o compra de enlaces con el sólo motivo de aumentar artificialmente el PageRank.
    6. Técnicas de alto riesgo como el cloaking (mostrar dos o más páginas diferentes según quien desee visualizarla, es decir, no enviar la misma página al usuario y al buscador).
    7. Páginas Doorway (páginas creadas con el propósito de posicionarse para una determinada palabra clave y, entonces, ser una página de entrada a la web alternativa) creadas masivamente o de contenido prácticamente idéntico.
    8. Enlaces entrantes, es decir, hacia nuestra web, en cantidad masiva desde weblogs, guestbooks o granjas de enlaces.
    9. Enlaces salientes, es decir, desde nuestra web, hacia sitios web que realizan prácticas de alto riesgo o SPAM.
    10. No crear varias páginas, subdominios o dominios que presenten básicamente contenido duplicado.

Como conclusión, en el posicionamiento juegan cientos de variables y algorítmos que cambian día a día. Por más que en determinados
momentos se logren excelentes resultados, nadie tiene la verdad. No es una ciencia cierta.

Tags: , ,

ASP.NET | Desarrollo Web

Librerias de gráficos en Javascript

por Daniel Laco  18. enero 2010

En su blog Hector Insua (http://hinsua.blogspot.com) publicó unos comentarios de unas librerias realizadas en Javascript que nos permiten hacer graficaciónes avanzadas con un bajo costo de desarrollo.

Me he tomado el atrevimiento de reproducir su post, asi nos queda referencia de los links correspondientes.

Algunas de las librerias al utilizar SVG y Canvas no funcionan adecuandamente en IE 8, asi que antes de incorporarlas a algún desarrollo, verificar su funcioanmiento.

Aqui va la transcripción del artículo:

4 Nuevos componentes de Graficación!

image

JavaScript InfoVis es otro componente en base a Java que podemos utilizar en nuestros proyectos de SharePoint, provee nuevas funcionalidades que no podemos lograr con Visifire o FusionCharts, por ejemplo para el armado de Redes Sociales u Organigramas, es Gratuito y tiene una excelente calidad Grafica.

image

Timeline de Smile Widgets permite generar Lineas de tiempo con elementos que despliegan informacion en cada uno de los puntos, de gran ayuda en proyectos y facil de implementar.

image

Timeplot Tambien de Smile Widgets, genera interesantes graficos con evolucion de elementos a lo largo de tiempo, cada uno de los puntos permite visualizar la informacion y la fecha, ademas es posible adicionar comentarios en cada uno de ellos, tambien es un proyecto gratuito. (Basado en Ajax)

image image image

ProtoVis nos permite generar graficos complejos, importantes para proyectos de Business Intelligence como son los TreeMaps, tambien es gratuito, es un proyecto desarrollado por la Universidad de Standford, no es muy dificil de implementar en sharepoint y con un alto impacto.

Lo importante a destacar es que hay muchisimos componentes de este estilo y representa una interaccion de Java con SharePoint, nos ayudan muchisimo a mejorar la grafica de nuestros proyectos, son mayoritariamente gratuitos, con mucho desarrollo, muy bien documentatos, con numerosos ejemplos publicados y todo es en pro de mejorar la Experiencia del Usuario, espero les sean de utilidad.

 

 

Tags: , ,

ASP.NET | Desarrollo Web | Javascript | JQuery

Excelente librería de gráficos de Barras, Curvas, etc. en Javascript

por Daniel Laco  13. enero 2010

No dejo de asombrarme con las cosas que desarrollan los programadores sobre javascript En http://www.highcharts.com/ se puede encontrar una impresionante libreria que permite generar gráficos de Torta, Barras, etc. para aplicaciones webs.

 

        

Tags: , ,

ASP.NET | JQuery | Javascript | Desarrollo Web

240 JQuery Plugins

por Daniel Laco  10. enero 2010

Tags: , ,

ASP.NET | JQuery | Javascript | Desarrollo Web

Acerca de los Autores

Este es el blog del equipo de VEMN SA 
Presentaremos temas que nos parezcan de interés sobre tecnología .NET, Procesos y Metodologías y todo aquello relacionado con el proceso de desarrollo de Software

Month List

BlogRoll

Download OPML file OPML