Módulos en JavaScript… AMD, CommonJS

Con las aplicaciones web cada vez con mayor carga de cliente, y el uso cada vez mayor de sistemas de build en cliente como grunt o gulp, usar módulos para desarrollar en JavaScript es cada vez más habitual. Si todavía te descargas las librerías de sus páginas web y las incluyes una a una en tags <script/> es probable que este post te interese.

¿Qué es un módulo?

Llamamos módulo JavaScript a un código que de alguna manera es “auto contenido” y que expone una interfaz pública para ser usada. Esto no es realmente nuevo, el patrón de módulo ya hace bastantes años que se utiliza y no requiere más que algunos conocimientos de JavaScript para aplicarlo. El problema con los módulos en JavaScript no ha sido nunca el crearlos si no el de cargarlos. Puede parecer simple… de hecho, ¿no se trata solo de poner un tag <script />? Pues la realidad es que no, porque cargar un módulo implica que antes deben estar cargadas sus dependencias y por lo tanto debemos tener un mecanismo para definir esas dependencias y otro mecaniso para cargarlas al tiempo que cargamos el módulo deseado.

Es ahí donde entran en juego los distintos estándares de módulos que tenemos. Nos permiten crear módulos JavaScript, declarar las dependencias (es decir indicar de qué módulos depende nuestro módulo e incorporar la funcionalidad del módulo del cual dependemos) y cargar determinados módulos. Hay dos estándares usados hoy en día: CommonJS y AMD.

CommonJS

CommonJS es un sistema de módulos síncrono: es decir la carga de módulos es un proceso síncrono que empieza por un módulo inicial. Al cargarse este módulo se cargaran todas sus dependencias (y las dependencias de las dependencias, y las dependencias de las dependencias de las dependencias… y así hasta cualquier nivel de profundidad). Una vez finalicen todas esas cargas, el módulo inicial está cargado y empieza a ejecutarse. Definir un módulo en formato CommonJS es muy sencillo:

  1. var Complex = function (r, i) {
  2.     this.r = r instanceof Complex ? r.r : r;
  3.     this.i = r instanceof Complex ? r.i : (i || 0);
  4. }
  5. module.exports = Complex;

Supón que este código está en un fichero Complex.js. Este código define un módulo que exporta una función (constructora) llamada Complex. Observa el uso de module.exports para indicar que es lo que exporta el módulo. Todo lo que no pertenezca al exports son variables (y funciones) privadas del módulo. Ahora podríamos declarar otro módulo que dependiese de este módulo:

  1. var Complex = require('./complex');
  2. addComplex = function (ca, cb) {
  3.     return new Complex(ca.r + cb.r, ca.i + cb.i);
  4. }
  5. var math = {
  6.     add: function (a, b) {
  7.         if (a instanceof Complex || b instanceof Complex) {
  8.             return addComplex(new Complex(a), new Complex(b));
  9.         }
  10.         return a + b;
  11.     }
  12. }
  13. module.exports = math;

Este módulo (math.js) requiere el módulo complex.js (de ahí el uso de require), define un objeto math con un método y exporta dicho objeto. La función addComplex es privada al módulo.

Finalmente podemos crear un tercer módulo (main.js) que use esos módulos para sumar tanto números reales como complejos. Este va a ser nuestro módulo inicial:

  1. var Complex = require('./complex');
  2. var math = require('./math');
  3.  
  4. console.log(math.add(40, 2));
  5. var c1 = new Complex(40, 3);
  6. console.log(math.add(c1, 2));

Si ejecutamos el siguiente código mediante nodejs vemos como todo funciona correctamente:

image

Nodejs soporta módulos CommonJS de forma nativa, pero… ¿qué pasa con el navegador? Pues que necesitamos soporte de alguna herramienta externa. Una de las más conocidas es browserify que se instala como un paquete de node. Browserify es, a la vez, una herramienta de línea de comandos y un módulo CommonJS que podemos integrar con grunt o gulp. Si usamos la herrramienta de línea de comandos, se puede usar el comando browserify main.js > bundle.js para crear un fichero (bundle.js) que contenga el código de main.js y de todos sus módulos requeridos. Este fichero es el que usaría con un tag script.

Lo bueno de browserify es que solo debo indicarle el fichero inicial (en mi caso main.js). Él se encarga de ver los módulos necesarios y empaquetarlos todos juntos en un fichero. Usar browserify como herramienta de línea de comandos es posible (para ello basta con que lo tengas instalado como módulo global, es decir npm install –g browserify), pero no es lo más cómodo: lo suyo es tenerlo integrado dentro de nuestro script de build que tengamos con gulp o grunt. Por suerte browserify es también un módulo CommonJS por lo que podemos usarlo dentro de nuestro script de build. P. ej. el siguiente código muestra como usarlo mediante gulp:

  1. var gulp = require('gulp');
  2. var browserify = require('browserify');
  3. var source = require('vinyl-source-stream');
  4.  
  5. gulp.task('browserify', function () {
  6.     browserify('./main.js')
  7.         .bundle()
  8.         .pipe(source('bundle.js'))
  9.         .pipe(gulp.dest('./scripts'));
  10. });

Con esto basta con que ejecutes “gulp broswerify” para que browserify te deje en scripts un fichero bundle.js con el resultado de browserify. El único requisito es tener instalado (además de gulp y broswerify, obviamente) un módulo llamado vinyl-source-stream que se usa para pasar de streams basados en texto (los que usa browserify)  a streams de gulp.

Muchas de las librerías JavaScript (incluyendo incluso jQuery) tienen versión CommonJS lo que ayuda mucho a organizar tu código. Por supuesto se puede configurar browserify para que genere source maps o que aplique más transformaciones al código (p. ej. convertir código JSX de React en código JavaScript).

AMD

AMD es otra especificación de módulos JavaScript, cuya principal diferencia con CommonJS es que es asíncrona (AMD significa Asynchronous Module Definition). La implementación más conocida para navegadores de AMD es requirejs. Al ser asíncrona permite escenarios con carga de módulos bajo demanda (es decir cargar un módulo sólo si se va a usar), lo que puede ser interesante en según que aplicaciones.

Si usas requirejs no necesitas nada más: no es necesario que uses ninguna herramienta de línea de comandos o que crees tareas de grunt o gulp. Dado que requirejs implementa AMD va a ir cargando los módulos JavaScript de forma asíncrona. No tienes por qué crear un bundle con todos ellos.

La sintaxis para definir un módulo AMD es un poco más “liosa” que la sintaxis de CommonJS, pero tampoco mucho más. Empecemos por ver como sería el módulo AMD para definir el tipo Complex:

  1. define([], function () {
  2.     console.log('complex loaded…');
  3.     var Complex = function (r, i) {
  4.         this.r = r instanceof Complex ? r.r : r;
  5.         this.i = r instanceof Complex ? r.i : (i || 0);
  6.     }
  7.  
  8.     return Complex;
  9. });

Los módulos AMD empiezan con una llamada a define, que acepta básicamente dos parámetros: un array con las dependencias del módulo (el equivalente al require de CommonJS) y luego una función con el código del  módulo. Esa función devuelve lo que el módulo exporta (es decir, el return de la función equivale al module.exports de CommonJS). El módulo que define Complex no depende de nadie, así que el array está vacío. No ocurre lo mismo con el modulo math (fichero math_amd.js):

  1. define(['complex_amd'], function (Complex) {
  2.     addComplex = function (ca, cb) {
  3.         return new Complex(ca.r + cb.r, ca.i + cb.i);
  4.     }
  5.     var math = {
  6.         add: function (a, b) {
  7.             if (a instanceof Complex || b instanceof Complex) {
  8.                 return addComplex(new Complex(a), new Complex(b));
  9.             }
  10.             return a + b;
  11.         }
  12.     }
  13.     return math;
  14. });

Observa ahora como el módulo depende del módulo complex_amd. Eso significa que al cargarse este módulo, el módulo complex_amd (fichero complex_amd.js) debe estar cargado. Si no lo está requirejs lo cargará asincronamente, y cuando esta carga haya finalizado invocará la función que define el módulo. Observa ahora que la función tiene un parámetro. Este parámetro se corresponde con lo que exporta (devuelve) el módulo complex_amd del cual dependíamos. Básicamente, por cada elemento (dependencia) del array tendremos un parámetro en la función. Eso se ve todavía más claro en el modulo main_amd.js quie depende tanto de complex_amd como de math_amd:

  1. define(['complex_amd', 'math_amd'], function (Complex, math) {
  2.     console.log(math.add(40, 2));
  3.     var c1 = new Complex(40, 3);
  4.     console.log(math.add(c1, 2));
  5. });

