Y el combate se decidió por KO (i)

Hace algunas semanas salió un post de Shaun Walker titulado “Microsoft Declares the future of ASP.NET is Web API”. La verdad es que el post es interesante. Yo no sé cuales serán las intenciones de Microsoft (creo que ni ellos las saben realmente) pero lo que si es cierto es que las aplicaciones web están realmente cambiando a un modelo donde cada vez se procesa más en cliente y menos en servidor. Es un modelo que deja totalmente obsoleto no solo a Webforms si no incluso a ASP.NET MVC.

En este modelo las páginas o vistas de una aplicación web, ya no son servidas desde el servidor. En todo caso, se sirve un “bootstrapper” inicial, que contiene el código javascript que realiza la petición inicial y luego todo es modificar el DOM en cliente a partir de datos recibidos por servicios REST, usando javascript. Por lo tanto dejamos de servir contenido de presentación (HTML generado dinámicamente) desde el servidor, para servir tan solo datos. Y la conversión de datos a algo visible (DOM) se realiza en el propio cliente, usando javascript.

A nivel tecnológico, salvando quizá cuestiones de rendimiento (que no son en absoluto despreciables, ahí está la experiencia de Twitter) estamos ya bastante preparados para dar este salto. Y aquí entra lo que os quería comentar en el post de hoy. Vamos a iniciar una serie, de longitud desconocida, hablando de Knockout, una librería javascript que resumiendo lo que hace es implementar el patrón MVVM en javascript.

Como siempre no soy el primero en hablar de Knockout en geeks. Concretamente el Maestro se me ha avanzado con un post (http://geeks.ms/blogs/jmaguilar/archive/2012/05/07/knockout-i-pongamos-orden-en-el-lado-cliente.aspx).

También está Marc Rubiño que tiene una presentación en slideshare: http://www.slideshare.net/webcatbcn/javascript-con-mvvm-knockout-por-marcrubino 

Pero por encima de todos Sergio Marrero que tiene una serie impresionante sobre Knockout en su blog: http://smarrerof.blogspot.com.es/search/label/knockout

Pero bueno… Yo intentaré aportar mi grano de arena y intentaré acercaros un poco Knockout para que los que no lo conozcáis os echeis las manos a la cabeza y os preguntéis como narices habeis podido hacer aplicaciones web todo este tiempo 🙂

¡Espero que esta sea una larga serie de posts, porque realmente hay mucho que contar!

Introducción a Knockout

Knockout es una librería javascript que básicamente nos da soporte para el patrón MVVM. Este patrón, bien conocido para los que desarrollan en WPF, Silverlight o WinRT, es en realidad un MVP Supervising Controller con dos peculiaridades:

  1. Se crea un modelo específicamente para presentación. Es decir, no se usa el “Modelo” real para tareas de presentación, si no que se crea otro modelo específicamente para ello. En muchos casos este otro modelo está adaptado y optimizado para cada vista. En MVVM conocemos a este otro modelo con el nombre de ViewModel.
  2. Se promueve que la comunicación entre la vista y su viewmodel sea a través de bindings (bidireccionales).

Lo que Knockout nos permite hacer de forma muy rápida es, realizar estos bindings entre elementos del DOM y nuestro viewmodel así como crear elementos del DOM a partir del propio viewmodel (lo que antes hacíamos con jquery_tmpl p.ej.)

Hello Knockout

Vamos a empezar con el primer proyecto usando Knockout. En este caso vamos a montar una tabla que muestre información de cervezas. Para ello, nos creamos un nuevo proyecto de tipo WebApi, donde vamos a añadir un controlador:

   1: public class ApiBeersController : ApiController

   2: {

   3:     public IEnumerable<Beer> GetAll()

   4:     {

   5:         yield return new Beer { Name = "Estrella Damm", Abv = 5.4M, Ibu = 21 };

   6:         yield return new Beer { Name = "Heineken", Abv = 5.0M, Ibu = 10 };

   7:     }

   8: }

La clase Beer simplemente contiene las propiedades Name, Abv e Ibu.

Si ahora abrimos una URL y navegamos a /api/apibeers deberíamos recibir un json:

[{"Name":"Estrella Damm","Abv":5.4,"Ibu":21},{"Name":"Heineken","Abv":5.0,"Ibu":10}]

Nota: Si recibes un XML en lugar de un JSON eso es debido a la cabecera Accept que envía el navegador. Una solución para ello es indicarle a WebApi que nunca devuelva resultados en XML, lo que se consigue colocando la siguiente línea en el Application_Start: GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

 

Ahora vamos a incluir a Knockout en nuestra página master.

Para ello damos de alta un nuevo bundle para incluir Knockout (en el método RegisterBundles de la clase BundleConfig):

   1: bundles.Add(new ScriptBundle("~/bundles/ko").Include(

   2:                         "~/Scripts/knockout-2*"));

Ok, ahora vamos a crear un controlador “normal” que nos devuelva la vista que debe mostrar las cervezas:

   1: <h2>Index de cervezas</h2>

   2:  

   3: <script type="text/javascript">

   4:     $(document).ready(function () {

   5:         var url="@Url.RouteUrl("DefaultApi", new {httproute="", controller="ApiBeers"})";

   6:         $.getJSON(url, function(data) {

   7:             ko.applyBindings({beers: data});

   8:         });

   9:     });

  10: </script>

  11:  

  12:  

  13: <div id="beers">

  14:     <table>

  15:         <tbody data-bind="foreach:beers">

  16:             <tr>

  17:                 <td data-bind="text: Name" />

  18:                 <td data-bind="text: Abv" />

  19:                 <td data-bind="text: Ibu" />

  20:             </tr>

  21:         </tbody>

  22:     </table>

  23: </div>

Analicemos el código:

  1. Al cargarse la página usamos $.getJSON para obtener los datos de las cervezas
  2. Una vez hemos obtenido el objeto JSON con las cervezas (recordad que era un IEnumerable<Beer> en C# y un array puro en javascript) creamos un objeto con una propiedad llamada “beers” cuyo contenido es precisamente este array de cervezas.
  3. Llamamos al método applyBindings, para Knockout haga su magia. A applyBindings se le pasa el viewmodel a usar.

¿Y como hace el enlace Knockout? Pues, y ahí radica su gracia, usa un atributo propio llamado data-bind. Recordad que HTML5 nos permite definir nuestros propios atributos siempre y cuando empiecen por data-. Knockout buscará los elementos que estén marcados con este atributo data-bind y realizará distintas tareas según el valor de dicho atributo y del viewmodel definido. En este primer ejemplo vemos dos usos de data-bind:

  • foreach: propiedad –> Itera sobre todos los elementos de la propiedad especificada. En nuestro caso usamos foreach:beers, ya que beers es el nombre de la propiedad de nuestro viewmodel que contiene el array de cervezas. Por cada elemento de la propiedad beers, knockout repetirá todos los elementos que estén dentro del elemento que contiene el foreach. En nuestro caso creará un <tr> por cada elemento.
  • text: propiedad –> Muestra el texto de la propiedad especificada. Como estamos dentro de un foreach muestra la propiedad especificada del elemento que se está “renderizando”.

El resultado de crear esta vista es, tal y como seguro que esperabais:

image

Bueno… ¿no está nada mal por un par de líneas, no? ¿A que ya os está picando la curiosidad?

En los próximos posts iremos ampliando y viendo muchas otras cosas que Knockout nos ofrece! 😀

Bundles en ASP.NET MVC4

¡Buenas! Este va a ser un post cortito, sobre los Bundles en ASP.NET MVC. Los bundles es el mecanismo que tiene ASP.NET MVC para incluir varios ficheros (de script o css) que están relacionados entre ellos.

Si os creáis un proyecto de ASP.NET MVC4 nuevo (sin que sea la plantilla Empy, claro) veréis el siguiente código en la página de Layout:

   1: <!DOCTYPE html>

   2: <html>

   3: <head>

   4:     <meta charset="utf-8" />

   5:     <meta name="viewport" content="width=device-width" />

   6:     <title>@ViewBag.Title</title>

   7:     @Styles.Render("~/Content/themes/base/css", "~/Content/css")

   8:     @Scripts.Render("~/bundles/modernizr")

   9: </head>

  10: <body>

  11:     @RenderBody()

  12:  

  13:     @Scripts.Render("~/bundles/jquery")

  14:     @RenderSection("scripts", required: false)

  15: </body>

  16: </html>

En lugar de incluir directamente las etiquetas <link> (para las CSS) o <script> para referenciar a archivos js externos, lo que tenemos son llamadas a Styles.Render y Scripts.Render.

El parámetro que se pasa a esos métodos Render es el nombre del bundle a renderizar. ¿Y donde están definidos los bundles? Pues en el archivo App_Start/BundleConfig tenéis el siguiente método:

   1: public static void RegisterBundles(BundleCollection bundles)

   2: {

   3:     bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

   4:                 "~/Scripts/jquery-1.*"));

   5:  

   6:     bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(

   7:                 "~/Scripts/jquery-ui*"));

   8:  

   9:     bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(

  10:                 "~/Scripts/jquery.unobtrusive*",

  11:                 "~/Scripts/jquery.validate*"));

  12:  

  13:     bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(

  14:                 "~/Scripts/modernizr-*"));

  15:  

  16:     bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

  17:  

  18:     bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(

  19:                 "~/Content/themes/base/jquery.ui.core.css",

  20:                 "~/Content/themes/base/jquery.ui.resizable.css",

  21:                 "~/Content/themes/base/jquery.ui.selectable.css",

  22:                 "~/Content/themes/base/jquery.ui.accordion.css",

  23:                 "~/Content/themes/base/jquery.ui.autocomplete.css",

  24:                 "~/Content/themes/base/jquery.ui.button.css",

  25:                 "~/Content/themes/base/jquery.ui.dialog.css",

  26:                 "~/Content/themes/base/jquery.ui.slider.css",

  27:                 "~/Content/themes/base/jquery.ui.tabs.css",

  28:                 "~/Content/themes/base/jquery.ui.datepicker.css",

  29:                 "~/Content/themes/base/jquery.ui.progressbar.css",

  30:                 "~/Content/themes/base/jquery.ui.theme.css"));

  31: }