Observa como hay dos parámetros en el array de dependencias y por lo tanto la función del módulo recibe dos parámetros. El array indica las dependencias y el parámetro de la función permite acceder a ellas.

Finalmente tan solo nos queda tener un html que cargue primero requirejs y una vez haya terminado, indique a requirejs que cargue el modulo main_amd.js y lo ejecute. Al cargar este módulo, requirejs cargará asíncronamente todas las dependencias. El código del fichero HTML es trivial:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title></title>
  5. </head>
  6. <script data-main="main_amd" src="bower_components/requirejs/require.js"></script>
  7. <body>
  8. </body>
  9. </html>

El escenario “cargar requirejs y una vez haya terminado empieza a cargar el módulo indicado” es tan común que requirejs lo soporta a través del atributo data-main del tag script. Podemos ver en la pestaña network del navegador como realmente se cargan los tres módulos por separado:

image

¿Cuál usar?
La verdad es que AMD se acerca mucho más a la filosofía de la web que CommonJS. La carga asíncrona y on-demand es mucho más natural en la web que la carga síncrona que tiene CommonJS. Lo que ocurre es que actualmente solemos siempre crear un bundle de todos nuestros JavaScript, porque sabemos que es más rápido descargarse un solo fichero de 100Ks que 10 ficheros de 10Ks cada uno. Seguro que todos habéis oído que una de las normas básicas de optimizar una página web consiste en minimizar la descarga de ficheros. Los bundles de JavaScript, de CSS, los sprite-sheets y el uso de data-uris van todos por ese camino: cargar un fichero más grande antes que varios de pequeños. Si seguimos esa tónica perdemos la característica de carga on-demand y asíncrona de AMD (porque antes de ejecutar la aplicación hemos tenido que generar ese bundle). La verdad es que cargar los scripts on-demand no es algo que requieran la mayoría de aplicaciones (un escenario sería casos en que una aplicación quiere cargar scripts distintos en función de ciertos datos de ejecución, p. ej. scripts distintos por usuario).

Así parece que, actualmente, no haya una diferencia sustancial entre usar CommonJS y AMD si al final terminamos en un bundle. La cuestión puede reducirse a gustos personales o cantidad de módulos existentes en cada formato (a pesar de que es posible, con poco trabajo, usar módulos CommonJS bajo AMD) pero HTTP2 puede cambiar eso. HTTP2 convierte los bundles en no necesarios, ya que mejora el soporte para varias conexiones… bajo ese nueva prisma AMD parece ser una mejor opción que CommonJS. Pero HTTP2 es todavía muy nuevo, así que hasta todos los navegadores y servidores web lo soporten va a pasar algún tiempo… y cuando esté establecido, quizá y solo quizá, la duda de si usar CommonJS o AMD dejará de tenir sentido porque los módulos nativos de ES6 habrán tomado el relevo.

Saludos!

ASP.NET 5: Configuración

Una de las novedades de ASP.NET5 es su sistema de configuración. En versiones anteriores el sistema de configuración estaba muy atado al fichero web.config. En este fichero se guardaba tanto la configuración propia del programa (cadenas de conexión, appsettings o información adicional que suele estar en secciones de configuración propias) como información de configuración del propio runtime: tipo de seguridad, módulos a cargar, bindings de assemblies y un sinfin más de configuraciones.

En ASP.NET5 eso se ha simplificado mucho. Un tema importante es que ahora está claramente separada la configuración del framework que se realiza mayoritamente por código, la configuración del runtime que se delega en el fichero project.json y la configuración propia del programa que está en cualquier otro sitio. En este post nos centraremos solamente en la configuración propia del programa.

Configuración propia del programa (configuración de negocio)

La configuración propia toma la forma básica de un diccionario con claves que son cadenas y valores que son… cadenas. No tenemos una interfaz propia para cadenas de conexión o para las secciones propias de configuración. Para entendernos, sería como si todo fuesen appsettings. Es un mecanismo sencillo (si necesitas algo por encima de eso es fácil construirlo). El sistema por si mismo no nos ata a ningún formato de fichero (esa configuración puede estar en un json, un xml o lo que sea).

Aunque, es cierto, que el sistema de por si no nos ata a ningún esquema en concreto de nuestros datos, si queremos utilizar los métodos que son capaces de leer información automáticamente de un fichero, entonces si que debemos adaptarnos al esquema previsto, aunque es bastante ligero.

P. ej. esos tres ficheros contienen todos ellos la misma configuración:

  1.  
  2.   "smtp": {
  3.     "server": {
  4.       "user": "eiximenis",
  5.       "mail": "mail@domain.com"
  6.     },
  7.     "auth": {
  8.       "anonymous": "true"
  9.     }
  10.   }
  11. }

  1. <smtp>
  2.     <server>
  3.         <user>eiximenis</user>
  4.         <mail>mail@domain.com</mail>
  5.     </server>
  6.     <auth>
  7.         <anonymous>true</anonymous>
  8.     </auth>
  9. </smtp>

  1. [smtp:server]
  2. user=true
  3. mail=mail@domain.com
  4. [smtp:auth]
  5. anonymous=true

Los 3 ficheros definen las siguientes claves:

  • smtp:server:user –> Con valor “eiximenis”
  • stmp:server:mail –> Con valor “mail@domain.com
  • smtp:auth:anonymous –> Con valor “true”

En versiones anteriores esos 3 valores hubiesen ido, seguramente, en la sección<appSettings /> del web.config. La verdad es que usar el separador dos puntos (:) en las claves de appsettings es algo que ya era como un “estándard de facto”.

Para cargar esos ficheros usamos los métodos AddJsonFile(), AddXmlFile() o AddIniFile() del objeto ConfigurationBuilder, que se suele crear en el constructor de la clase Startup:

  1. public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
  2. {
  3.     // Setup configuration sources.
  4.     var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
  5.         //.AddJsonFile("config.json")
  6.         //.AddXmlFile("config.xml")
  7.         .AddIniFile("config.ini")
  8.         .AddEnvironmentVariables();
  9.     Configuration = builder.Build();
  10. }
  11.  
  12. public IConfiguration Configuration { get; set; }

El objeto Configuration básicamente expone un indexer para acceder a los valores de la configuración:

  1. var usermail = Configuration["smtp:server:user"];

Si quiero acceder a un valor de la configuración desde un controlador MVC puedo incorporar el objeto Configuration dentro del sistema de inyección de dependencias de asp.net 5:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3.     services.AddInstance<IConfiguration>(Configuration);
  4. }

Y ahora puedo añadir un parámetro IConfiguration a cada controlador que lo requiera.

El método GetConfigurationSection me devuelve otro IConfiguration pero cuya raíz es la clave que yo indique. Es decir dado el objeto Configuration que tenía si hago:

  1. var usermail = Configuration["smtp:server:user"];
  2. var smtpcfg = Configuration.GetConfigurationSection("smtp");
  3. var usermail2 = smtpcfg["server:user"];
  4. var servercfg = Configuration.GetConfigurationSection("smtp:server");
  5. var usermail3 = servercfg["user"];

Los valores de usermail, usermail2 y usermail3 son el mismo.

Configuración tipada

Este esquema de configuración es funcional y sencillo, pero a veces queremos usar objetos POCO para guardar nuestra propia configuración. Supongamos que tenemos la siguiente clase para guardar los datos del servidor:

  1. public class ServerConfig
  2. {
  3.     public string User { get; set; }
  4.     public string Mail { get; set; }
  5. }

Podemos mapear los datos que están en “smtp:server” con el siguiente código:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3.     services.Configure<ServerConfig>(Configuration.GetConfigurationSection("smtp:server"));
  4. }

Recuerda que GetConfigurationSection(“stmp:server”) me devuelve un IConfiguration que apunta directamente a esta clave y que por lo tanto tiene las dos claves “user” y “mail” que se corresponen con los nombres de las propiedades de la clase ServerConfig. Esta línea además incluye dentro del sistema de inyección de dependencias la clase ServerConfig así que ahora puedo inyectarla a cualquier controlador MVC que lo requiera:

  1. public HomeController(IOptions<ServerConfig> sc)
  2. {
  3.     var cfg = sc.Options;
  4.     var user = cfg.User;
  5. }