Dicho método, que es llamado desde el Application_Start, es donde se asocia cada bundle con un conjunto de archivos (fijaos que se puede usar * para indicar un conjunto de archivos).

Así vemos que el bundle ~/bundles/modernizr se compone de los archivos que estén en ~/scripts y cuyo nombre empiece por mondernizr-

Esto en tiempo de ejecución no hace nada especial, la verdad. Si abrimos cualquier vista y le damos a “ver código fuente” lo que vemos es simplemente el conjunto de tags <link> o <script>:

   1: <link href="/Content/themes/base/jquery.ui.core.css" rel="stylesheet" type="text/css" />

   2: <link href="/Content/themes/base/jquery.ui.resizable.css" rel="stylesheet" type="text/css" />

   3: <link href="/Content/themes/base/jquery.ui.selectable.css" rel="stylesheet" type="text/css" />

   4: <link href="/Content/themes/base/jquery.ui.accordion.css" rel="stylesheet" type="text/css" />

   5: <link href="/Content/themes/base/jquery.ui.autocomplete.css" rel="stylesheet" type="text/css" />

   6: <link href="/Content/themes/base/jquery.ui.button.css" rel="stylesheet" type="text/css" />

   7: <link href="/Content/themes/base/jquery.ui.dialog.css" rel="stylesheet" type="text/css" />

   8: <link href="/Content/themes/base/jquery.ui.slider.css" rel="stylesheet" type="text/css" />

   9: <link href="/Content/themes/base/jquery.ui.tabs.css" rel="stylesheet" type="text/css" />

  10: <link href="/Content/themes/base/jquery.ui.datepicker.css" rel="stylesheet" type="text/css" />

  11: <link href="/Content/themes/base/jquery.ui.progressbar.css" rel="stylesheet" type="text/css" />

  12: <link href="/Content/themes/base/jquery.ui.theme.css" rel="stylesheet" type="text/css" />

  13: <link href="/Content/site.css" rel="stylesheet" type="text/css" />

  14:  

  15: <script src="/Scripts/modernizr-2.0.6-development-only.js" type="text/javascript"></script>

Pero lo bueno de usar los bundles no es tan solo poder agrupar lógicamente nuestros archivos javascript o css. Lo bueno es que estamos a una sola propiedad de distancia de poder agruparlos y minificarlos (pésima palabreja lo sé, pero soy incapaz de encontrar una traducción para minification).

Así, si en el Application_Start añadimos la línea:

   1: BundleTable.EnableOptimizations = true;

Si ahora ejecutamos y miramos el código fuente veremos lo siguiente:

   1: <link href="/Content/themes/base/css?v=UM624qf1uFt8dYtiIV9PCmYhsyeewBIwY4Ob0i8OdW81" rel="stylesheet" type="text/css" /> 

   2: <link href="/Content/css?v=ZAyqul2u_47TBTYq93se5dXoujE0Bqc44t3H-kap5rg1" rel="stylesheet" type="text/css" /> 

   3: <script src="/bundles/modernizr?v=EuTZa4MRY0ZqCYpBXj_MhJfFJU2QBDf0xGrV_p1fHME1" type="text/javascript"></script>

Podemos ver como tenemos tan solo dos tags <link> y uno <script> en lugar de todos los tags de antes. Ahora cada Bundle ha generado un solo tag. Eso, ya de por sí es bueno, ya que se reduce el número de peticiones http (una de las cosas que más tardan a la hora de cargar una página web es ir abriendo y cerrando conexiones http).

Pero no solo eso, sino que además ahora el contenido está “minificado”, es decir se han eliminado todos los espacios y saltos de línea redundantes y se renombran todas las variables y nombres de función posibles para que tengan una longitud menor, de forma que el tamaño final sea menor, lo que a su vez redunda en un mejor rendimiento.

Y todo ello tan solo habilitando la EnableOptimizations.

Un par de curiosidades…

Hoy en día muchas librerías js incluyen dos .js: uno sin normal y el otro minificado. Usualmente (por una de esas convenciones que terminan siendo estándares de facto) el archivo minificado tiene el sufijo “min”. Pongamos a jQuery como ejemplo. Si te lo descargas verás dos archivos como p.ej.

  1. jquery-1.6.2.js
  2. jquery-1.6.2.min.js

El primero es el archivo .js normal (para usar en debug p.ej.) y el otro es el archivo minificado. Funcionalmente ambos son equivalentes pero el primero ocupa unas 230K y el segundo unas 91.

Pues bien, imaginad que tenemos un bundle para jQuery, definido de la siguiente forma:

   1: bundles.Add(new ScriptBundle("~/bundles/jquery")

   2: .Include("~/Scripts/jquery-1.6.2*"));

Fijaos que es un bundle que incluiría cualquier archivo js cuyo nombre empezase por jquery-1.6.2. Y en nuestro directorio ~/Scripts tenemos dos archivos que empiezan por este nombre, jquery-1.6.2.js y jquery-1.6.2.min.js. Pues bien el bundle es lo suficientemente inteligente como:

  • Generar tan solo el tag <script> para el archivo jquery-1.6.2.js en el caso de que las optimizaciones estén deshabilitadas (y no generar el tag para el archivo jquery-1.6.2.min.js).
  • Retornar el contenido del archivo jquery-1.6.2.min.js en el caso de que las optimizaciones estén habilitadas. Aunque ASP.NET igualmente minifica el propio archivo (aunque ya esté minificado). Es decir coje el archivo jquery-1.6.2.min.js, lo minifica y devuelve el contenido. Debo reconocer que me ha sorprendido que minifique de nuevo el contenido, pero es es lo que hace.

De hecho en el propio Solution Explorer de VS2012 ya se aprecia que VS “sabe” que ambos archivos están relacionados:

image

Espero que este post os haya resultado de interés! 😉

Saludos!

[OT] El peor móvil de la história

Este es un post para desahogarme… 🙂

¿Cual es para vosotros el peor móvil de la historia? O al menos, ¿el peor móvil que nunca hayáis tenido? El mío, lo tengo clarísimo: El LG Optimus 2X del que soy un desgraciado poseedor ahora mismo.

La verdad es dificil hacerlo tan mal como lo ha hecho LG. Parece imposible tanta incompetencia junta a todos los niveles.

Este móvil era el estandarte de LG cuando salió a principios del 2011. Fue el primer móvil en tener doble núcleo, y así lo anunciaron. Vigilad siempre si cuando os anuncian un móvil lo que más os recalcan es el hardware. Vigiladlo muy, pero que muy bien. El Optimus 2X era (y sigue siendo) una bestia parda. Un hardware excelente para su época. Evidentemente ahora palidece un poco por culpa de esa estúpida carrera en ver quien monta el pepinaco más grande en que se ha convertido el desarrollo de móviles Android, pero sigue siendo un excelente hardware hoy en día.

El problema es que en LG parecen no saber (y lo que es peor, ni querer enterarse) de que un teléfono no es un pedazo hierro. Yo cuando compro un móvil lo que quiero es poder usar aplicaciones, jugar, escuchar música. navegar por internet y de vez en cuando hasta llamar y todo. El problema es que ninguna de esas cosas es factible con el Optimus 2X. ¿La razón? La peor ROM de Android que pueda ser compilada nunca jamás. Un listado ni mucho menos completo de “maravillas” de las que es capaz el Optimus 2X incuye lo siguiente:

  1. Colgarse cuando te llaman. Quedarse el móvil tan frito, que debes reiniciarlo quitando la batería.
  2. Perder la cobertura y no recuperarla hasta que se reinicia el móvil. A veces ocurre con los datos. A veces se debe reiniciar varias veces.
  3. La que ocurre más a menudo: le coge un jamacuco y entonces todo va muy lento (pero muy lento eh? pulsar un botón puede llevar MINUTOS). El problema es que este jamacuco puede durar una hora… o más. Cuando eso ocurre la única solución es quitar la batería, ya que está tan frito que ni el botón de parar responde. Lo más triste es que a veces al cabo de 10 minutos de volver a arrancarlo está igual.
  4. Continuamente salen mensajes de que la aplicación XXX (la que queráis) no responde. Por supuesto el móvil está tan frito cuando eso ocurre que darle al botón de “cerrar aplicación” es una tarea titánica, que puede llevar cinco minutos o más.
  5. Escuchar música a veces es como escuchar canal+, de lo entrecortada que se oye.
  6. No se conecta con el Exchange de mi curro… Contando con que es el móvil profesional eso lo invalida bastante. Tengo que usar un programa externo, lo que implica que me puedo olvidar de sincronizar calendarios o contactos. Eso puede ser culpa de la configuración del exchange, no lo niego pero… el resto de móviles Android no tienen ningún problema.ç

Lo más triste es que a veces te “regala” con algunos minutos de funcionamiento perfecto, donde se vislumbra el potencial que el móvil tiene: todo va fluido, perfecto, rápido, suave… hasta que al cabo de unos minutos aparece su verdadera naturaleza y es un infierno de nuevo.

Eso convierte al Optimus 2X en un móvil que tan solo sirve para estamparlo contra la pared (más de una vez he estado a punto, lo juro). Y la culpa es LA PUTA MIERDA DE SOFTWARE DE LG. Yo desde mi humilde tribuna le pido a LG que si quiere modificar Android, contrate a desarrolladores que sepan un poco. Y que prueben su producto. Porque sacar un móvil con el software que trae el Optimus 2X debería estar penado con penas de cárcel… Y cada actualización que sale (tampoco muchas no os penséis) no sirve para nada. Hace poco sacaron una que me ha dejado el móvil igual de mal que antes.

Y es que si encima hablamos de las actualizaciones ya es para llorar. Porque lo bueno es que cuando me pasé al LG Optmus 2X, un servidor ya tenía Gingerbread instalado en su Galaxy S. Y el Optimus 2X salió con Froyo. Pero, ¡no pasa nada! Desde LG juraron y perjuraron que en breve salía la actualización a Gingerbread…