Observa, eso sí, que el parámetro no es un ServerConfig, si no un IOptions<ServerConfig> (eso es porque services.Configure incluye la interfaz IOptions<T> en el sistema de inyección de dependencias). Para acceder al objeto con la configuración usamos la propiedad Options.

Configuración anidada

Por supuesto el sistema soporta configuración anidada. Es decir, lo siguiente es correcto:

  1. public class SmtpConfig
  2.  {
  3.      public ServerConfig Server { get; set; }
  4.      public AuthConfig Auth { get; set; }
  5.  }
  6.  
  7.  public class ServerConfig
  8.  {
  9.      public string User { get; set; }
  10.      public string Mail { get; set; }
  11.  }
  12.  
  13.  public class AuthConfig
  14.  {
  15.      public string Anonymous { get; set; }
  16.  }

Y registrarlas de la siguiente manera:

  1. services.Configure<SmtpConfig>(Configuration.GetConfigurationSection("smtp"));

Y ahora podemos inyectar un  IOptions<SmtpConfig> en cualquier controlador que lo requiera:

  1. public HomeController(IOptions<SmtpConfig> sc)
  2. {
  3.     var cfg = sc.Options;
  4.     var user = cfg.Server.User;
  5. }

¡Sencillo y rápido!

Y hasta aquí este post sobre la configuración en ASP.NET5… espero que os haya resultado interesante!

Saludos!

Enviar un array (JS) a un controlador MVC5

Buenas!

En los foros de MSDN aparece la pregunta sobre como enviar un array JS a un controlador de MVC. La verdad es que hay varias maneras de hacerlo… veamos dos de ellas, ambas muy sencillas.

En todos los casos el controlador tiene el siguiente método:

  1. public ActionResult Index(long[] data)
  2. {
  3.     return View();
  4. }

Opción 1 – Mandar el array como un JSON

Esta es la aproximación que se intenta en el post. Basta el siguiente código en el cliente:

  1. var uri = '@Url.Action("Index", "Home")';
  2. var arr = [4,8,15,16,23,42];
  3. $.ajax({
  4.     url: uri,
  5.     data: JSON.stringify(arr),
  6.     type: 'POST',
  7.     contentType: 'application/json'
  8. });

Y el array se recibe sin problema alguno en el controlador. En el caso que el controlador declarase el parámetro como IEnumerable<long> funcionaría igual.

Opción 2 – Mandar el array como x-www-form-urlencoded

Esto, con jQuery es un poco más complejo. El siguiente código:

  1. var arr = [4,8,15,16,23,42];
  2. $.ajax({
  3.     url: uri,
  4.     data: arr,
  5.     type: 'POST',
  6. });

Genera una petición incorrecta, debido como jQuery serializa los parámetros. Lo que jQuery manda en el cuerpo de la petición es:

undefined=&undefined=&undefined=&undefined=&undefined=&undefined=

Si en la llamada a $.ajax colocamos el parámetro processData a false, entonces jQuery nos genera lo siguiente en el cuerpo de la petición:

4,8,15,16,23,42

Aunque ahora si que están los datos, MVC5 no es capaz de procesar los datos en este formato, y recibiremos null en el parámetro.

La solución es bastante sencilla: debemos convertir el array en un objeto, cuya propiedad sea el array:

  1. var arr = [4,8,15,16,23,42];
  2. $.ajax({
  3.     url: uri,
  4.     data: { data: arr },
  5.     type: 'POST'
  6. });

Ahora bien, es muy importante que el nombre de la propiedad (data) sea el mismo que el nombre del parámetro en el controlador, ya que el model binder de MVC nos enlazará por nombre.

En este caso lo que se envia en el cuerpo de la petición que se genera es lo siguiente:

data%5B%5D=4&data%5B%5D=8&data%5B%5D=15&data%5B%5D=16&data%5B%5D=23&data%5B%5D=42

Parece un poco críptico, pero ten presente que %5B es la codificación del carácter [ y %5D se corresponde al carácter ]. Así que realmente lo que se envía es:

data[]=4&data[]=8&data[]=15&data[]=16&data[]=23&data[]=42

El model binder de MVC es capaz de entender esto sin ningún problema. Al igual que antes sigue funcionando si el controlador recibe un IEnumerable<long> en lugar de un long[] (eso sí, que se llame “data” igual!).

Saludos!

Mi (pequeño) homenaje al gran, genial e irrepetible Terry Pratchett

Terry Pratchett ha sido uno de los grandes escritores de novelas de fantasía. Su serie más conocida Mundodisco, cuenta con 41 libros escritos en un estilo desenfadado y humorístico que parodian el género fantástico pero que a la vez encierran durísimas y mordaces críticas contra muchos aspectos de nuestra sociedad. El humor de Pratchett es reconocido como uno de los más ácidos e inteligentes a la vez que absurdos y esta mezcla es explosiva: sus libros te hacen estallar en carcajadas a la vez que reflexionar. Para mi ha sido uno de los escritores que más me ha impactado.

Son muy conocidas sus frases, sacadas tanto de sus libros como de sus charlas y hay webs que se dedican a recopilarlas. Algunas hacen gracia por si mismas, otras hacen mucha más gracia dentro del contexto del libro. En inglés, la página “L-Space Web” contiene probablemente la mayor recopilación de frases de Pratchett. En castellano es “la concha de gran A’Tuin” quien contiene otra buena colección.

Ahora que Terry Pratchett ha muerto he pensado que es el momento de hacerle un pequeño (pequeñísimo) homenaje, similar a otros que se están haciendo al estilo de “GNU Terry Pratchett”.

En mi caso he dedicido crear un middlewarwe OWIN que añade una cabecera HTTP con una cita de Terry Pratchett al azar. Las citas están extraídas de la lista contenida en L-Space Web, aunque el middleware es configurable para que puedas usar tus propias citas si lo prefieres.

El código principal del middleware es muy sencillo:

  1. public class PratchettOwinMiddleware
  2. {
  3.     private readonly AppFunc _next;
  4.     private readonly PratchettQuotesFactory _quotesFactory;
  5.     private readonly Random _random;
  6.     public PratchettOwinMiddleware(AppFunc next, PratchettQuotesFactory quotesFactory)
  7.     {
  8.         _next = next;
  9.         _quotesFactory = quotesFactory;
  10.         _random = new Random();
  11.     }
  12.  
  13.     public async Task Invoke(IDictionary<string, object> environment)
  14.     {
  15.         await _next.Invoke(environment);
  16.         var headers = environment["owin.ResponseHeaders"] as IDictionary<string, string[]>;
  17.         var quotes = _quotesFactory.GetQuotes();
  18.         var quote = quotes[_random.Next(0, quotes.Length)];
  19.         headers.Add("X-Pratchett-Quote", new string[] { quote});
  20.     }
  21. }

Básicamente se dedica a añadir la cabecera X-Pratchett-Quote a cualquier petición que sea procesada. Para registrarlo en el pipeline de OWIN se usa un método de extensión sobre IAppBuilder definido en la clase PratchettAppBuilderExtensions:

  1.   public static class PratchettAppBuilderExtensions
  2.   {
  3.       public static void UseTerryPratchett(this IAppBuilder app)
  4.       {
  5.           app.Use(typeof(PratchettOwinMiddleware),
  6.               new PratchettQuotesFactory(
  7.                   new InternalFileQuoteParser(),
  8.                     () =>Assembly.GetExecutingAssembly().GetManifestResourceStream("PratchettQuotes.terry_quotes.txt")));
  9.       }
  10.  
  11.       public static void UseTerryPratchett(this IAppBuilder app, IQuoteParser quoteParser, string filename)
  12.       {
  13.           app.Use(typeof(PratchettOwinMiddleware),
  14.               new PratchettQuotesFactory(quoteParser, () => new FileStream(filename, FileMode.Open, FileAccess.Read)));
  15.       }
  16.  
  17.       public static void UseTerryPratchett(this IAppBuilder app, IQuoteParser quoteParser, Func<Stream> quotesProvider)
  18.       {
  19.           app.Use(typeof(PratchettOwinMiddleware), new PratchettQuotesFactory(quoteParser, quotesProvider));
  20.       }
  21.   }