… y salió más de medio año después. De hecho, salió la actualización a Gingerbread poco antes de que Google liberase ICS. Evidentemente preguntarle a LG para cuando la actualización era totalmente inútil. Su respuesta era siempre la misma “no lo sabemos”, “ya daremos fechas”, “no falta mucho”, “en breve” o cualquier otra excusa… Para una muestra reciente: https://twitter.com/LG_ES/status/222247495063973888 o https://twitter.com/LG_ES/status/222211821753483264 o tantas otras que podéis encontrar navegando por su timeline.

¿La solución? Lo que voy a hacer justo ahora: meter una ROM no oficial y listos. Peor es imposible que me vaya el móvil. Pero eso no debería ser así. Por mi parte no pienso tener nunca jamás un móvil LG… y nada suyo que lleve software ya de paso.

Ale… ya me he desfogado.

ASP.NET MVC: Encriptar RouteValues

Muy buenas! El otro día publicaba en mi blog una solución para encriptar la querystring en ASP.NET MVC. A raiz de este post, me preguntaron si era posible hacer lo mismo pero en el caso de que tengamos URL amigables y como se podría hacer.

La respuesta es que sí, que se puede hacer y que a diferencia del caso de la querystring tenemos dos opciones.

Opción 1 – Value Provider

En este caso el planteamiento es el mismo que en el post anterior, debemos crear un value provider que recoja los datos y los desencripte.

Así, empezamos por crear la factoría de nuestro nuevo value provider:

public class RouteValuesEncriptedValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        return new RouteValuesEncriptedValueProvider(controllerContext);
    }
}

Y el código de nuestro value provider, muy parecido al del post anterior. La diferencia está en que en lugar de mirar en la request los valores de la querystring, miramos en RouteData que es donde están guardados todos los valores de ruta. Los valores de ruta se extraen a partir del PathInfo, es decir la parte de la URL que va después del host hasta el inicio de la querystring. El código es muy sencillo:

class RouteValuesEncriptedValueProvider : DictionaryValueProvider<string>
{

    public RouteValuesEncriptedValueProvider(System.Web.Mvc.ControllerContext controllerContext)
        : base(GetRouteValueDictionary(controllerContext), Thread.CurrentThread.CurrentCulture)
    {
    }

    private static IDictionary<string, string> GetRouteValueDictionary(ControllerContext controllerContext)
    {
        var dict = new Dictionary<string, string>();
        foreach (var key in controllerContext.RouteData.Values.Keys.Where(x => x.First() == '_'))
        {
            var value = controllerContext.RouteData.GetRequiredString(key);
            var decripted = Crypto.DecryptValue(value);
            dict.Add(key.Substring(1), decripted);
        }
        return dict;
    }
}

El funcionamiento es análogo al del value provider del post anterior y actúa sobre los route values cuyo nombre empieze por un subrayado (aunque por supuesto esto va a gustos del consumidor). Dado que el nombre de los route values se establece en la tabla de rutas, debemos cambiar la definición para que allí los nombres empiecen por un subrayado:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{_id}",
    defaults: new { controller = "Home", action = "Index", _id = UrlParameter.Optional }
);

Al igual que en el value provider del post anterior, al añadir el valor desencriptado de un route value, lo hace eliminando el subrayado inicial, por lo que en nuestros controladores podemos declarar el parámetro como “id” y recibiremos el valor desencriptado (si lo declarásemos como _id lo recibiríamos encriptado).

Pero, en este caso especial tenemos otra opción distinta para desencriptar los valores de ruta…

Opción 2: Un custom route handler

Hemos visto como este value provider nuevo no accede a la request, ni mira la URL. En su lugar accede a los valores de ruta (controllerContext.RouteData). Pero… ¿quien rellena esto? Pues el route handler.

Cada vez que una ruta es seleccionada, se ejecuta su route handler, que es el encargado de rellenar los valores de ruta. ASP.NET MVC viene con un route handler por defecto, pero nos podemos crear el nuestro. En este caso, nada nos impide crear un route handler que desencripte los valores antes de añadirlos:

public class UnencriptedRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
    {
        foreach (var rd in requestContext.RouteData.Values.Where(x=>x.Key.First()== '_').ToList())
        {
            var value = rd.Value.ToString();
            var decrypted = !string.IsNullOrEmpty(value) ?
                Crypto.DecryptValue(value) :
                string.Empty;
            requestContext.RouteData.Values.Remove(rd.Key);
            requestContext.RouteData.Values.Add(rd.Key.Substring(1), decrypted);
        }


        return base.GetHttpHandler(requestContext);
    }
}

Si miramos el código, vemos como accedemos a los route values (que en este punto ya están relleanados por el route handler por defecto del cual derivamos) y buscamos aquellos que empiecen por un subrayado y en su lugar los colocamos de nuevo pero desencriptados (y sin subrayar).

Ahora debemos indicarle a ASP.NET MVC que use este route handler nuestro, y eso se hace en la definición de la tabla de rutas:

routes.Add(
    "Default",
    new Route("{controller}/{action}/{_id}",
                new RouteValueDictionary(new { controller = "Home", action = "Index", _id = UrlParameter.Optional }),
                new UnencriptedRouteHandler())
);

En lugar de usar MapRoute, usamos el método Add, el cual espera un objeto Route al cual de lo podemos especificar cual será su route handler. Además fijaos en que hemos declarado _id en lugar de id, eso es porque nuestro route handler actuará tan solo sobre los route values que empiecen por el subrayado.

Con esto ya hemos terminado. Ahora, si generamos URLs que tengan el _id encriptado:

@Html.ActionLink("Test", "Test", 
new {_id=MvcApplication4.Crypto.EncryptValue("bar value")}, null)

Podemos declarar en el controlador la acción que espere un parámetro llamado id que tendrá el valor ya desencriptado. A diferencia de la opción anterior, ahora si usamos _id como parámetro recibiremos null. El valor encriptado se ha perdido y no existe a todos los efectos.

Usando esta segunda opción no hay necesidad alguna de crear un value provider propio.

¿Y las diferencias?

Las diferencias entre las dos opciones son básicamente el momento en que se produce la desencriptación:

  1. En la primera opción (value provider), la desencriptación se produce tan solo en el momento de enlazar los parámetros al controlador. Y la desencriptación es tan solo visible para el model binder. Así p.ej. si hay filtros que actuen sobre los valores de ruta, estos verán los valores encriptados.
  2. En la segunda opción, justo al inicio de procesarse la request, antes incluso de decidirse que controlador y que acción deben ser invocados, los valores de ruta son desencriptados. El valor encriptado desaparece y todo el mundo (y no sólo el model binder) tiene acceso a los valores desencriptados.

Si me preguntáis en mi caso que opción escogería… probablemente la segunda 🙂

Un saludo!

PD: En el caso de la querystring no existe la posibilidad de esta segunda opción. Los route handlers actúan tan solo sobre los valores de ruta, no sobre la querystring, por lo que el value provider es la única opción.

ASP.NET MVC: Encriptar la query string

Buenas! Este post surge debido a esta pregunta del foro de ASP.NET MVC. El usuario se pregunta si existe en el framework una manera built-in de encriptar la query string. Y la realidad es que no, no la hay, pero añadir una es muy sencillo y me da una excusa perfecta para poner un buen ejemplo del poder de los value providers.

ASP.NET MVC está construído de una forma bastante flexible, pero en el pipeline de una petición hay más o menos 4 pasos:

  1. Procesar la url para generar unos valores de ruta
  2. En base a dichos valores seleccionar una acción de un controlador
  3. Explorar la petición http en busca de parámetros (que pueden estar en la querystring, en formdata, etc)
  4. A partir de esos parámetros rellenar los parámetros de la acción del controlador

De todos esos pasos, el tercero es responsabilidad de los value providers: ellos inspeccionan la petición http en busca de parámetros y los dejan en “un sitio común”. Luego, en el paso cuatro, el model binder recoge los parámetros de este sitio común y a partir de ellos instancia los parámetros de la acción del controlador.

Ahora supongamos que nuestra querystring tiene algún parámetro encriptado. Para este post he supuesto que algunos parámetros de la querystring pueden ir encriptados, mientras que otros no (no se encripta toda la querystring, aunque la técnica seria la misma). Para el ejemplo supongo que los parámetros encriptados tienen un nombre que empieza por un subrayado (y no, el nombre en si mismo no se encripta).

Así, p.ej. voy a tener una querystring del tipo:

/accion/controlador?_foo=aIGf0UYsNARiBaGZr3blRg%3D%3D

Donde el valor del parámetro _foo está encriptado.

Ahora bien, a mi me gustaría evitar tener que colocar el código para desencriptar dichos valores en cada acción:

public ActionResult Test(string _foo)
{
    // Codigo a evitar:
    var realfoo = Crypto.DecryptValue(_foo);
    // ...
}

Aquí es donde entran los value providers. ASP.NET MVC trae de serie varios value providers que “inspeccionan” una petición http y miran los sitios más habituales donde se pueden encontrar datos:

image

Los value providers se registran en el sistema a través de una factoría, y esta captura muestra las factorías de value providers registradas por defecto en ASP.NET MVC4 RC. Cada factoría devuelve un tipo de value provider, es decir un value provider que mira en una parte de la petición http y por el nombre se puede más o menos deducir:

  1. ChildActionValueProviderFactory: Se usa cuando se invocan acciones hijas (a través de Html.Action o de Html.RenderAction). Es más de infraestructura de ASP.NET MVC que otra cosa.
  2. FormValueProviderFactory: Devuelve un value provider encargado de inspeccionar los parámetros de formdata (básicamente formularios enviados via POST).
  3. JsonValueProviderFactory: Introducido en MVC3 devuelve un value provider que inspecciona la petición http, buscando datos en el cuerpo de ésta que estén en formato json. Es el responsable de que en MVC3 puedas hacer un POST enviando datos en formato json y que el model binder los entienda.
  4. RouteDataValueProviderFactory: Devuelve un value provider que inspecciona los route values, o sea los parámetros establecidos a través de la tabla de rutas.
  5. QueryStringValueProviderFactory: Devuelve un value provider que inspecciona la querystring.
  6. HttpFileCollectionValueProviderFactory: Devuelve un value provider que inspecciona los datos de los ficheros subidos (usando <input type=”file”>).