El método está sobrecargado para admitir tus propias citas. En este caso debes pasar también una clase que implemente la interfaz IQuoteParser que indique como leer los datos del stream que contiene las citas. No se incluye ninguna implementación de dicha interfaz (bueno, se incluye una pero es interna ya que se usa para parsear los datos del fichero de citas, que está sacado literalmente de L-Space Web).

Si llamas simplemente a UseTerryPratchett() se usará el fichero de citas que está incrustado dentro del ensamblado.

El resultado lo puedes ver en esta captura de la pestaña red de Chrome:

image

El código fuente del proyecto lo teneis en github: https://github.com/eiximenis/PratchettQuotes

Saludos!:D

WebApi: Subir un fichero y datos adicionales

El modelo de binding que tiene WebApi es bastante más simple que el de MVC y por ello algunas tareas que en MVC eran más simples en WebApi pasan a ser un poco más complejas. Una de esas tareas es la subida de ficheros y de datos adicionales.

A diferencia de MVC donde el contenido del cuerpo de la petición puede ser inspeccionado numerosas veces (según los value providers que tengamos configurados) en WebApi el cuerpo de la petición es un stream que puede ser leído una sola vez. A nivel práctico eso implica que de todos los parámetros que pueda recibir el controlador tan solo uno puede ser puede ser enlazado con los datos del cuerpo de la petición. El resto deben ser enlazados desde otros sitios (usualmente la URL).

Así imagina que tienes el siguiente formulario que quieres enviar a un controlador:

  1. <form method=«post» action=«@Url.Action(«Index», «Upload»)« enctype=«multipart/form-data»>
  2.     <p>
  3.         Name: <input type=«text» name=«Beer.Name» /> <br />
  4.         Rating: <input type=«number» name=«Beer.Rating» /><br />
  5.         Image: <input type=«file» name=«image» /><br />
  6.         <input type=«submit» value=«send!» />
  7.     </p>
  8. </form>

En MVC te bastará con declarar un parámetro de tipo HttpPostedFileBase para el fichero y otro para el resto de valores (name y rating). El framework se encargará del resto:

image

En WebApi eso no es posible, ya que aquí tenemos dos parámetros (image y beer) enlazados con datos provenientes del cuerpo de la petición. Por lo tanto, en WebApi tanto los datos del fichero subido como el resto de valores deberán venir juntos (asumiendo que están todos en el cuerpo de la petición como es este ejemplo).

Además la subida de ficheros en WebApi no se gestiona enlanzando ningún parámetro del controlador, si no instanciando y usando un objeto del tipo MultipartFileStreamProvider. Esta clase lee el cuerpo de la petición y guarda el fichero en disco. Pero claro, si este objeto lee el cuerpo de la petición ya nadie más puede hacerlo en WebApi, lo que implica que los datos asociados también debe leerlos. Por suerte existe la clase derivada MultiPartFormDataStreamProvider que almacena los datos adicionales en la propiedad FormData. Así podemos tener el siguiente código en un ApiController:

  1. [HttpPost]
  2. [Route(«api/upload»)]
  3. public async Task<IHttpActionResult> Upload()
  4. {
  5.     var folder = HostingEnvironment.MapPath(«~/Uploads»);
  6.     var provider = new MultipartFormDataStreamProvider(folder);
  7.     var data = await Request.Content.ReadAsMultipartAsync(provider);
  8.     return Ok();
  9. }

Y podemos ver como en data tenemos tanto la ubicación del fichero en disco (del servidor) puesto que ya ha sido guardado, así como una propiedad llamada FormData que es un NameValueCollection con los datos adicionales:

image

A partir de aquí, convertir esta NameValueCollection en un objeto tipado, ya depende de tí. P. ej. puedes usar este método de extensión:

  1. static class NameValueCollectionExtensions
  2. {
  3.     public static T AsObject<T>(this NameValueCollection source, string prefix)
  4.         where T : class, new()
  5.     {
  6.         var result = new T();
  7.         string fullPrefix = string.IsNullOrEmpty(prefix) ? prefix : prefix + «.»;
  8.         foreach (var key in source.AllKeys.Where(k => k.StartsWith(fullPrefix)).
  9.             Select(kwp => kwp.Substring(fullPrefix.Length)))
  10.         {
  11.             var prop = typeof(T).GetProperty(key);
  12.             if (prop != null && prop.CanWrite)
  13.             {
  14.                 prop.SetValue(result, Convert.ChangeType(source[fullPrefix + key], prop.PropertyType));
  15.             }
  16.         }
  17.  
  18.  
  19.         return result;
  20.     }

Este método es muy limitado (no admite propiedades complejas) pero si los datos que envías junto a los ficheros son simples, te puede servir. En este caso se usaría de la siguiente manera:

  1. var beer = data.FormData.AsObject<Beer>(«Beer»);

Y por supuesto, si necesitas soluciones más complejas que esas evalúa soluciones como Slapper.AutoMapper o similares que ya te lo dan todo hecho… no vayas por ahí reinventando la rueda (o si, vamos… eso ya es cosa tuya :P)!

Saludos!

De NuGet y la gestión de paquetes

Ya hace bastante tiempo que NuGet salió y desde entonces se ha convertido en un compañero inseparable de todos nosotros. Y más que va a serlo cuando vNext salga de forma definitiva. En este post doy por supuesto que conoces NuGet y que lo has usado alguna vez (si no… ¡debes aprender a usarlo ya!). En este post quiero comentar los tres modos de funcionamiento que tiene NuGet y algunas cosillas más con las que me he encontrado.

Funcione NuGet en el modo en que funcione, cuando agregamos un paquete siempre ocurre lo mismo:

  • Se crea (si no existe) un directorio packages a nivel de la solución (localizado en el mismo directorio que el .sln).
  • Se descarga el paquete en dicho directorio
  • Se crea (si no existe) un fichero packages.config en el proyecto al cual se haya agregado el paquete y se añade una línea indicando el paquete agregado.
  • Se añade una referencia en el proyecto que apunta al ensamblado del paquete que se encuentra en el directorio packages. Y si el paquete tiene scripts de instalación adicionales, pues se ejecutan.

Recuerda que NuGet es básicamente un automatizador de agregar referencias en VS. Hace todo aquello que harías tu manualmente (descargar el ensamblado, guardarlo en algún sitio, agregar la referencia y cosas extra como editar web.config) y nada más (o nada menos, depende de como se mire).

Vamos a configurar NuGet para que funcione en el primero de los modos. Para ello abre VS2013 y en Tools –> Options –> NuGet Package Manager desmarca las dos checkboxes que están bajo el título de “Package Restore”. De esta manera NuGet funciona de la forma en que funcionaba originalmente. Y dicha forma consiste en que NuGet no hará nada más que lo que hemos descrito hasta ahora. Eso significa que cuando subas a tu repositorio de control de código fuente el proyecto debes incluir el directorio packages que contiene los binarios de los paquetes instalados por NuGet. Si no lo haces, cuando otra persona quiera descargarse el código fuente el proyecto no le compilará porque no encontrará las referencias a los paquetes NuGet:

image

¿Está todo perdido? Pues no, porque NuGet detectará que hay paquetes referenciados (eso lo sabe mirando el packages.config) que no existen en el directorio packages. Así mostrará el siguiente mensaje en la “Package Manager Console”:

image

Este mensaje aparece porque estamos en una versión nueva de NuGet. Cuando apareció NuGet este mensaje no aparecía y honestamente no sé cual era la solución entonces porque ese era un escenario no soportado: en la primera versión de NuGet los paquetes descargados debían subirse al control de código fuente.

Una versión de NuGet posterior habilitó el segundo modo de funcionamiento de NuGet. Para activarlo se debe pulsar sobre la solución en el “solution explorer” y seleccionar la opción “Enable NuGet Package Restore”:

image

Cuando se pulsa esta opción se crea una carpeta .nuget en la raíz de la solución que contiene tres ficheros (NuGet.config, NuGet.exe y NuGet.targets). Y además se modificarán los ficheros de la solución para agregarles por un lado una entrada nueva dentro de <PropertyGroup>:

  1. <RestorePackages>true</RestorePackages>

Y la ejecución de la tarea para que NuGet descargue los paquetes al compilar la solución:

  1. <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
  2. <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
  3.   <PropertyGroup>
  4.     <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
  5.   </PropertyGroup>
  6.   <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
  7. </Target>

De este modo al compilar la solución NuGet se descargará los paquetes que no existan de forma automática. Habilitar esta opción te marcará automáticamente la primera de las checkboxes que habíamos desmarcado antes en Tools –> Options –> NuGet Package Manager.

Así, ahora, una de las preguntas más recurrentes sobre NuGet (¿Tengo que subir los paquetes a mi sistema de control de código fuente?) se respondía ahora diciendo que si los querías subir podías hacerlo sin problemas pero que si no, no era necesario siempre y cuando la solución tuviese habilitada la opción de “Package Restore”. En este último caso la carpeta .nuget si que debías subirla.

En principio con esos dos modos cubrimos la totalidad de los escenarios pero con la versión 2.7 de NuGet agregaron un tercer modo de funcionamiento. Dicho tercer modo es básicamente el modo anterior que acabamos de describir pero automatizado. Ya no tenemos que hacer nada, excepto marcar la segunda de las checkboxes que hemos desmarcado al principio (y es que este es, a partir de la versión 2.7, el modo por defecto de NuGet).

Para verla en acción marca la segunda checkbox, y luego borra la carpeta .nuget. Luego recompila la solución y recibirás un error: “This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is XXX.nugetNuget.targets”. Este error se da porque a pesar de que hemos borrado la carpeta .nuget, tenemos los proyectos todavía configurados para que la usen. Así, que no toca otra: abrir en modo texto los ficheros de proyecto y eliminar las líneas que se nos añadieron antes.

Una vez hecho esto, recargas los proyectos y al recompilar automáticamente NuGet descargará los paquetes. La ventaja de este modo de funcionamiento respecto al anterior es que no es intrusivo: no requiere modificar los ficheros de proyecto.

Si actualmente usas NuGet 2.7 o superior (que es de esperar que sí) y tienes la carpeta .nuget en tu repositorio de código fuente lo mejor que puedes hacer es eliminarla. Y luego modificar los proyectos para quitar las líneas indicadas anteriormente. Y con la segunda checkbox marcada (que es como está por defecto) ya tienes la descarga de paquetes automatizada. Por si tienes alguna duda sobre lo que tienes que eliminar en los ficheros de proyecto (aunque son las líneas mencionadas antes), todo el proceso está documentado en la propia web de NuGet. Si usas team build también son necesarias pequeñas modificaciones en TFS2012 o anterior (en TFS2013 así como Visual Studio Online o bien deploys en Azure web sites el proceso está ya integrado). De nuevo tienes toda la documentación en la web de NuGet sobre como configurar el team build.

De todos modos que tengas habilitada la descarga de paquetes automatizada no te impide colocar los paquetes (la carpeta packages) en el repositorio de control de código fuente: es una opción personal. Colocarlos en el sistema de control de código fuente te evita una dependencia con el propio NuGet (que aunque se cae pocas veces, a veces lo hace). Hace tiempo Juanma escribió en su blog un post sobre los peligros de depender del gestor de paquetes. Hay soluciones más elaboradas como no tener los paquetes en el control de código fuente pero usar un servidor de NuGet corporativo. Aquí ya, cada caso es un mundo.

Proyectos en varias soluciones

Vale, eso es un poco más frustrante y es un aviso más que otra cosa: si tienes un proyecto con paquetes gestionados por NuGet y este proyecto lo tienes en varias soluciones, asegúrate de que todas las soluciones (los ficheros .sln) están en el mismo directorio. En caso contrario puedes tener problemas. Es lógico una vez se entiende que hace NuGet y realmente es difícil que pueda hacer otra cosa que la que hace, así que bueno… es algo a tener en cuenta.

Vamos a reproducirlo paso a paso, para entender que ocurre. Para ello crea un directorio, yo lo he llamado nuroot. Luego crea otra carpeta (yo la he llamado folder1) dentro de nuroot:

image

Ahora crea una solución de VS (una aplicación de consola) dentro de folder1 (yo la he llamado DemoProject). Una vez hecho esto agrega un paquete de NuGet a la solución (p. ej. DotNetZip). Ahora la estructura de paquete debe ser como sigue:

image

El directorio packages está al nivel de la solución, y si miras en el proyecto verás que la referencia al ensamblado (en el caso de DotNetZip el ensamblado se llama Ionic.Zip) apunta al directorio packages. De hecho la referencia se guarda relativa al fichero de proyecto (si abres el .csproj en modo texto lo verás):

  1. <Reference Include="Ionic.Zip">
  2.   <HintPath>..\packages\DotNetZip.1.9.3\lib\net20\Ionic.Zip.dll</HintPath>
  3. </Reference>

Perfecto. Ahora crea otra solución vacía (New Project –> Other Project Types –> Visual Studio Solution –> Empty Solution) y dale el nombre que quieras. Yo la he llamado SecondSolution. Lo importante es que la crees en nuroot, no en folder1. Por defecto VS crea un directorio para la solución, pero vamos a eliminarlo. Ve a nurootSecondSolution y mueve el fichero SecondSolution.sln a nuroot. Luego borra el directorio SecondSoution. En este punto la estructura de directorios es pues la misma de antes, con la salvedad de que en la carpeta nuroot hay el fichero SecondSolution.sln.

Finalmente agrega el proyecto existente (DemoProject) a la solución SecondSolution. ¡Una vez cargues el proyecto verás el mensaje de que faltan paquetes de NuGet! Dale a Restore para que NuGet se descargue los paquetes faltantes y la estructura de directorios será la siguiente:

image

Ten presente que NuGet funciona a nivel de solución. Cuando hemos agregado el proyecto DemoProject a la segunda solución, NuGet ha examinado el fichero packages.config del proyecto y ha visto una referencia a DotNetZip. Luego ha examinado el directorio packages de la solución. Y al estar esta otra solución en otro directorio que la anterior, NuGet no encuentra el directorio packages, y por lo tanto asume que debe descargarse los paquetes. Y al descargarlos es cuando nos aparece el otro directorio packages ahora colgando de nuroot (el directorio donde tenemos SecondSolution.sln).

Si compilas el proyecto todo funcionará pero hay un tema importante ahí. El proyecto DemoProject ya contenía una referencia al ensamblado de DotNetZip y al existir dicha referencia NuGet no la modificará. Es decir, la referencia sigue apuntando donde apuntaba inicialmente (nurootfolder1DemoProjectPackages).

Cierra VS y borra los dos directorios packages. Con esto simulas lo que le ocurriría a alguien que se descargase el código fuente (suponiendo que los paquetes no están subidos en él). Ahora carga SecondSolution.sln otra vez y de nuevo verás que NuGet dice que faltan paquetes (obvio, pues los hemos borrado todos). Restaura de nuevo y verás como NuGet crea otra vez el directorio nurootpackages. Pero el fichero csproj sigue teniendo la referencia a nurootfolder1DemoProject y por lo tanto no encontrará la referencia. Es decir, el código no compilará. Para que te compile debes abirlo con la solución DemoProject.sln, restaurar paquetes (o compilar simplemente, recuerda que al compilar se restauran automáticamente) y entonces ya te compilará (desde ambas soluciones).

En este escenario quizá no te parezca tan grave porque total, haces un readme.txt y que diga “Abrir primero DemoProject.sln” y listos. Hombre, es feo y un poco chapuza pero bueno…

Pero el problema lo tienes si luego añades otro paquete de NuGet al proyecto cuando lo tienes abierto con la solución SecondSolution.sln. P. ej. yo he instalado el CommandLineParser. Por supuesto este paquete está instalado en nurootpackages y la referencia del proyecto apunta a este directorio. Observa como han quedado las referencias del proyecto:

  1. <Reference Include="CommandLine">
  2.   <HintPath>..\..\..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
  3. </Reference>
  4. <Reference Include="Ionic.Zip">
  5.   <HintPath>..\packages\DotNetZip.1.9.3\lib\net20\Ionic.Zip.dll</HintPath>
  6. </Reference>

Por lo tanto ahora si borras los directorios packages, debes abrir el proyecto en ambas soluciones y compilarlas en ambas (para forzar la restauración de paquetes). La primera solución que compiles te dará un error (le faltará el paquete que se añadió a través de la otra solución), da igual el orden en que lo hagas. La segunda que compiles si que compilará bien.

Al final terminarás con ambos paquetes instalados en ambos directorios packages pero solo se usará uno de cada (el referenciado por el proyecto).

Igual no te parece muy grave pero si tienes una build que compila una de esas soluciones dala por perdida: cada vez que se ejecuta la build se parte d un entorno nuevo, por lo que la build no compilará, está rota.

La mejor solución para ello es simplemente tenerlo presente: evita que un mismo proyecto esté en varias soluciones localizadas en directorios distintos (si las soluciones, los ficheros .sln, están todos en la misma carpeta no hay problema porque el directorio packages de todas ellas es el mismo).

Espero que el post os haya resultado interesante!

Saludos!

ASP.NET Historia de una optimización. ¡Cuidado con la Sesión!

En un cliente en el que he estado últimamente tenían un problema de rendimiento en su aplicación ASP.NET. El problema era más o menos que “cuando un usuario está buscando algo, entonces la aplicación se queda bloqueada”. Por supuesto el primer paso fue determinar que significaba “bloqueada” ya que es una palabra un tanto ambigua… Uno de los aspectos que conoce todo el mundo que trata con problemas reportados por usuarios finales es que muchas veces (por no decir casi siempre) el problema está descrito entre mal y peor.

Al final que la aplicación se bloqueaba significaba que el usuario no podía navegar a ningún otro sitio. Debo contextualizar que es una aplicación web que consta de una pagina principal con un menú a modo de “escritorio” y cada opción que selecciona el usuario se abre en una ventana nueva de navegador. Así los usuarios terminan teniendo varias ventanas del navegador abiertas y van haciendo cosillas (búsquedas, ediciones, lo que sea que hagan) en cada una de ellas. Pues bien lo que ocurría es que mientras en alguna ventana se estaban buscando datos, cuando se pulsaba en cualquier otra opción, esta se quedaba esperando y no cargaba hasta que finalizaba la búsqueda de la otra ventana.

Lo primero que hice fue verificar que no estuviesen haciendo ellos un bloqueo por código (tenían algunos singletons donde se guardaban ciertos datos) pero no vi nada sospechoso. Había descartado cualquier bloqueo de BBDD porque un análisis previo realizado había confirmado que no habían bloqueos, por lo que en este aspecto estaba tranquilo. El siguiente punto fue ver en que momento se bloqueaban las otras peticiones. Ahí tuve que invertir un poco de tiempo ya que el proyecto consta de varias soluciones de VS y es un proyecto en webforms bastante complejo. Al final pude configurar mi sistema para depurar parte de la web y observé que ni llegaba al Page_Load del formulario. Eso era interesante pero como el ciclo de vida de Webforms es inescrutable tenía que asegurarme que no se quedase en algún punto anterior al Page_Load (lease algún evento Init o PreInit perdidos por ahí) de cualquier formulario base que pudiese haber. Después de navegar un poco por el código fuente (es una aplicación con una jerarquía de formularios bastante… interesante) llegué a la conclusión de que no. De que las peticiones ni tan siquiera habían entrado en el pipeline de webforms. Se quedaban atascadas antes.

Finalmente me dio por monitorizar el estado de peticiones del proceso de trabajo desde la consola de IIS y llegué a ver lo siguiente:

Captura

Tenía dos peticiones en marcha y una tercera que estaba bloqueada esperando acceso a la sesión. Y es que en asp.net solo se permite una petición concurrente por usuario que tenga acceso de escritura a la sesión. Si la petición requiere solo de acceso a lectura no hay problema, pero nunca entraran dos peticiones concurrentes que tengan permisos de escritura en la sesión (por usuario). Esto es muy importante porque no se trata de si realmente se lee o se escribe en la sesión. Se trata de si se tienen permisos para escribir o leer en la sesión.

De las dos peticiones en marcha que tenía una era a un servicio web que no usaba sesión y la otra era la del buscador. El problema básico de la aplicación es que todas las peticiones tenían permisos de lectura y escritura en la sesión, por lo que todas las peticiones se encolaban una tras otra y nunca había dos peticiones (a dos formularios .aspx) en paralelo. Eso en la operativa normal de la aplicación no se notaba y pasaba desapercibido, pero cuando había un buscador en marcha se notaba simplemente porque el buscador podía tardar bastante tiempo en responder (del orden de segundos). Por lo tanto si un usuario mientras esperaba que el buscador le devolviese resultados (recuerda que el buscador está en otra ventana) volvía a la ventana principal y intentaba lanzar otra operación, esta operación se quedaba sin poder cargarse hasta que finalizaba el buscador. Además al abrirse en ventana nueva, la sensación del usuario era de una ventana nueva en blanco que no hacía nada.

Un workaround rápido fue declarar que los buscadores tuviesen solo acceso de lectura a la sesión. Como todos los buscadores derivaban de un formulario padre para búsquedas fue fácil y rápido añadir el atributo EnableSessionState=”ReadOnly” en la directiva @Page de dicho formulario. Y problema solucionado…

…o más bien parche aplicado, porque la solución real pasaría por la inversa: declarar que el acceso habitual a la sesión es de “solo lectura” (añadiendo <pages enableSessionState="ReadOnly" /> en el web.config) y declarar acceso de lectura y escritura solo en aquellos formularios que quieran escribir en la sesión (usando EnableSessionState=”true” en la directiva @Page). De hecho sería incluso mejor deshabilitar en el web.config el acceso a sesión (colocando false en el enableSessionState) y colocar explícitamente los valores ReadOnly y true a cada formulario que requiera acceder a la sesión (en modo de solo lectura o con permisos totales). Pero ese es un refactoring mucho más complejo, claro.

Ten presente pues que si tus peticiones declaran que pueden acceder en modo lectura y escritura a la sesión nunca se ejecutarán de forma concurrente (para un mismo usuario). Quizá no te des por aludido porque tu aplicación no abre varias ventanas pero… ¿haces varias llamadas AJAX de forma simultánea? Si es así… ¿qué permisos tienen al respecto de la sesión?.

Si en lugar de webforms usas ASP.NET MVC recuerda que puedes aplicar el atributo [SessionState] para indicar que un controlador requiere un acceso a la sesión distinto del que esté indicado por defecto en el web.config (o que no requiere acceso en absoluto).

¿Mi recomendación? Evita usar la sesión en todo lo que puedas. Recuerda que en ASP.NET los datos de autenticación no se guardan en la sesión (tienen su propia cookie separada). Pero si al final te decides a usarla, desactívala por defecto en el web.config y actívala explícitamente en todas aquellas páginas/controladores que la requieran. Y cuando la actives actívala siempre en modo ReadOnly a no ser que realmente debas escribir en ella, claro 😉

Con esos sencillos pasos conseguirás evitar que se te queden peticiones enganchadas esperando por una sesión que a lo mejor ni necesitan!

Saludos!

Securiza tu WebApi con tokens autogenerados

El escenario que vamos a abordar en este post es el siguiente: tienes una API creada con ASP.NET WebApi y quieres que sea accesible a través de un token. Pero en este caso quieres ser tu quien proporcione el token y no un tercero como facebook, twitter o Azure Mobile Services (como p. ej. en el escenario que cubrimos en este post). Para ello nuestra API expondrá un endpoint en el cual se le pasarán unas credenciales de usuario (login y password) para obtener a cambio un token. A partir de ese momento todo el resto de llamadas de la API se realizarán usando este token y las credenciales del usuario no seran necesarias más.

Para empezar crea un proyecto ASP.NET con el template “Empty” pero asegúrate de marcar la checkbox de “Web API” para que nos la incluya por defecto. Luego agregamos los paquetes para hospedar OWIN, ya que vamos a usar componentes OWIN tanto para la creación de los tokens oAuth como su posterior validación. Así pues debes incluir los paquetes “Microsoft.AspNet.WebApi.Owin” y “Microsoft.Owin.Host.SystemWeb”.

El siguiente paso será crear una clase de inicialización de Owin (la famosa Startup). Para ello puedes hacer click con el botón derecho sobre el proyecto en el solution explorer y seleccionar “Add –> OWIN Startup class” o bien crear una clase normal y corriente llamada Startup. El código inicial es el siguiente:

  1. [assembly: OwinStartup(typeof(OauthProviderTest.Startup))]
  2.  
  3. namespace OauthProviderTest
  4. {
  5.     public class Startup
  6.     {
  7.         public void Configuration(IAppBuilder app)
  8.         {
  9.             var config = new HttpConfiguration();
  10.             WebApiConfig.Register(config);
  11.             ConfigureOAuth(app);
  12.             app.UseWebApi(config);
  13.         }
  14.     }
  15. }

La clase WebApiConfig es la que configura WebApi y la generó VS al crear el proyecto (está en la carpeta App_Start). Nos falta ver el método ConfigureOAuth que veremos ahora mismo. Observa que el método ConfigureOAuth se llama antes del app.UseWebApi, ya que vamos a añadir middleware OWIN en el pipeline http y debemos hacerlo antes de que se ejecute WebApi. Y por cierto, dado que ahora inicializamos nuestra aplicación usando OWIN puedes eliminar el fichero Global.asax, ya que no lo necesitamos para nada.

Veamos ahora el método ConfigureOAuth. En este método debemos añadir el middleware OWIN para la creación de tokens OAuth. Para ello podemos usar el siguiente código:

  1. public void ConfigureOAuth(IAppBuilder app)
  2. {
  3.     var oAuthServerOptions = new OAuthAuthorizationServerOptions()
  4.     {
  5.         AllowInsecureHttp = true,
  6.         TokenEndpointPath = new PathString("/token"),
  7.         AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
  8.         Provider = new CredentialsAuthorizationServerProvider(),
  9.     };
  10.     app.UseOAuthAuthorizationServer(oAuthServerOptions);
  11. }

Con ello habilitamos un endpoint (/token) para generar los tokens oAuth. El proveedor de dichos tokens es la clase CredentialsAuthorizationServerProvider (que veremos a continuación). Esta clase será la que recibirá las credenciales (login y password), las validará y generará un token oAuth.

Por supuesto nos falta ver el código para validar las credenciales y aquí es donde entra la clase CredentialsAuthorizationServerProvider. Esa clase es la que recibe el login y el password del usuario, los valida y crea el token oAuth:

  1. public class CredentialsAuthorizationServerProvider : OAuthAuthorizationServerProvider
  2. {
  3.     public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
  4.     {
  5.         context.Validated();
  6.     }
  7.  
  8.     public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
  9.     {
  10.  
  11.         context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
  12.  
  13.         using (TestContext db = new TestContext())
  14.         {
  15.             var user = db.Users.FirstOrDefault(u => u.Login == context.UserName && u.Password == context.Password);
  16.             if (user == null)
  17.             {
  18.                 context.SetError("invalid_grant", "The user name or password is incorrect.");
  19.                 return;
  20.             }
  21.         }
  22.  
  23.         var identity = new ClaimsIdentity(context.Options.AuthenticationType);
  24.         identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
  25.         identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
  26.         context.Validated(identity);
  27.     }
  28. }

Lo único remarcable es la primera línea del método GrantResourceOwnerCredentials que se encarga de habilitar CORS. WebApi tiene soporte para CORS, pero el endpoint /token no está gestionado por WebApi si no por el middleware OWIN así que debemos asegurarnos de que mandamos las cabeceras para habilitar CORS. El resto del código es un acceso a la BBDD usando un contexto de EF para encontrar el usuario con el login y el password correcto. Por supuesto en un entorno de producción eso no se haría así. Este código no tiene en cuenta que las passwords deben guardarse como un hash en la BBDD. Si quieres acceder a BBDD directamente debes generar el hash de los passwords aunque si una mejor opción es usar Identity (y la clase UserManager) para acceder a los datos de los usuarios. Una vez validado que las credenciales son correctas creamos la ClaimsIdentity y generamos el token correspondiente.

Para probarlo podéis hacer un POST a /token con las siguientes características:

  • Content-type: application/x-www-form-urlencoded
  • En el cuerpo de la petición añadir los campos:
    • grant_type = “password”
    • username = “Login del usuario”
    • password = “Password del usuario”

Es decir como si tuvieses un formulario con esos campos y lo enviaras via POST al servidor. Os pongo una captura de la petición usando postman:

image

Se puede ver que la respuesta es el token, el tiempo de expiración (que se corresponde con el valor de la propiedad AccessTokenExpireTimeSpan) y el tipo de autenticación que es bearer (ese es el valor que deberemos colocar en la cabecera Authorization).

A partir de ese punto tenemos un token válido y nos olvidamos de las credenciales del usuario. Al menos hasta que el token no caduque. Cuando el token caduque, se deberá generar uno nuevo con otro POST al endpoint /token.

El siguiente punto es habilitar WebApi para que tenga en cuenta esos tokens. Hasta ahora nos hemos limitado a generar tokens, pero WebApi no hace nada con ellos. De hecho no habilitamos WebApi si no que añadimos otro módulo OWIN para autenticarnos en base a esos tokens. El proceso ocurre antes y es transparente a WebApi. Para ello debemos añadir las siguientes líneas a la clase Startup al final del método ConfigureOAuth:

  1. var authOptions = new OAuthBearerAuthenticationOptions()
  2. {
  3.     AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active
  4. };
  5. app.UseOAuthBearerAuthentication(authOptions);

Así añadimos el módulo de OWIN que autentica en base a esos tokens. Para hacer la prueba vamos a crear un controlador de WebApi y vamos a indicar que es solo para usuarios autenticados:

  1. [Authorize]
  2. public class SecureController : ApiController
  3. {
  4.     public IHttpActionResult Get()
  5.     {
  6.         return Ok("Welcome " + User.Identity.Name);
  7.     }
  8. }

Y ahora para probarlo hacemos un GET a la URL del controlador (/api/secure) y tenemos que pasar la cabecera Authorization. El valor de dicha cabecera es “Bearer <token>”:

image

Y con esto deberíamos obtener la respuesta del controlador. En caso de que no pasar la cabecera o que el token fuese incorrecto el resultado sería un HTTP 401 (no autorizado).

Unas palabras sobre los tokens

Fíjate que en ningún momento guardamos en BBDD los tokens de los usuarios y esos tokens son válidos durante todo su tiempo de vida incluso en caso de que el servidor se reinicie. Eso es así porque el token realmente es un ticket de autenticación encriptado. El middleware de OWIN cuando recibe un token se limita a desencriptarlo y en caso de que sea válido, extrae los datos (los claims de la ClaimIdentity creada al generar el token) y coloca dicha ClaimIdentity como identidad de la petición. Es por eso que en el controlador podemos usar User.Identity.Name y recibimos el nombre del usuario que entramos.

Por lo tanto cualquier persona que intercepte el token podrá ejecutar llamadas “en nombre de” el usuario mientras este token sea válido. A todos los efectos poseer el token de autenticación equivale a poseer las credenciales del usuario, al menos mientras el token sea válido. Tenlo presente si optas por ese mecanismo de autenticación: si alguien roba el token, la seguridad se ve comprometida mientras el token sea válido. Por supuesto eso no es distinto al caso de usar una cookie, donde el hecho de robar la cookie compromete la seguridad de igual forma. Y los tokens son más manejables que las cookies y dan menos problemas, en especial en llamadas entre orígenes web distintos.

¡Espero que este post os resulte interesante!

Aurelia… lo nuevo después de Durandal

Seguro que muchos de vosotros conocéis Durandal, la librería para crear aplicaciones SPA ideada por Rob Eisenberg autor también de otras librerías como Caliburn y con cierta obsesión por las espadas…

Hacé algún tiempo Rob anunció que se había unido al equipo de Angular para colaborar con el desarrollo de la versión 2.0 de dicha librería y que Angular y Durandal convergerían en una única librería. Eso generó muchas reacciones, algunas positivas, otras no tanto. A pesar de que Durandal tenía sus usuarios la verdad es que no había conseguido, ni mucho menos, la popularidad de Angular que con esas noticias parecía convertirse en el standard de facto para crear aplicaciones SPA (si es que ya no lo era).

Posteriormente Rob anunció que dejaba el equipo de Angular debido a diferencias en como tenía que evolucionar la librería. Así que anunció que Durandal seguiría su camino y que evolucionaría de forma independiente a Angular y que en fin… todo seguía más o menos como antes. También prometía novedades sobre Durandal NextGen, la versión que tenía en mente y que nunca hubiese existido si Rob se hubiese quedado en el equipo de Angular.

Y finalmente Rob anuncia la aparición de Aurelia, su nuevo framework para aplicaciones SPA. Ya… para seguir la tradición lo podría haber llamado Hrunting o Glamdring o ya puestos Tizona pero no… ha optado por Aurelia. Nuevo nombre para dejar claro que no se trata de una evolución de Durandal si no de una librería totalmente nueva, diseñada desde cero y pensada para ser moderna: está escrita integramente en ES6 aunque transpilada (vaya palabreja) y pollyfilizada (otra palabreja… lo siento por los de la RAE) para trabajar con los navegadores evergreen de hoy en día (un navegador evergreen es aquel del cual no importa su versión por estar siempre actualizado. Si te pregunto tu versión de Chrome o Firefox dudo que la sepas exactamente, esos son los navegadores evergreen).

A diferencia de Angular, Aurelia es más un conjunto de librerías que de forma conjunta forman un framework. Así se pueden usar las librerías sueltas para pequeñas tareas o bien utilizarlas todas en conjunto para crear aplicaciones SPAs. Eso tiene la ventaja de que uno solo paga por lo que usa y que la curva de aprendizaje es, a priori, más suave en tanto se pueden incorporar los conceptos de forma más independiente (en Angular todo está muy atado y es difícil explicar los conceptos de forma separada).

Empezando con Aurelia

Las instrucciones para empezar están todas en http://aurelia.io/get-started.html. Básicamente antes se tiene que instalar gulp y jspm. El primero se usará como sistema de build y el segundo se usa como gestor de paquetes. Esas dos herramientas no forman parte de Aurelia como tal y podrías usar grunt o bower si lo prefieres (asumiendo que los paquetes de Aurelia estén subidos en bower que no lo sé), pero todos los ejemplos de la página web de Aurelia están en glup y jspm.

Veamos lo mínimo necesario para usar Aurelia. Para ello partimos de un directorio vacío y ejecutamos jspm install aurelia-bootstrapper lo que nos instalará un montón de dependencias.

Ahora vamos a necesitar crear tres ficheros:

  • El bootstrapper o lanzador de nuestra aplicación
  • La vista
  • El viewmodel

Vemos pues que Aurelia usa el patrón MVVM y en efecto tiene enlace bidireccional entre la vista y el viewmodel asociado. Veamos cada uno de esos componentes.

El lanzador (index.html) es el encargado de cargar aurelia y lanzar la vista inicial:

  1. <!doctype html>
  2. <html>
  3. <head>
  4. </head>
  5. <body aurelia-app>
  6.     <script src="jspm_packages/system.js"></script>
  7.     <script src="config.js"></script>
  8.     <script>
  9.     System.baseUrl = 'myapp';
  10.     System.import('aurelia-bootstrapper').catch(console.error.bind(console));
  11.     </script>
  12. </body>
  13. </html>

La línea clave aquí es la que establece System.baseUrl a “myapp”. Eso le indica a Aurelia que cargue la vista myapp/app.html y el viewmodel asociado myapp/app.js.

Empecemos por ver el viewmodel que es una clase pura de JavaScript:

  1. export class Welcome{
  2.     constructor(){
  3.         this.name = 'Eduard Toms';
  4.         this.twitter = '@eiximenis';
  5.     }
  6.  
  7.     get twitterUri() {
  8.         return `http://twitter.com/${this.twitter}`;
  9.     }
  10.  
  11.     welcome(){
  12.         alert(`Welcome ${this.name}!`);
  13.     }
  14. }

Aunque quizá no te lo parezca eso es JavaScript puro, concretamente EcmaScript 6, que tiene entre otras características la posibilidad de definir módulos y clases. En este caso el el fichero myapp/app.js es un módulo que se limita a exportar una sola clase llamada Welcome.

Observa también que las propiedades twitterUri() y welcome() usan cadenas pero con el carácter ` (tilde abierta) y no con la comilla simple (ya, el coloreado de código falla totalmente :p). Esa es otra característica de ES6 llamada string interoplation. Sí… ES6 viene cargado con un montón de novedades.

Finalmente nos queda la vista. Las vistas son pedazos de html encapsulados dentro de una etiqueta template:

  1. <template>
  2.     <section>
  3.         <h2>${heading}</h2>
  4.         <form role="form" submit.delegate="welcome()">
  5.             <div class="form-group">
  6.                 <label for="fn">First Name</label>
  7.                 <input type="text" value.bind="name" class="form-control" id="fn" />
  8.             </div>
  9.             <div class="form-group">
  10.                 <label for="ln">Twitter</label>
  11.                 <input type="text" value.bind="twitter" class="form-control" id="ln" />
  12.             </div>
  13.             <div class="form-group">
  14.                 <label>Hello ${name}. Check your twitter account (<a href.bind="twitterUri">${twitter}</a>)?</label>
  15.                 <p class="help-block">${fullName}</p>
  16.             </div>
  17.             <button type="submit" class="btn btn-default">Submit</button>
  18.         </form>
  19.     </section>
  20. </template>

Puedes observar como los enlaces en texto se definen con la sintaxis ${propiedad} (estos enlaces son obviamente unidireccionales). Por otro lado cuando enlazas una propiedad (tal como value o href) se usa la sintaxis propiedad.bind=”propiedad” (p. ej. value.bind o href.bind).

Si ejecutas index.html eso en un navegador que soporte ES6 (p. ej. Chrome) deberás ver algo parecido a:

image

(El alert aparece al pulsar el botón de submit).

Por supuesto si el navegador que tienes no soporta ES6 siempre puedes usar 6to5 para transpilar (es decir convertir xD) el código ES6 usado al ES5 soportado hoy en día para la mayoría de navegadores. Aquí es donde entra gulp en el proceso.  Otra opción es usar directamente ES5 si prefieres. Pero bueno… veremos esto en otro post 🙂

Y bueno… tampoco me ha dado tiempo para ver mucho más de Aurelia. Por lo poco que he visto la idea en general es parecida a Angular o Ember en tanto que usa el patrón MVVM (el viewmodel de Aurelia se corresponde aproximadamente al controlador de Angular) y que soporta enlaces bidireccionales.

Os animo por supuesto a que le echéis un vistazo, al menos para conocerla brevemente… veremos si gana popularidad y se convierte en una alternativa a Angular o se queda en el intento. 🙂

Saludos!

Evento–Bilbostack 2015

Buenas! El sábado (17 de Enero) tuve el placer de asistir al Bilbostack, una conferencia para desarrolladores que se celebra anualmente en Bilbao. En primer lugar felicitar a los organizadores porque la conferencia estaba muy bien montada: el sitio (la universidad de Deusto) era genial al igual que las dos salas usadas para los dos tracks paralelos que había. La primera, el auditorio, simplemente espectacular. La segunda, la sala Gárate, se quedó pequeña en alguna charla (la de IoT con gadgeeter que impartió Quique) pero bueno…

Yo tuve el placer de dar una charla hablando sobre Reactjs, ya sabéis “la librería esa de Facebook” que está ganando mucha tracción ultimamente y que propone un modelo diferente para gestionar nuestra UI. Tengo varios posts a medias sobre Reactjs que espero poder irlos terminando y sacando en breve 😉

Sobre las charlas en general todas muy buenas… Una mención especial para Hugo Biarge que eligió un tema que puede ser muy pero que muy farragoso (las directivas de Angular) pero que contó magistralmente… vamos que creo que incluso alguno se enteró hasta de lo que era la transclusión 😛

Y otra mención especial para Javi Santana: su charla sobre la arquitectura de CartoDB estuvo muy pero que muy bien. No entró en mucho detalle, tuvo un poco de “he venido a vender el producto”, pero lo mejor de la charla fueron “los troleos” que iba soltando, todos ellos muy interesantes y acertados (troleos contra los que quieren usar solo lo último, migrar por si, o usar el nuevo requeteframework sin valorar las implicaciones). Lo dicho, una gran charla para terminar el Bilbostack.

Luego del evento hubo comida-networking-y-todo-eso por la Plaza Nueva tomando los pintxos “en el bar de Jon”… brutales 🙂

Os dejo con el enlace a slideshare de mi presentación.

Ah sí… Y se me olvidaba, jejejeje… felicitar de nuevo a los organizadores por haber hecho que el Beerbao fest coincidiese con el Bilbostack 😛. ¿Qué mejor manera de terminar un evento se os ocurre que tomarse unas buenas cervezas artesanas, eh? 😉

Saludos!!!!

PD: Para ver más info y fotos del Bilbostack os recomiendo seguir a su cuenta de twitter y buscar por el hastag #Bilbostack.