Por supueso nosotros podemos crearnos nuestras propias factorías de value providers, y eso es justamente lo que haremos. En nuestro caso vamos a hacer un value provider que:

  1. Inspeccione la query string
  2. Seleccione aquellos parámetros que empiezan por un subrayado.
  3. Desencripte cada uno de esos parámetros y guarde en “este sitio común” dicho parámetro, pero con el valor desencriptado. Además el nombre del parámetro en el “sitio común” lo modificaremos quitándole el subrayado.

Veamos primero el código de la factoría:

public class QSEncriptedValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        return new QSEncriptedValueProvider(controllerContext);
    }
}

Simple no? 🙂 Nos limitamos a devolver una instancia de nuestro value provider propio. Veamos su código:

 

public class QSEncriptedValueProvider : DictionaryValueProvider<string>
{
    public QSEncriptedValueProvider(ControllerContext controllerContext) : base(GetQSDictionary(controllerContext), Thread.CurrentThread.CurrentCulture)
    {
    }

    private static IDictionary<string, string> GetQSDictionary(ControllerContext controllerContext)
    {
        var dict = new Dictionary<string, string>();
        var req = controllerContext.HttpContext.Request;
        foreach (var key in req.QueryString.AllKeys.Where(x => x.First() == '_'))
        {
            var value = req.QueryString[key];
            dict.Add(key.Substring(1),Crypto.DecryptValue(value));
        }

        return dict;
    }

}

Derivamos de la clase base DictionaryValueProvider<string>. Esto lo que significa es que este value providers, tan solo dejará strings en “este sitio común”. Bueno, esto es lógico ya que en la querystring tan solo hay strings. Pero p.ej. otros value providers podrían dejar objetos enteros en este “sitio común” (como el que inspecciona la petición buscando datos en json).

Al derivar de esta clase obtenemos la facilidad de trabajar con un diccionario de cadenas (en nuestro caso de <string,string> ya que las claves siempre son cadenas). Básicamente, lo que dejemos en este diccionario es lo que se colocará en este “sitio común”. Y eso es lo que hace el método GetQSDictionary.

Mirando el código vemos que selecciona todas las claves de la querystring que empiecen por un subrayado, y por cada uno de ellas:

  • Desencripta su contenido
  • Guarda el valor desencriptado con la misma clave, pero quitando el subrayado del principio.

¡Ya estamos listos! Ahora tan solo debemos registrar la factoría de value providers. Para ello en el Application_Start podemos colocar la línea:

ValueProviderFactories.Factories.Add(new QSEncriptedValueProviderFactory());

¡Ya podemos probarlo!

Para ello me creo una vista con un link que tenga un parámetro encriptado:

@Html.ActionLink("Test", "Test", 
new {_foo=MvcApplication4.Crypto.EncryptValue("bar value")}, null)

El código fuente generado es el siguiente:

<a href="/Home/Test?_foo=aIGf0UYsNARiBaGZr3blRg%3D%3D">Test</a>

Podemos ver como el valor “bar value” ha sido encriptado. Y ahora viene lo bueno: en nuestro controlador declaramos la acción Test que recibe una cadena, y eso es lo que obtenemos:

image

En el controlador obtenemos ya el valor desencriptado! Fijaos además como el parámetro de la acción se llama foo (en lugar de _foo). Eso es porque en el value provider hemos eliminado el subrayado inicial. Por supuesto dado que también existe el value provider que procesa la querystring de forma “normal”, si en la acción declaramos un parámetro _foo obtendremos el valor encriptado.

Esta es una breve demostración del poder de los value providers. Por supuesto, deberíamos crearnos un conjunto de helpers para generar URLs encriptadas de forma más fácil, pero eso ya daría para otro post.

Un saludo!

PD: No he puesto el código de encriptar/desencriptar adrede porque hay muchas maneras de hacerlo. Yo en mi caso he usado uno que he encontrado en http://www.joshrharrison.com/archive/2009/01/28/c-encryption.aspx (aunque ello no es relevante para lo que cuenta este post).

Microsoft MVP 2012

¡Muy buenas!

Hoy al mirar el correo me he llevado una más que grata sorpresa: he sido reconocido como MVP en el área de ASP.NET / IIS.

Es para mi un honor pertenecer a este grupo, y además en la categoría de ASP.NET junto con monstruos de la talla de José M. Aguilar o Marc Rubiño sin ir más lejos!

Y ya que estoy en mi tarima particular, me gustaría aprovechar para dar las gracias a todos los que han hecho que entrase en eso que se conoce como “la comunidad”. Soys muchos, pero quiero dedicar unas “gracias especiales” al gran Bandolero, cuando nos presentaron un buen día, hace ya algunos añitos, allí en Igualada y me lió con todo lo de CatDotNet. ¡José Miguel, eres todo un grande!

Y no quiero poner ningún nombre más, aunque me vienen varios, muchos, a la cabeza (de catalunya, de españa entera, de andorra incluso), pero me dejaría alguno y no sería justo. A todos ellos, muchas gracias. Y muchas gracias también a todos aquellos que solo conozco virtualmente, a través de twitter o facebook. Siempre queda la esperanza (más o menos lejana según el caso) de conocernos en persona algún día!

Y por supuesto: ¡felicidades a todos los MVP de hoy, nuevos o renovados!

Un abrazo a todos!

ASP.NET MVC: Edición de colecciones usando Ajax

Buenas! El otro día me enviaron la siguiente duda:

Imaginate esto:

class Direccion{

string Calle {get;set;}

int Piso {get;set;}

}

class Cliente {

string Nombre {get;set;}

List<Direccion> Direcciones {get;set;}

}

Imagina que tiene mas propiedades cada clase pero para el ejemplo sirve.

Entonces tengo una Vista para definir la información de Cliente:

Donde van a aparecer los campos para rellenar el cliente. Dentro voy a tener un boton que va a ir agregandome vistas  parciales con la info de Direcciones.

Actualmente la parte de ediccion de Direcciones estaría en una Vista Parcial (con su correspondiente Form).

El problema que tengo es que necesito Enviar todo en el submit de Cliente y realmente es como si no tuviese la información de las direcciones.

Vale, de lo que se trata es que des de la vista para crear un Cliente, podamos añadir objetos Direccion, tantos como sea necesario, usando una vista parcial que iremos cargando via Ajax. El problema era que no se recibía la información de las direcciones.

Hace algún tiempo hablé de las peculiaridades del binding de colecciones en ASP.NET MVC (parte 1, parte 2 y parte 3). Si os miráis la primera parte de la serie, allí comentábamos que nombre debían tener los campos que se enlazaban a una colección. Básicamente si queremos enlazar una colección que está en la propiedad Direcciones de nuestro viewmodel, los campos deben llamarse Direcciones[0], Direcciones[1] y así sucesivamente.

Sabiendo esto, queda claro lo que hay que hacer:

  1. Una vista para crear un Cliente. Dicha vista contendrá un <div> en el cual iremos cargando las vistas parciales para crear direcciones
  2. Una vista parcial para crear un objeto direccion

Tan solo debemos tener cuidado de incrustar las vistas parciales dentro del <form> de la vista principal y de seguir la convención de nombres del ModelBinder. Asi pues, empecemos por la vista para crear un Cliente.

Partimos del código que nos genera VS al crear una nueva vista para crear objetos de tipo Cliente:

@model MvcAjaxCol.Models.Cliente

@{
    ViewBag.Title = "Nuevo cliente";
}
<h2>Nuevo Cliente</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Cliente</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Nombre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Nombre)
            @Html.ValidationMessageFor(model => model.Nombre)
        </div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

El propio VS no nos genera código para la propiedad “Direcciones” de la clase Cliente porque es una colección. Bueno, el siguiente paso es crear un <div> vacío donde colocaremos via Ajax las vistas parciales para crear objetos del tipo dirección. Por supuesto dicho <div> debe estar dentro del <form>. Luego añadimos un botón y cargamos una vista parcial nueva cada vez que se pulse el botón:

@model MvcAjaxCol.Models.Cliente

@{
    ViewBag.Title = "Nuevo cliente";
}

<script type="text/javascript">
    var idx = 0;
    $(document).ready(function() {
        $("#cmdNewDir").click(function() {
            var uri = "@Html.Raw(Url.Action("NewDir", "Home", new {prefix="Direcciones", idx="xxx" }))";
            uri = uri.replace("xxx", idx);
            idx++;
            $.get(uri, function(data) {
                $("#direcciones").append(data);
            });
        });
    });

</script>
<h2>Nuevo Cliente</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Cliente</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Nombre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Nombre)
            @Html.ValidationMessageFor(model => model.Nombre)
        </div>
        <hr />
        <input type="button" value="Nueva Direccion" id="cmdNewDir"/>
        <div id="direcciones"></div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

Como se puede a cada click del botón llamamos a una acción del controlador llamada NewDir. Esta acción es la que devuelve la vista parcial que añadimos al div:

public ActionResult NewDir(string prefix, string idx)
{
   ViewData["prefix"] = prefix;
   ViewData["idx"] = idx;
   return PartialView("Direccion");
}

Y finalmente la vista Direccion, usa los parámetros prefix y idx para ir generando campos cuyo nombre sea prefix[idx].XXX:

@model MvcAjaxCol.Models.Direccion
<div class="editor-label">
    @Html.LabelFor(model => model.Calle)
</div>
<div class="editor-field">
    @Html.Editor(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Calle"))
    @Html.ValidationMessage(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Calle"))
 
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.Piso)
</div>
<div class="editor-field">
    @Html.Editor(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Piso"))
    @Html.ValidationMessage(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Piso"))
</div>

¡Y listos! Con esto ya podemos ir añadiendo tantas direcciones como sea necesario a nuesro cliente!

¡Un saludo!