En el post anterior vimos todo la parte teórica de HTTP que nos permite realizar descargas de ficheros, pausarlas y continuarlas. Pero ahora viene lo bueno… Vamos a implementar el soporte en el servidor para soportar dichas descargas.

Dado que nuestro amado FileStreamResult no soporta la cabecera Range, nos va a tocar a nostros hacer todo el trabajo. Pero, la verdad… no hay para tanto.

Nota importante: Todo, absolutamente todo el código que pongo en este blog está para que hagas con él lo que quieras. Pero del mismo modo, el código que hay en este blog NO ES CÓDIGO DE PRODUCCIÓN. En muchos casos no está suficientemente probado y en otros, para simplificar, no se tienen en cuenta todas las casuísticas o no hay apenas control de errores. Si quieres hacer copy/paste eres totalmente libre de hacerlo, pero revisa luego el código. Entiéndelo y hazlo tuyo.

Bien, el primer paso va a ser crearnos un ActionResult nuevo, en este caso una clase llamada RangeFileActionResult y que herederá pues de ActionResult. En dicho ActionResult vamos a obtener el valor del campo Range y a parsearlo. Empecemos por el constructor:

  1. public RangeFileActionResult(string filename, string contentType)
  2. {
  3.     _stream = new FileStream(filename, FileMode.Open, FileAccess.Read);
  4.     _length = _stream.Length;
  5.     _contentType = contentType;
  6.  
  7.     byte[] hash;
  8.     using (var md5 = MD5.Create())
  9.     {
  10.         hash = md5.ComputeHash(_stream);
  11.     }
  12.     _etag = Convert.ToBase64String(hash);
  13.     _stream.Seek(0, SeekOrigin.Begin);
  14. }

Siplemente nos guardamos el stream al fichero y calculamos el MD5. Vamos a usar el MD5 como ETag del fichero.

Ahora, el siguiente paso es implementar el método ExecuteResult, que es donde se procesa toda la petición:

  1. public override void ExecuteResult(ControllerContext context)
  2. {
  3.     using (_stream)
  4.     {
  5.         var ranges = ParseRangeHeader(context.HttpContext.Request);
  6.         if (ranges == null || ranges.Empty)
  7.         {
  8.             new FileStreamResult(_stream, _contentType).ExecuteResult(context);
  9.             return;
  10.         }
  11.         if (ranges.Multiple)
  12.         {
  13.             ProcessMultipleRanges(context, ranges);
  14.         }
  15.         else
  16.         {
  17.             ProcessSingleRange(context, ranges.Values.Single());
  18.         }
  19.     }
  20. }

Básicamente:

  • Miramos el valor de la cabecera Range. Si no existe o no lo sabemos interpretar, creamos un FileStreamResult tradicional y lo ejecutamos. Es decir, nos saltamos todo lo de los rangos y devolvemos una descarga de fichero tradicional.
  • En caso de que haya más de un rango vamos a generar la respuesta multipart, con el método ProcessMultipleRanges.
  • En el caso de que haya un solo rango vamos a generar la respuesta con el método ProcessSingleRange.

El método ParseRangeHeader se encarga de parsear el valor de la cabecera Range:

  1. private Ranges ParseRangeHeader(HttpRequestBase request)
  2. {
  3.     var rangeHeader = request.Headers["Range"];
  4.     if (string.IsNullOrEmpty(rangeHeader)) return null;
  5.     rangeHeader = rangeHeader.Trim();
  6.  
  7.     if (!rangeHeader.StartsWith("bytes="))
  8.     {
  9.         return Ranges.InvalidUnit();
  10.     }
  11.  
  12.     return Ranges.FromBytesUnit(rangeHeader.Substring("bytes=".Length));
  13.  
  14. }

Se apoya en unas clases llamadas Ranges (que es básicamente una coleccion de objetos Range) y Range que representa un rango (origen, destino). El método importante de Ranges es el FromBytesUnit que es el que realmente parsea una cadena (sin el prefijo bytes=):

  1. public static Ranges FromBytesUnit(string value)
  2. {
  3.     var tokens = value.Split(',');
  4.  
  5.     var ranges = new Ranges();
  6.     foreach (var token in tokens)
  7.     {
  8.         ranges.Add(new Range(token));
  9.     }
  10.  
  11.     return ranges;
  12. }

Y la otra parte del trabajo la realiza el constructor de Range. Así si tenemos la cadena “0-100, 101-200” se llamará dos veces al constructor de Range pasándole primero la cadena “0-100” y luego “101-200”.

  1. public Range(string value)
  2. {
  3.     if (!value.Contains("-"))
  4.     {
  5.         _from = long.Parse(value);
  6.         _to = -1;
  7.     }
  8.     else if (value.StartsWith("-"))
  9.     {
  10.         _from = -1;
  11.         _to = long.Parse(value.Substring(1));
  12.     }
  13.     else
  14.     {
  15.         var idx = value.IndexOf('-');
  16.         _from = long.Parse(value.Substring(0, idx));
  17.         _to = idx == value.Length - 1 ? -1 :
  18.             long.Parse(value.Substring(idx + 1));
  19.     }
  20. }

Sirviendo rangos

Empecemos por el caso más sencillo: Tenemos un solo rango. En este caso el método ProcessSingleRange toma el control:

  1. private void ProcessSingleRange(ControllerContext context, Range range)
  2. {
  3.     var response = context.HttpContext.Response;
  4.     response.StatusCode = 206;
  5.     response.AddHeader("Content-Range", new ContentRange(range, _length).ToString());
  6.     response.AddHeader("ETag", _etag);
  7.     response.ContentType = _contentType;
  8.     FlushRangeDataInResponse(range, response);
  9. }

Este método es muy sencillo. Establece el código de respuesta (206) y crea las cabeceras Content-Range, ETag y Content-Type. Luego llama al método FlushRangeDataInResponse. Dicho método va leyendo los bytes del fichero y los va escribiendo en el buffer de respuesta. Para evitar cargar todo el rango en memoria del servidor (un rango puede ser todo lo largo que desee el cliente), los datos se van leyendo en bloques de 1024 bytes y se van escribiendo en el buffer de salida:

  1. private void FlushRangeDataInResponse(Range range, HttpResponseBase response)
  2. {
  3.     var creader = new ChunkReader(_stream);
  4.     ChunkResult result = null;
  5.     var startpos = 0;
  6.     do
  7.     {
  8.         result = creader.GetBytesChunk(range, startpos);
  9.         startpos += result.BytesRead;
  10.         if (result.BytesRead > 0)
  11.         {
  12.             response.OutputStream.Write(result.Data, 0, result.BytesRead);
  13.         }
  14.         response.Flush();
  15.     } while (result.MoreToRead);
  16. }

Aquí el que hace el trabajo de verdad es la clase ChunkReader. Esta clase es la que va leyendo un stream, por “trozos” y devuelve después de cada trozo si hay “más trozos por leer”:

  1. public class ChunkReader
  2. {
  3.     private readonly Stream _stream;
  4.     public ChunkReader(Stream stream)
  5.     {
  6.         _stream = stream;
  7.     }
  8.  
  9.  
  10.     public ChunkResult GetBytesChunk(Range range, int startpos)
  11.     {
  12.         var chunk = new ChunkResult();
  13.         var reader = new BinaryReader(_stream);
  14.         var remainingLen = range.Length != -1 ? range.Length - startpos : -1;
  15.         if (remainingLen == 0)
  16.         {
  17.             return new ChunkResult();
  18.         }
  19.                 
  20.         var bytesWanted = remainingLen != -1 ? Math.Min(1024, remainingLen) : 1024;
  21.         reader.BaseStream.Seek(range.FromBegin ? startpos : range.From + startpos, SeekOrigin.Begin);
  22.         var buffer = new byte[bytesWanted];
  23.         chunk.BytesRead = reader.Read(buffer, 0, (int)bytesWanted);
  24.         chunk.Data = buffer;
  25.         chunk.MoreToRead = remainingLen != -1
  26.             ? chunk.BytesRead != remainingLen
  27.             : chunk.BytesRead != 0;
  28.  
  29.         return chunk;
  30.     }
  31. }

El objeto ChunkResult contiene los bytes leídos, el número real de bytes leídos y un booleano que indica si hay “más datos” que leer.

Vayamos ahora al soporte para múltiples rangos. La idea es exactamente la misma, salvo que entre cada rango hay que generar el boundary correspondiente en la respuesta. Y eso es exactamente lo que hace el método ProcessMultipleRanges:

  1. private void ProcessMultipleRanges(ControllerContext context, Ranges ranges)
  2. {
  3.     var response = context.HttpContext.Response;
  4.     response.StatusCode = 206;
  5.     response.AddHeader("ETag", _etag);
  6.     response.AddHeader("Content-Type", "multipart/byteranges; boundary=THIS_STRING_SEPARATES");
  7.     foreach (var range in ranges.Values)
  8.     {
  9.         AddRangeInMultipartResponse(context, range);
  10.     }
  11. }

Primero añadimos los campos comunes (es decir el código de respuesta, el ETagm el content-type con el boundary). Y luego para cada rango llamamos al método AddRageInMultipartResponse, que simplemente coloca el boundary, luego el content-range y el content-type correspondiente y finalmente volca los datos del rango en el buffer de respuesta:

  1. private void AddRangeInMultipartResponse(ControllerContext context, Range range)
  2. {
  3.     var response = context.HttpContext.Response;
  4.     response.Write("-- THIS STRING SEPARATES\x0D\x0A");
  5.     response.Write(string.Format("Content-Type: {0}\x0D\x0A", _contentType));
  6.     var contentRange = new ContentRange(range, _length);
  7.     if (contentRange.IsValid)
  8.     {
  9.         response.Write("Content-Range: " + contentRange + "\x0D\x0A\x0D\x0A");
  10.     }
  11.  
  12.     FlushRangeDataInResponse(range, response);
  13.     response.Write("\x0D\x0A");
  14. }

¡Y ya estamos! Algunos ejemplos de lo que vemos. La imagen de la izquierda contiene un solo rango y la de la derecha dos:

imageimage

Con esto hemos visto como añadir soporte para rangos desde el servidor. Por supuesto no está del todo pulido, faltaría añadir el soporte para el If-Range pero bueno… los mimbres vendrían a ser eso.

Nota: Si lo pruebas y colocas un valor de Range inválido (p. ej. Range: bytes=100) recibirás un HTTP 400. Este 400 es generado por IIS incluso antes de que la petición llegue a ASP.NET MVC.

Saludos!

He subido todo el código del POST en un repositorio de GitHub: https://github.com/eiximenis/PartialDownloads. La aplicación web de demo simplemente tiene el siguiente código en Home/Index:

  1. public ActionResult Index()
  2. {
  3.     return this.RangeFile("~\\Content\\test.png", "image/png");
  4. }

Por lo tanto si navegas con un navegador a /Home/Index deberás ver o descargarte la imagen entera. Pero usando fiddler o cURL puedes generar una petición con rangos para ver como funciona.

Para usar cURL te basta con la opción --header:

curl --header "Range: bytes=0-100" http://localhost:39841/

Saludos!

con no comments
Archivado en:

Muy buenas! El objetivo de esta serie posts es ver como podemos implementar en ASP.NET MVC descargas de ficheros con soporte para “pausa y continuación”.

En este primer post veremos (por encima) que cabeceras HTTP están involucradas en las peticiones y las respuestas para permitir continuar una descarga.

Dicho soporte debe estar implementado en el cliente, pero también en el servidor. En el cliente porque ese tiene que efectuar lo que se llama una range request, es decir pasar en la petición HTTP que “parte” del archivo quiere. Y el servidor debe tener soporte para mandar solo esta parte.

En ASP.NET MVC usamos un FileActionResult para soportar descargas de archivos. Es muy cómodo y sencillo pero no tiene soporte para range requests. En esta serie posts veremos como podemos crearnos un ActionResult propio para que soporte este tipo de peticiones!

En el apartado range de la definición de HTTP1.1. se encuentra la definición de la cabecera Range que es la madre del cordero.

De hecho, básicamente hay dos cabeceras involucradas en una range request (que envía el cliente y que el servidor debe entender):

  1. Range: Especifica el rango que desea obtener el cliente
  2. If-Range: Especifica que se haga caso de “Range” solo si los datos no se han modificado desde xx (xx se especifica en la cabecera If-Range). Si los datos se han modificado, olvida Range y envía todos los datos.

Formato de Range

El formato de Range es muy sencillo. Oh sí, leyendo la especificación parece que es super complejo, pero para eso son las especificaciones… :P

A la práctica Range tiene el siguiente formato:

  • Range: bytes=x-y: El cliente quiere los bytes desde x hasta y (ambos inclusivos). P. ej. Range 0-499: Devuelve los 500 primeros bytes.

Si y no aparece entonces significa “dame desde el byte x hasta el final”:

  • Range: bytes=9000: El cliente quiere desde el byte 9000 (inclusive) hasta el finak

Si x no aparece entonces significa “dame los últimos y bytes”. P. ej:

  • Range: bytes=-500: El cliente quiere los últimos 500 bytes.

La cabecera admite distintos rangos separados por comas:

  • Range: bytes=100-200,400-500, –800: Dame los bytes del 100 al 200. Y del 400 al 500 y los últimos 800.

Si los rangos se solapan esto no debe generar un error:

  • Range: bytes=500-700, 601-999: El cliente quiere los bytes del 500 al 700 y del 601 al 999.

Nota: Que la cabecera Range empiece por bytes= no es superfluo. El estándard es extensible y permite que se puedan definir otras unidades además de bytes para especificar los rangos. De ahí que deba especificarse la unidad usada en la cabecera Range. De hecho el servidor puede usar la cabecera Accept-Ranges para especificar que unidades de rangos soporta (p. ej. Accept-Ranges: bytes). Nosotros nos centraremos única y exclusivamente en rangos de bytes.

Respuesta del servidor

Si el cliente solo pide un rango, la respuesta del servidor es una respuesta normal, salvo que en lugar de usar el código 200, devuelve un 206 (Partial content) y con la cabecera Content-Range añadida.

El formato de la cabecera Content-Range es el rango servido, seguido por la longitud total del elemento separado por /. P. ej:

  • Content-Range: bytes 100-200/5000 –> Se están sirviendo los bytes 100 a 200 (ambos inclusives) de un recurso cuya longitud es de 5000 bytes.
  • Content-Range: bytes 100-200/* –> Se están sirviendo los bytes 100 a 200 de un recurso cuya longitud es desconocida.

Sí, en Content-Range no hay el símbolo = entre bytes y el valor de rango. Mientras que en la cabecera Range si que existe dicho símbolo…

Por otra parte, si el cliente ha pedido más de un rango la respuesta del servidor pasa a ser una multipart, es decir, dado que el cliente nos envía varios rangos, en la respuesta debemos incluirlos todos por separado. Hay varias cosas a tener presente.

  1. El código de retorno no es 200, es 206 (Como en el caso anterior)
  2. El content-type debe establecerse a multipart/byteranges y debe indicarse cual es la cadena separadora (el boundary).
  3. Cada subrespuesta viene precedida del boundary y tiene su propio content-type y content-range indicados.

Un ejemplo sería como sigue (sacado de http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p5-range-latest.html#status.206):

HTTP/1.1 206 Partial Content
Date: Wed, 15 Nov 1995 06:25:24 GMT
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
Content-Length: 1741
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 500-999/8000

...the first range...
--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 7000-7999/8000

...the second range
--THIS_STRING_SEPARATES--

Si el servidor no puede satisfacer la petición del cliente debido a que los rangos pedidos son inválidos o hay demasiados que se solapan o lo que sea, puede devolver un HTTP 416 (Range not Satisfiable). Si lo hace deberia añadir una cabecera Content-Range inválida con el formato:

Content-Range: bytes */x (siendo x el número de bytes totales del recurso).

Otra opción si los rangos son inválidos es devolver el recurso entero usando un HTTP200 tradicional (muchos servidores hacen esto, ya que si un cliente usa rangos debe estar siempre preparado por si el servidor no los admite).

Cabecera If-Range

Nos falta mencionar la cabecera If-Range. La idea de dicha cabecera es que el cliente pueda decir algo como “Oye, tengo una parte del recurso descargado pero es de hace 3 días. Si no ha cambiado, pues me mandas los rangos que te indico en Range, en caso contrario, pues que le vamos a hacer me lo mandas todo con un 200 tradicional”.

El valor de If-Range puede ser, o bien un ETag que identifique el recurso o bien una fecha. Si no sabes lo que es un ETag, pues bueno es simplemente un identificador del contenido (o versión) del recurso. Puede ser un valor de hash, un valor de revisión, lo que sea. No hay un estándar definido, pero la idea es que si el cliente sabe el ETag de un recurso y lo manda, el servidor puede indicarle al cliente si dicho recurso ha cambiado o no. El cliente manda el ETag que tiene para dicho recurso (en la cabecera If-Range o bien en la If-None-Match si no hablamos de rangos).

Oh, por supuesto, uno podría crear un servidor que devolviese un ETag único cada vez que el cliente no le pasa un ETag previo y devolver siempre ETag recibido por el cliente en caso contrario. En este caso, se podría asegurar que cada cliente tendría un ETag distinto. ¿Ves por donde vamos, no? Un mecanismo sencillo y barato para distinguir usuarios únicos. Mucho mejor que todas esas maléficas cookies y además para usar ETags no es necesario colocar ningún popup ni nada parecido. Además, a diferencia de las cookies que se eliminan borrando las cookies (lo que mucha gente hace), los ETags se borran generalmente vaciando la cache del navegador (lo que hace mucha menos gente). Por supuesto, he dicho que uno podría hacer esto… no que se haya hecho :P

Bueno… Hasta ahí el primer post. Rollo teórico, pero bueno, siempre es importante entender como funcionan las cosas ¿no?… en el siguiente post pasaremos a la práctica!!! :D

con no comments
Archivado en:

Esta noche he tenido el placer de participar en el marco de un #programadorIO en un debate sobre los lenguajes tipados vs los no tipados. Puedes ver el debate en youtube: https://www.youtube.com/watch?v=sxOM6sYgn5U

Nota: En el contexto de este post “no tipado” significa débilmente tipado o de tipado dinámico. Y tipado significa fuertemente tipado o de tipado estático.

Mi opinión es que los lenguajes no tipados son muy adecuados para prototipados, por que las herramientas suelen ser más ágiles y porque te permiten “saltarte” en primera instancia una fase mucho más formal de diseño (fase que luego tarde o temprano tiene que venir, pero en un lenguaje tipado tiene que realizarse al principio para, precisamente, poder diseñar los tipos). De todos modos mi experiencia profesional versa mayoritariamente en los lenguajes tipados (C++, Java y C#). También he mencionado que creo que el auge de JavaScript no es tanto por el lenguaje en sí, si no que viene de la mano del auge del desarrollo web. Si desarrollas para la web, debes hacerlo casi si o si, en JavaScript. Hubiese estado bien la opinión de alguien que hubiese desarrollado tan solo en un lenguaje dinámico que no sea JavaScript (p. ej. Ruby) porque dentro de pequeñas diferencias creo que todos compartíamos mucho en común y que estábamos más del lado de los tipados que de los no tipados.

Bien, aclarado esto, yo he hecho bastante incapié, o lo he intentado al menos, en que a veces un sistema estático de tipos es un “corsé” no deseado y que para ciertas tareas un lenguaje no tipado es mejor o te permite realizarlas de forma mucho más natural o productiva. Y voy a poner algunos ejemplos concretos usando C# (casi todo lo que diré es aplicable a Java también).

Ejemplo 1: Deserialización de datos dinámicos

Este es el ejemplo que apuntaba Pedro. En el fondo es muy simple, puesto que si los datos son dinámicos ¿qué mejor que un lenguaje dinámico para deserializarlos?

Imagina que tienes que consumir una api REST que te puede devolver un objeto (da igual el formato, JSON, XML o lo que sea) que tiene 150 campos posibles, todos ellos opcionales. Pueden aparecer o no pueden aparecer. Si tienes que deserializarlo en un lenguaje tipado, que haces: crear una clase con 150 miembros? Y si alguno de los miembros es un int y vale 0… este 0 es porque no ha aparecido o bien porque realmente he llegado un 0. Si claro, puedes usar Nullable<int> pero… bonito y divertido no es.

¿Y si en lugar de ser campos simples son compuestos? ¡Terminas teniendo una jerarquía enorme de clases tan solo para deserializar las respuestas!

El mismo problema te lo encuentras si eres el que crea la API REST por supuesto. Pero incluso peor… porque igual no puedes usar la clase con 150 miembros porque a lo mejor los miembros vacíos o con el valor por defecto se serializarían también y no quieres eso. Vas a terminar igual: con un numero enorme de clases tan solo para serializar los datos.

Si los datos con los que trabajas tienen una naturaleza dinámica, un lenguaje dinámico es lo mejor para tratarlos.

Por supuesto podrías trabajar con algo parecido a un Dictionary<string, object> y serializar el diccionario con el formato de datos esperado. Si, pero haciendo esto estás haciendo un workaround, te estás enfrentando al sistema de tipos. Estás simulando un tipo dinámico en un lenguaje estático. Todas las ventajas del tipado estático desaparecen (el compilador no te ayudará si te equivocas en el nombre de una clase), las herramientas de refactoring no pueden ayudarte en nada (incluso menos que en el caso de un lenguaje dinámico), tu código queda “sucio” (lleno de dictionarios, llamadas a métodos .Add) y además… tardas más.

Ejemplo 2: Jerarquías de clases distintas autogeneradas

Imagina que tienes dos servicios WCF distintos que te devuelven datos muy parecidos. En ambos casos son datos de productos. En ambos casos siempre hay un nombre, un precio y un id. Los nombres de los campos y los tipos SOAP asociados son los mismos (imagina que eso lo puedes definir o imponer).

Si generas los proxies para acceder a los servicios vas a terminar con dos clases diferentes (una por cada servicio). Pero incluso aunque los miembros para el nombre, precio e id se llamasen igual no podrías intercambiar esos proxies en código. Vas a tener dos clases iguales (ambas tendrán un string nombre, un decimal precio y un int id) pero para el compilador son dos clases distintas. Cualquier función que opere sobre uno de los proxies no puede operar con el otro. A pesar de que el aspecto de ambas clases es “idéntico”, a pesar de que representan el “mismo” concepto, para el compilador tienen la mismo parecido que el de un perro con una manzana.

Sí: el problema principal está en que tienes dos clases distintas para lo mismo, pero eso ocurre en la vida real cuando hay herramientas que autogeneran código. ¿Qué soluciones tienes? Pues crear una tercera clase que sea tu “producto” y “transformar” cada uno de los objetos proxy a un objeto de tu clase “producto” que será con la que trabaje tu código. Sí, es posible que los lenguajes tipados tengan un rendimiento superior a los no tipados, pero si empiezas a tener que copiar objetos muchas veces…

¿No estaría bien que tu código que trabaja con un producto pudiese trabajar directamente con cualquiera de los dos proxies? Aunque sean de clases “distintas”. Aunque no haya ninguna interfaz en común. A fin de cuenta tu código tan solo necesita un nombre, un id y un precio. Estaría bien que pudiese funcionar con cualquier objeto que tiene esos tres campos no? Eso se llama duck typing y viene “de serie” con los lenguajes no tipados.

¡Ojo! Que el hecho de que un lenguaje sea tipado no le impide tener algo muy parecido (a efectos prácticos idéntico) al duck typing: P. ej. este problema de los proxies se podría solucionar en C++ con el uso de templates. El uso de templates en C++ es un ejemplo de lo que conoce como structural typing (que es, básicamente, duck typing en tiempo de compilación). Pero no, ni Java ni C# tienen soporte para structural typing.

Ejemplo 3: Generics

Podría poner muchos ejemplos parecidos al de los proxies, incluso cuando no hay código generado. Un ejemplo rápido. Tengo cuatro clases mías, independientes entre ellas.

Ahora quiero crear una colección propia, que implemente IEnumerable<T> pero que internamente use un diccionario, ya que continuamente se estarán buscando elementos por nombre.

Por supuesto las 4 clases tienen una propiedad string Name para guardar el nombre.

Pues bien, para crear esas cuatro colecciones, tienes dos opciones:

  1. Crearte cuatro clases colección idénticas que solo cambian el tipo de datos que aceptan / devuelven. Si, eso suena muy .NET 1.0
  2. Usar generics… Salvo que no puedes.

Y no puedes usar generics porque dentro del código de la clase genérica no puedes acceder a la propiedad Name del tipo genérico. Porque el tipo genérico es “object” por defecto. Por supuesto si las 4 clases implementasen una interfaz común, que se llamase INamedItem (p. ej.) y que definiese la propiedad Name, podrías poner una restricción de generics para que el tipo genérico implementase INamedItem y entonces podrías usar generics para crear tu colección propia. Pero realmente la interfaz INamedItem no representa ningún concepto real. Está tan solo para permitirte usar generics en este caso. Este es otro caso donde duck typing vendría bien: tu colección propia debería funcionar con cualquier objeto que tenga la propiedad Name. Pero el sistema de tipos de C# (con el de Java pasa lo mismo) es incapaz de dar soporte a esta situación.

Ejemplo 4: delegados

Tengo una función que devuelve un bool y acepta un int. Tengo un delegado de tipo Func<int, bool> que “apunta” a dicha función.

Quiero pasar este delegado a otra función… que espera un Predicate<int>.

Conceptualmente Func<T, bool> es lo mismo que Predicate<T> pero para el compilador son totalmente distintos. Por suerte en este caso la solución es muy sencilla, convertir un Func a un Predicate es muy sencillo, pero tienes que hacerlo igualmente.

Nota: Por cierto, aprovechando, no uses nunca Predicate<T> en tu código. Está obsoleto. Func<T,bool> es lo que se debe usar.

Ejemplo 5: Instanciación de tipos dinámica

Este es muy sencillo: quieres instanciar un tipo cuyo nombre no conoces en tiempo de compilación. Da igual la razón: el método puede venirte de un fichero, BBDD o lo que sea.

Cierto, en C# y en Java puedes usar reflection (p. ej. Activator.CreateInstance en C#) para crear la instancia. El problema es que usar reflection elimina todas las ventajas del tipado estático y además el código queda muy “sucio”. Pasar de reflection a tipado estático otra vez no siempre es posible (si sabes que cualquiera de las posibles clases implementa el mismo interfaz puedes convertir el resultado al interfaz y a partir de allí recuperar el tipado estático). Y si tienes que hacer varias cosas usando reflection el código queda “sucio”, dificil de entender y ninguna herramienta de refactorización puede ayudarte.

En un lenguaje no tipado en cambio, la creación del objeto puede requerir una sintaxis distinta pero una vez creado el objeto invocar los métodos será con la misma sintaxis de siempre.

En resumen

Todos los ejemplos presentados (y hay más de posibles), se resumen en dos grandes tipos: comportamiento dinámico (ejemplos 1 y 5) y “objetos semánticamente compatibles pero incompatibles a la práctica” (el resto de ejemplos).

¿Justifican esos casos usar un lenguaje dinámico para todos tus proyectos? No. Pero si que justifican que los lenguajes estáticos añadan opciones para facilitar la programación dinámica. Por ejemplo el dynamic de C# es un paso en esa dirección. Los templates de C++ son otro (los genérics de .NET o de Java ni de lejos).

Entonces… ¿tienes que usar un lenguaje estático para todos tus proyectos? Pues no. Puedes hacer grandes proyectos tanto en lenguajes tipados como en no tipados. Y puedes hacer aberraciones en ambos.

Aunque yo personalmente prefiero un lenguaje estático a uno dinámico, me siento cómodo en estos y a veces cuando estoy en C# si que me gustaría tener toda la flexibilidad que estos me ofrecen. De hecho, cuanto más me he acostrumbrado a JavaScript más echo en falta ciertas cosas en C#. Pero no siempre, no continuamente. Solo “cuando yo quiero”.

Ahora sí, lo que tengo claro es que desarrollar bien en un lenguaje no tipado requiere mayor disciplina que en un lenguaje tipado y que cualquier desarrollador que se precie debería conocer los conceptos de orientación a objetos clásicos (de hecho yo creo que cualquier desarrollador debería aprender C++, pero esa es otra batalla :P).

Bueno… si has llegado hasta aquí… gracias por leer este tostón! Y por supuesto, siéntete libre de dejar un comentario con tu opinión!

Un saludo!

con no comments
Archivado en:

Una de las cosas que se argumentan en contra de JavaScript cuando se habla de orientación a objetos es que no soporta la visibilidad de métodos o propiedades. Es decir, todo es público por defecto.

Mucha gente hoy en día cuando programa en JavaScript adopta alguna convención tipo “lo que empiece por guión bajo es privado y no debe ser invocado”. Como chapuza para ir tirando, pues bueno, pero en JavaScript hay maneras de simular una visibilidad privada y de que realmente el creador de un objeto no pueda invocar algunos métodos. En este post veremos un método rápido y sencillo. Por supuesto no es el único ni tiene porque ser el mejor…

Empecemos por la declaración de una función constructora que me permite crear objetos de tipo Foo:

  1. var Foo = function () {
  2.     this.count = 0;
  3.     this.inc = function() {
  4.         this._addToCount(1);
  5.     };
  6.  
  7.     this._addToCount = function (a) {
  8.         this.count += a;
  9.     };
  10. }
  11.  
  12. var foo = new Foo();
  13. console.log(foo.count);
  14. foo.inc();
  15. // Esto debera ser privado
  16. foo._addToCount(100);
  17. console.log(foo.count);
  18. // count no debera poder accederse
  19. foo.count = 10;
  20. console.log(foo.count);

Estoy usando la convención de que los métodos privados empiezan por un guión bajo. Pero es esto: una convención. Para el lenguaje no hay diferencia. De hecho si ejecuto este código el resultado es el siguiente:

image

El desarrollador que crea un objeto Foo puede acceder tanto a inc, como a addToCount como a count. Como podemos solucionar eso?

La solución pasa por no devolver a quien crea el objeto Foo entero si no un “subobjeto” que tan solo contenga las funciones publicas:

  1. var Foo = function () {
  2.     this.count = 0;
  3.     this.inc = function() {
  4.         this._addToCount(1);
  5.     };
  6.  
  7.     this._addToCount = function (a) {
  8.         this.count += a;
  9.     };
  10.  
  11.     return {
  12.         inc : this.inc
  13.     };
  14. }
  15.  
  16. var foo = new Foo();
  17. console.log(foo);

Si ejecuto este código parece que vamos por el buen camino:

image

Ahora el objeto foo contiene tan solo el método inc. Pero, que ocurre si ¿lo ejecutamos? Pues eso:

image

JavaScript se queja que el método _addToCount no está definido! Que es lo que ha ocurrido? Lo ocurrido tiene que ver con el contexto de JavaScript o el valor de this. El método inc que invocamos es el método inc del objeto anónimo que devolvemos al final de la función constructora de Foo. Dentro de este método el valor de this es el valor del objeto anónimo que, por supuesto, no tiene definido _addToCount. Parece que estamos en un callejón sin salida, verdad?

Aquí es cuando entra en escena la función bind: bind es un función que se llama sobre una función. El resultado de aplicar bind a una función es otra función pero atada permanentemente al contexto que se pasa como parámetro a bind. Dicho de otra manera cuando devolvemos el objeto anónimo, tenemos que modificar el contexto del método inc para que sea el objeto Foo entero. Así modificamos el return para que quede como:

  1. return {
  2.     inc: this.inc.bind(this)
  3. };

Cuando se ejecuta este return el valor the this es el objeto Foo entero así que lo que estamos devolviendo es un objeto anónimo, con una función inc (que realmente es this.inc es decir la función inc del objeto Foo entero), pero que está bindeada a this (el objeto Foo entero), es decir que cuando se ejecute este método inc del objeto anónimo el valor de this no será el objeto anónimo si no el propio objeto Foo.

Con esto hemos terminado! Ahora cuando llamamos a new Foo(), lo que obtenemos es un objeto solo con el método inc. Cuando invocamos inc todo funciona ahora correctamente. Y ya no podemos invocar el método privado _addToCount ni acceder a la propiedad count.

Esto es tan solo un mecanismo, hay varias maneras distintas de hacer lo mismo pero todas se basan en este mismo principio.

Saludos!

PD: Os dejo el código de un método, que he llamado _publicInterface. Dicho método lo que hace es, a partir de un objeto, crear otro objeto que contenga tan solo aquellas funciones que NO empiezan por guión bajo:

  1. this._publicInterface = function() {
  2.     var keys = Object.keys(this);
  3.     var protocol = {};
  4.     for (var idx = 0; idx < keys.length; idx++) {
  5.         var key = keys[idx];
  6.         if (key.charAt(0) !== '_' && typeof (this[key]) === "function") {
  7.             protocol[key] = this[key].bind(this);
  8.         }
  9.     }
  10.  
  11.     return protocol;
  12. };

Así podéis definir en vuestros objetos funciones públicas y privadas (que empiecen por guión bajo) y en el return final hacer: return this._publicInterface();

con no comments
Archivado en:

Muy buenas! Estreno el blog este 2014… dios a finales de Febrero! A ver, si empezamos a retomar el ritmo…

Este es un post sencillito, por si os encontráis con ello. La situación es la siguiente: Tenéis controladores que devuelven vistas parciales, las cuales desde JavaScript incluís dentro de vuestro DOM a través de una llamada Ajax, usando p. ej. el método load de jQuery.

Todo funciona correctamente, hasta que un día el usuario entra en el site, se va a comer y cuando vuelve pulsa uno de esos enlaces (o botones o lo que sea) que incluyen una de esas llamadas Ajax… Y ocurre que en lugar de aparecer la vista parcial, aparece la página de Login allí incrustada.

La razón, obviamente, es que la acción que devuelve la vista parcial está protegida con [Authorize] y al haber caducado la cookie de autorización, este atributo manda un HTTP 401 (no autorizado). Hasta ahí bien. Entonces entra en juego nuestro amigo FormsAuthentication, que “captura” este 401 y lo convierte en un 302 (redirección) que es lo que recibe el navegador. Desde el punto de vista del navegador, lo que llega es un 302 por lo que este, obendientemente, se redirige a la página de Login. Las peticiones Ajax hacen caso del HTTP 302 y por lo tanto el resultado de la redirección (la página de Login) se muestra.

Una alternativa sencilla y rápida para solucionar esto consiste en modificar la petición modificada por FormsAuthentication, de forma que cambiamos todos los 302 que sean resultado de una petición Ajax por un 401 y así revertir lo que FormsAuthentication hace.

  1. protected void Application_EndRequest()
  2. {
  3.     var context = new HttpContextWrapper(this.Context);
  4.     if (FormsAuthentication.IsEnabled && context.Response.StatusCode == 302
  5.         && context.Request.IsAjaxRequest())
  6.     {
  7.         context.Response.Clear();
  8.         context.Response.StatusCode = 401;
  9.     }
  10. }

Con esto convertimos todos los 302 en 401 cuando sean peticiones Ajax y estemos bajo FormsAuthentication. Ojo, que los convertimos todos, incluso aquellos 302 legítimos que podrían haber.

Ahora ya solo queda actualizar nuestro código JavaScript y comprobar que no recibimos un 402 ;)

Postdata 1: .NET Framework 4.5

Si usas .NET Framework 4.5 (VS2012), ya no es necesario que hagas este truco. En su lugar puedes usar la propiedad SuppressFormsAuthenticationRedirect de HttpResponse y ponerla a true. Si el valor de esa propiedad es true, pues FormsAuthentication no convierte los 401 en 302.

Es una propiedad que debes establecer cada vez, por lo que lo puedes hacer de nuevo en el Application_EndRequest de Global.asax si lo deseas.

Si alguien me pregunta porque narices esa propiedad es a nivel de Response (ya me dirás tu porque el objeto Response tiene que “entender” del framework de autorización), pues no lo sé… pero no me termina de gustar, la verdad.

Postdata 2: Katana Cookie Middleware

Ya lo decía el bueno de Andrés Montes: La vida puede ser maravillosa. Si en FormsAuthentication arreglaron esta situación con la propiedad SuppressFormsAuthenticationRequest en el middleware de autenticación por cookies de Katana volvemos a la situación anterior. Y si usas VS2013 o los nuevos templates de ASP.NET en VS2012 no estarás usando FormsAuthentication si no el middleware de Katana.

Por suerte Katana está mejor pensado que FormsAuthentication y podemos configurar mucho mejor el middleware de autenticación basada en cookies.

Buscad donde se configura el middleware basado en cookies de Katana, que por defecto es en el fichero App_Start/StartupAuth.cs y sustutís:

  1. app.UseCookieAuthentication(new CookieAuthenticationOptions
  2. {
  3.     AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  4.     LoginPath = new PathString("/Account/Login")
  5. });

por:

  1. app.UseCookieAuthentication(new CookieAuthenticationOptions
  2. {
  3.     AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  4.     LoginPath = new PathString("/Account/Login"),
  5.     Provider = new CookieAuthenticationProvider
  6.     {
  7.         OnApplyRedirect = ctx =>
  8.         {
  9.             if (!IsAjaxRequest(ctx.Request))
  10.             {
  11.                 ctx.Response.Redirect(ctx.RedirectUri);
  12.             }
  13.         }
  14.     }
  15. });

De esta manera tomamos el control del redirect por 401 y lo hacemos solo si la request no es Ajax. Bueno, bonito y barato.

Ah si! El método IsAjaxResponse… Este método no es el método IsAjaxResponse clásico (ctx.Request es una IOwinRequest) así que os lo tendréis que crear vosotros. Aquí os pongo una implementación:

  1. public static bool IsAjaxRequest(IOwinRequest request)
  2. {
  3.     IReadableStringCollection query = request.Query;
  4.     if (query != null)
  5.     {
  6.         if (query["X-Requested-With"] == "XMLHttpRequest")
  7.         {
  8.             return true;
  9.         }
  10.     }
  11.  
  12.     IHeaderDictionary headers = request.Headers;
  13.     if (headers != null)
  14.     {
  15.         if (headers["X-Requested-With"] == "XMLHttpRequest")
  16.         {
  17.             return true;
  18.         }
  19.     }
  20.     return false;
  21. }

No,  no me deis las gracias… Si el método IsAjaxRequest os funciona las dais al equipo de Katana, ya que está copiado del código fuente de Katana (concretamente de aquí). Si, si… yo también me pregunto porque es privado este método y no un método de extensión público.

En fin… eso es todo! Espero que os sea útil!

Saludos!

Fuentes usadas:

con no comments
Archivado en: ,

Una de las grandes novedades (probablemente la de mayor calado si obviamos la revolución de OWIN) de la nueva versión de ASP.NET es ASP.NET Identity, que sustituye al viejuno Membership (que apareció allá en 2005). ASP.NET Identity está diseñado para dar solución a muchos de los problemas de los que Membsership acaecía.

Una de las preguntas más recurrentes en los foros de la MSDN y fuera de ellos es como usar Membership con un esquema de base de datos propio. Esto básicamente implica crearte un custom memberhip provider lo que, sin ser excesivamente complicado, te hace bueno… derivar de una clase con un porrón de métodos abstractos, la mitad de los cuales puede que ni apliquen en tu caso y que vas a dejar llenos de NotImplementedException. Pesado, feo y además un NotImplementedException siempre demuestra un fallo de diseño en algún punto del sistema (en este caso en el Membership).

ASP.NET Identity ha sido diseñado, entre otras cosas, para solucionar este problema: que los cambios de esquema de la BBDD no impliquen tantos problemas. En este post veremos como adaptar ASP.NET Identity a un esquema de BBDD propio, utilizando, eso sí, el proveedor de ASP.NET Identity para Entity Framework. Y es que otras de las características de ASP.NET Identity es que puede trabajar con varios proveedores de datos (p. ej. se podría hacer un proveedor noSQL para MongoDb) aunque, obviamente (no le pidamos peras al olmo) implementar un proveedor nuevo no es algo trivial. MS ha implementado uno para EF que es el que mucha gente va a utilizar, así que centrémonos en él. Dicho proveedor reside en el paquete Microsoft.AspNet.Identity.EntityFramework y se incorpora por defecto cuando seleccionamos la opción “Individual User Accounts” al crear un proyecto ASP.NET en VS2013.

Si no hacemos nada, el esquema por defecto que se nos crea es el siguiente:

image

Miremos ahora alguna de las tablas, p. ej. AspNetUsers:

image

Bien, veamos ahora como podemos añadir un campo adicional a esta tabla.

Añadiendo un campo adicional a la tabla de usuarios

Honestamente este es la parte fácil (y la que encontrarás en la mayoría de tutoriales sobre ASP.NET Identity). Pero antes de ver como hacerlo déjame contarte un par de cosillas sobre como está montado el proveedor de ASP.NET Identity para EF. Dicho proveedor se basa en EF Code First y en varias clases que implementan las interfaces necesarias para ASP.NET Identity. P. ej. ASP.NET Identity trabaja con la interfaz IUser y el módulo de EF utiliza la clase IdentityUser. La interfaz IUser es muy simple, solo define dos campos (Id y UserName ambos de tipo string). La clase IdentityUser implementa dicha interfaz y añade varios campos más (como PasswordHash y SecurityStamp). Podríamos decir que la tabla AspNetUsers se genera a partir de dicha clase.

Nosotros no podemos modificar dicha clase y añadir campos, así que el proveedor de ASP.NET Identity para EF ha optado por ofrecernos otro mecanismo. Dicho proveedor para trabajar necesita un contexto de EF (es decir un DbContext), pero dicho DbContext debe derivar de IdentityDbContext<T> donde el tipo T derive de IdentityUser. Así pues para añadir un campo simplemente debemos:

  1. Crear una clase X derivada de IdentityUser y añadir el campo necesario.
  2. Derivar de IdentityDbContext<X> donde X es la clase creada en el punto anterior.

El template de VS2013 nos crea una clase llamada ApplicationUser que ya hereda de IdentityUser. Dicha clase está vacía y está pensada para que nosotros añadamos los campos necesarios. También nos crea un contexto de EF (llamado ApplicationDbContext) que hereda de IdentityDbContext<ApplicationUser>. Es decir, nos da casi todo el trabajo hecho :)

Para no ser originales, vamos a hacer el ejemplo clásico que encontrarás en cualquier post: añadir la fecha de nacimiento. Para ello nos basta con añadir dicho campo en la clase ApplicationUser:

  1. public class ApplicationUser : IdentityUser
  2. {
  3.     public DateTime? BirthDay { get; set; }
  4. }

Si ahora ejecutas de nuevo la aplicación, seguramente te dará el siguiente error: The model backing the 'ApplicationDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)

Esto es porque se ha modificado el esquema de datos y ahora tenemos un código que no se corresponde a la BBDD. Tenemos dos opciones: o eliminar la BBDD (y perder todos los datos) o usar Migrations. Para usar Migrations tenemos que habilitarlas primero mediante el Package Manager Console, tecleando Enable-Migrations. Una vez las tengas habilitadas debemos:

  1. Añadir una migración que refleje el cambio realizado en ApplicationUser, usando Add-Migration <nombre_migracion> en el Package Manager Console
  2. Actualizar la BBDD usando Update-Database en el Package Manager Console.

En mi caso cuando he tecleado Add-Migration AddBirthday me ha generado dentro de una carpeta Migrations el archivo de migración. Dicho archivo es una clase C# que le indica a Migrations que hacer. Se genera automáticamente “comparando” el esquema de la BBDD con el esquema que tendría que haber según la clases de EF code first. Para que veas, en mi caso la clase de migración contiene el siguiente código:

  1. public partial class AddBirthday : DbMigration
  2. {
  3.     public override void Up()
  4.     {
  5.         AddColumn("dbo.AspNetUsers", "BirthDay", c => c.DateTime());
  6.     }
  7.         
  8.     public override void Down()
  9.     {
  10.         DropColumn("dbo.AspNetUsers", "BirthDay");
  11.     }
  12. }

Puedes ver que lo que hará es añadir la columna BirthDay. Insisto: dicha clase se genera automáticamente al hacer Add-Migration. Cuando ejecutes Update-Database, se aplicarán los cambios indicados.

Así que bueno… lo único que te queda es esto, ejecutar el comando Update-Database desde el Package Manager Console. Cuando lo apliques verás algo parecido a esto:

image

Listos… Si ahora miras el esquema de la tabla AspNetUsers verás que tenemos ya el nuevo campo:

image

Fácil y sencillo. La verdad, comparado con lo que se tenía que hacer para conseguir lo mismo con el Membership es una maravilla :P

Cambiar el esquema

Vale… añadir campos es sencillo (hemos visto el ejemplo clásico que es la tabla de usuarios, para el resto de tablas es lo mismo pero con otras clases). Pero… y cambiar el esquema? Si quiero que la tabla AspNetUsers se llame simplemente Users y que PasswordHash se llame Pwd p. ej.? Que tenemos que hacer?

Pues bien, eso también es posible gracias al uso de EF Code First. En este caso vamos a tener que tocar el contexto de EF (la clase ApplicationDbContext en el caso de código generado por VS2013), en concreto redefinir el método OnModelCreating:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {
  3.     base.OnModelCreating(modelBuilder);
  4.     modelBuilder.Entity<IdentityUser>().ToTable("Users").
  5.         Property(u => u.PasswordHash).HasColumnName("Pwd");
  6.     modelBuilder.Entity<ApplicationUser>().ToTable("Users").
  7.         Property(u => u.PasswordHash).HasColumnName("Pwd");
  8. }

Y ahora el esquema que tengo en la BBDD es (mantengo mi clase ApplicationUser con la propiedad BirthDay):

image

De nuevo… comparado con lo que se tenía que hacer con el antiguo Membership es una maravilla.

El tipo de cambios que puedes hacer vienen limitados por EF Code First, pero en general estamos ante un modelo mucho más flexible que el anterior Membership.

En resumen, a pesar de que ASP.NET Identity tiene varias cosillas que no me gustan (o mejor dicho, realmente varias cosas que echo en falta) es sin duda un paso adelante respecto al anterior Membership. Me sigue quedando la duda de si lo que ofrece es suficiente para según que tipo de webs, pero si las funcionalidades que ofrece te son suficientes y no te importa ajustar tu esquema a algunos detalles menores (como esos Id de usuario de tipo string) es un avance muy significativo respecto a Membership.

Un saludo a todos y… ¡felices fiestas!

con no comments
Archivado en:

En el post anterior vimos como autenticar una aplicación NancyFx usando oAuth a través del paquete WorldDomination (o SimpleAuthentication que es el aburrido nombre que tiene ahora :p).

Pero dado que NancyFx puede funcionar como un componente OWIN y la estructura modular de OWIN permite que haya módulos de autenticación que se ejecuten antes en el pipeline, porque no “eliminar” toda responsabilidad sobre autenticación de NancyFx? Y que sea algún módulo OWIN el que lo haga no? A fin de cuentas, esa es la gracia de OWIN. En este post vamos a ver como integrar los módulos OWIN de autenticación que tiene Katana con NancyFx. Repasa el post anterior y haz lo siguiente:

  1. Crea una aplicación web vacía y añade los paquetes de Nancy, Nancy.Owin y Microsoft.Host.SystemWeb.
  2. Crea la clase de inicialización OWIN para que use Nancy.
  3. Crea un  módulo que redirija las peticiones a la URL / a una vista que muestre un enlace de login with twitter (que vaya p. ej. a /login/twitter).
  4. Crea otro módulo que redirija las peticiones de /secured a otra vista (basta que muestre un contenido tipo “Esto es seguro”.

Quédate aquí. En este punto puedes tanto acceder a / como a /secured obviamente, y pulsar en enlace “login with twitter” te generará un 404.

Pero ahora estamos listos para empezar.

Nota: Para este post vamos a usar la misma aplicación en twitter (que tenía el callback a /authentication/authenticatecallback. Aunque ahora la URL de callback puede ser la que queramos.

Dejando que Katana hable con Twitter…

Katana incorpora varios componentes de autenticación y hay uno que se encarga precisamente de gestionar el flujo oAuth con twitter. Este componente se llama Microsoft.Owin.Security.Twitter así que añádelo al proyecto. Para entendernos es el equivalente al WorldDomination pero en un mundo OWIN.

Una vez hayas añadido este paquete el primer paso es modificar la clase de inicio de OWIN para añadir el módulo de autenticación por Twitter:

  1. app.UseTwitterAuthentication(new TwitterAuthenticationOptions()
  2. {
  3.     ConsumerKey = "TU COMSUMER KEY",
  4.     ConsumerSecret = "TU CONSUMER SECRET"
  5. });

Por supuesto pon el consumer key y consumer secret de tu aplicacion.

En este punto si pulsas el enlace de “login with twitter” recibirás… un 404 de Nancy. Pues este enlace apunta a /login/twitter (o a la URL que tu hayas elegido, en el fondo da igual) y es una URL que no está enrutada. A diferencia del post anterior donde WorldDomination ya gestionaba la URL “/authentication/redirect/twitter” el módulo de Katana no gestiona ninguna URL. En su lugar “entra en acción” tan buen punto se recibe un 401.

Así que nada, vamos a añadir un  módulo que enrute la URL /login/twitter y devuelva un 401:

  1. public class AuthTwitterModule : NancyModule
  2. {
  3.     public AuthTwitterModule()
  4.     {
  5.         Get["/login/twitter"] = _ => new Response()
  6.         {
  7.             StatusCode = HttpStatusCode.Unauthorized
  8.         };
  9.     }
  10. }

Ahora si navegas a /login/twitter lo que recibirás es… bueno un 401 :P La razón es porque aunque el módulo de Katana entra en acción cuando recibe un 401, no basta solo con el 401. Antes requiere que se rellene el entorno de OWIN con cierta información.

El entorno de OWIN es un diccionario de objetos, literalmente un IDictionary<string, object> que contiene toda la información del pipeline de OWIN. En OWIN no hay objetos tipo Request, Response o HttpContext porque eso implicaría que existe alguna DLL principal de OWIN y OWIN no pretende eso: se basa en tipos de .NET (Hay una sola excepción a este caso y es la interfaz IAppBuilder que está definida en el paquete Owin). Así los módulos OWIN se pasan información entre ellos a través de ese diccionario compartido.

Por lo tanto antes de devolver el 401 debemos meter cierta información en el entorno de OWIN para que el módulo de autenticación de Katana sepa que queremos autenticarnos via Twitter. ¿Qué método hay en NancyFx para meter código antes del código que procesa la petición? Exacto, el module hook Before. Pero en este caso no añadiremos el código directamente en el Before (podríamos) pero lo haremos más reutilizable a través de métodos de extensión (así seria aplicable a más de un módulo).

Pero primero necesitamos un método de extensión que me permita obtener el entorno de OWIN. El paquete Nancy.Owin (que es quien gestiona la integración de NancyFx en OWIN) deja el entorno OWIN dentro de la clave NancyOwinHost.RequestEnvironmentKey del contexto de NancyFx:

  1. public static class NancyContextExtensions
  2. {
  3.     public static OwinContext GetOwinContext(this NancyContext context)
  4.     {
  5.         var environment = (IDictionary<string, object>)context.Items[NancyOwinHost.RequestEnvironmentKey];
  6.         var owinContext = new OwinContext(environment);
  7.         return owinContext;
  8.     }
  9. }

A partir de la información del entorno se crea una variable de tipo OwinContext. La clase OwinContext no es estandard OWIN. Esta clase es una clase de Katana. Así que para que no queden dudas: la integración que estamos haciendo es entre NancyFx y los componentes de Katana. De hecho no es posible una integración universal porque la especificación de OWIN no define el nombre de las claves del entorno que los módulos deben usar, salvo unas cuantas (que podéis encontrar en la especificación de OWIN). Así pues la clase OwinContext no es nada más que el entorno de OWIN, pero visto a través de algo más tipado que un IDictionary<string, object> y que además entiende las claves que usan los módulos de Katana.

Vale, ahora que ya tenemos como obtener el entorno OWIN, vamos a añadir otro método de extensión, pero ahora contra la clase NancyModule:

  1. static class NancyModuleExtensions
  2. {
  3.     public static void Challenge(this NancyModule module, string redirectUri, string userId)
  4.     {
  5.         module.AddBeforeHookOrExecute(ctx =>
  6.         {
  7.             var properties = new AuthenticationProperties() { RedirectUri = redirectUri };
  8.             if (userId != null)
  9.             {
  10.                 properties.Dictionary["XsrfId"] = userId;
  11.             }
  12.             module.Context.GetOwinContext().Authentication.Challenge(properties, "Twitter");
  13.             return null;
  14.         }, "Challenge");
  15.     }
  16. }
Lo que estamos haciendo es añadir código al module hook Before del módulo de NancyFx al que se llame este método (sería lo más parecido a crear un filtro en ASP.NET MVC que hay en Nancy). Básicamente lo que hacemos en el Before es llamar al método Challenge que proporciona Katana que es el que se encarga de todo lo necesario.

Ahora tenemos que modificar el AuthTwitterModule para añadir la llamada a este método de extensión:

  1. public AuthTwitterModule()
  2. {
  3.     this.Challenge("/auth/redirect", null);
  4.     Get["/login/twitter"] = _ => new Response()
  5.     {
  6.         StatusCode = HttpStatusCode.Unauthorized
  7.     };
  8. }

Ahora sí, si navegas a /login/twitter empezará el flujo de oAuth y después serás redirigido a la URL que hemos usado como primer parámetro de la llamada a Challenge, es decir /auth/redirect con independencia del valor de callback que teníamos especificado en la aplicación de twitter.

En esta URL de callback (/auth/redirect) tenemos que recoger los valores que nos haya devuelto el proveedor de oAuth. Para ello nos vamos a apoyar en otro componente de Katana, el paquete Microsoft.AspNet.Identity.Owin, así que añade este paquete ahora. Una vez lo hayas añadido podemos usar el método GetExternalLoginInfoAsync.

Este método es asíncrono, así que lo invocaremos con await. Para poder usar await en NancyFx tenemos que declarar que la ruta que responde a /auth/redirect es asíncrona:

  1. Get["/auth/redirect", true] = async (_, ct) =>
  2. {
  3.     var authManager = Context.GetOwinContext().Authentication;
  4.     var loginInfo = await authManager.GetExternalLoginInfoAsync();
  5.     // ...
  6. };

Este método se encarga de obtener los datos que nos envía el proveedor de oAuth. En este punto podemos recoger los datos del usuario y crear un ClaimsIdentity (esa clase es la nueva clase base de todas las identity en .NET). Lo más normal sería delegar en el Identity Membership para esto, pero, para no saturar, hagámoslo a mano. En el siguiente post veremos como integrarnos con  el Identity Membership. El código podría ser algo como así:

  1. Get["/auth/redirect", true] = async (_, ct) =>
  2. {
  3.     var authManager = Context.GetOwinContext().Authentication;
  4.     var loginInfo = await authManager.GetExternalLoginInfoAsync();
  5.     if (loginInfo != null)
  6.     {
  7.         authManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
  8.  
  9.         var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie.ToString(),
  10.             "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
  11.             "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
  12.         identity.AddClaim(new Claim("Player", "True"));
  13.         identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
  14.             loginInfo.DefaultUserName,
  15.             "http://www.w3.org/2001/XMLSchema#string"));
  16.         authManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);
  17.     }
  18.  
  19.     return new RedirectResponse("/secured");
  20. };

No te preocupes si no entiendes exactamente el código (como digo eso suele delegarse en el Identity Membership), pero básicamente creamos el ClaimsIdentity y le añadimos un nombre de usuario, así como una claim personalizada (Player con valor True).

Al final redirigimos al usuario a la URL /secured, una URL que se supone solo debe poder verse si el usuario no está autenticado.

El código del módulo Nancy que contiene la ruta para dicha URL es el siguiente:

  1. public class SecuredModule : NancyModule
  2. {
  3.     public SecuredModule()
  4.     {
  5.         this.RequiresOwinAuth();
  6.         Get["/secured"] = _ => View["secured.html"];
  7.     }
  8. }

El método RequiresOwinAuth es un método de extensión que nos hemos creado. Dicho método comprueba que existe una ClaimsIdentity en el contexto OWIN:

  1. public static void RequiresOwinAuth(this NancyModule module)
  2. {
  3.     module.AddBeforeHookOrExecute(ctx =>
  4.     {
  5.         var user = ctx.GetOwinUser();
  6.         return user == null || !user.Identity.IsAuthenticated ?
  7.             new Response() {StatusCode = HttpStatusCode.Unauthorized} :
  8.             null;
  9.     }, "OwinUser Not Found");
  10. }

(Este método de extensión sería el equivalente a aplicar [Authorize] en un controlador ASP.NET MVC).

Vale… ya casi lo tenemos, ahora tan solo nos falta configurar el pipeline OWIN para añadir la seguridad por cookies:

  1. app.UseCookieAuthentication(new CookieAuthenticationOptions()
  2. {
  3.     AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
  4. });

Añadimos este código al principio del método de inicialización de OWIN, para que este módulo esté en el principio del pipeline.

Y ¡voilá! hemos terminado. Si nada más iniciar la aplicación navegas a /secured recibirás un 401 (Unauthorized). Si entras en twitter, después de hacer el login verás como se te redirige a /secured y ahora si ves el contenido seguro.

Por supuesto, puedes hacer que en lugar de ver un 401 el usuario sea redirigido a una página de Login, simplemente cambiando la configuración del proveedor de seguridad por cookies:

  1. app.UseCookieAuthentication(new CookieAuthenticationOptions()
  2. {
  3.     AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  4.     LoginPath = new PathString("/login")
  5. });

Ahora si nada más empezar navegas a /secured verás que el usuario es redirigido a /login (en este caso verás un 404 ya que no hay ninguna ruta que responda a la URL /login).

Bueno… en el post anterior vimos como configurar NancyFx junto con WorldDomination para soportar login por oAuth. En este post hemos ido un paso más allá sustituyendo toda la autenticación por componentes OWIN, en lugar de que toda la responsabilidad esté gestionada por NancyFx.

Un saludo!

PD: Tenéis el código en mi carpeta de SkyDrive (fichero NancyKatanaIIS2).

con no comments
Archivado en: ,,,

Buenas! El objetivo de este post es explicar la solución a la que he llegado para integrar autenticación con oAuth en un sitio web desarrollado con NancyFx. En este primer post veremos como hacerlo “de la forma clásica” pero en otro siguiente nos aprovecharemos de los componentes de autenticación de Katana.

Iré hablando en este blog más sobre OWIN, Katana y también sobre NancyFx, pero por el momento algunas definiciones rápidas:

  • OWIN: Especificación que indica como deben comunicarse los servidores web, el middleware web y las aplicaciones web en .NET.
  • Katana: Implementación de varios componentes (hosts, servidores, middlewares varios) OWIN por parte de Microsoft.
  • NancyFx: Un framework basado en el patrón MVC para desarrollar aplicaciones web y APIs REST. Por decirlo de algún modo NancyFx “compite” con ASP.NET MVC y con WebApi a la vez.

Para saber más de OWIN hay un par de posts en mi blog pero también una serie fenomenal del Maestro. Échales un ojo.

Creando una aplicación Nancy ejecutándose en IIS

Vamos a crear una aplicación web, y vamos a usar NancyFx en lugar de ASP.NET, pero usando IIS (IISExpress).

NancyFx viene en dos “sabores”: puede ser un HttpHandler de ASP.NET (así instalas y configuras NancyFx usando el web.config), pero también existe como componente OWIN, lo que permite su uso dentro de cualquier entorno OWIN.

Para usar NancyFx como HttpHandler de ASP.NET debes instalar el paquete Nancy.Hosting.Aspnet que es el que contiene todas las referencias a ASP.NET (NancyFx es totalmente independiente de ASP.NET). Ahora vamos a crear una aplicación web ASP.NET, así que lo lógico parece ser usar NancyFx como HttpHandler y listos. En muchos casos puede ser lo más lógico, pero no es lo que vamos a hacer. No vamos a usar NancyFx como HttpHandler de ASP.NET. En su lugar lo vamos a utilizar como componente OWIN y, para ello, nos aprovecharemos de un componente de Katana que permite usar componentes OWIN dentro del pipeline de ASP.NET (en terminología OWIN diríamos que este componente de Katana implementa un Host y un servidor Web OWIN). Esto parece dar muchas vueltas, y en muchos casos así puede ser, pero tiene las siguientes ventajas:

  1. Dado que nuestra aplicación será OWIN en cualquier momento podremos abandonar el cómodo regazo de IIS e irnos a cualquier otro host o servidor OWIN.
  2. Incluso aunque no tengamos pensado divorciarnos de IIS, podremos utilizar cualquier componente OWIN que haya por ahí ;)

Como digo, para poder utilizar NancyFx como componente OWIN pero ejecutándose en un pipeline de ASP.NET necesitamos la ayuda de Katana, en concreto del componente Microsoft.Owin.Host.SystemWeb.

Así, los pasos para usar NancyFx como componente OWIN dentro del pipeline de ASP.NET son los siguientes. Antes que nada crea un proyecto de tipo ASP.NET y selecciona el template “Empty”:

image

Luego instala los siguientes paquetes de NuGet:

  • Nancy (el core de NancyFx)
  • Nancy.Owin (Para usar Nancy como módulo OWIN)
  • Microsoft.Owin.Host.SystemWeb (Para usar módulos OWIN en el pipeline de ASP.NET)

Con esto ya tenemos el esqueleto necesario.

El siguiente paso es crear la clase de inicialización de OWIN. Para ello agrega una clase normal y llámala Startup:

Clase de inicializacion OWIN
  1.     public class Startup
  2.     {
  3.         public void Configuration(IAppBuilder app)
  4.         {
  5.             app.UseNancy();
  6.         }
  7.     }

Es importante que la clase se llame Startup. Esto es una convención que sigue Katana (pues quien inicializa los módulos OWIN es Katana a través del paquete Microsoft.Owin.Host.SystemWeb). Si nuestra clase tiene otro nombre recibirás un error como el siguiente:

image

Una solución es o bien renombrar la clase para que se llame Startup o en el caso de que no quieras hacerlo usar el atributo OwinStartupAttribute:

  1. [assembly: OwinStartup(typeof(MyCustomStartup))]

Si ahora ejecutas la aplicación deberías ver algo parecido a lo siguiente:

image

Eso significa que Nancy está funcionando correctamente (este 404 ha sido servido por Nancy). ¡Felicidades! Ya tienes a Nancy corriendo bajo IIS.

Vamos ahora a crear un módulo de Nancy (más o menos equivalente a un controlador de ASP.NET MVC), para gestionar la llamada a la URL / y mostrar una vista con solo el enlace “Login with twitter”. Añade una clase a tu proyecto con el siguiente código:

Nancy MainModule
  1. public class MainModule : NancyModule
  2. {
  3.     public MainModule()
  4.     {
  5.         Get["/"] = _ => View["main.html"];
  6.     }
  7. }

Con esto has creado un módulo de NancyFx y has enrutado todas las peticiones que vayan a la URL “/” para que muestren la vista main.html. Añade pues un archivo main.html (no es necesario que esté en ninguna carpeta Views ni nada) que muestre un enlace a la URL “/authentication/redirect/twitter”.

Si ahora ejecutas el proyecto deberías se tendría que ver el contenido del fichero main.html. Y si pulsas sobre el enlace tienes que recibir el 404 de Nancy.

Perfecto! Ya tenemos el esqueleto base de la aplicación. Ahora vayamos a integrar la autenticación por twitter.

Integrando oAuth con NancyFx a la manera clásica

Nota: El contenido de este apartado presupone que tienes creada una aplicación en twitter y que por lo tanto tienes un consumer key y un consumer secret. También debes configurar la aplicación twitter para que la URL de callback sea /authentication/authenticatecallback">http://localhost:<puerto>/authentication/authenticatecallback

Nota 2: Twitter (y otros proveedores oAuth) no dejan dar de alta aplicaciones cuyo callback sea una dirección de localhost. En este caso lo más rápido es usar el dominio xxx.localtest.me (usa cualquier valor para xxx). El dominio localtest.me está especialmente pensado para estos casos: cualquier subdominio en localtest.me resuelve a 127.0.0.1 ;)

NancyFx es un framework muy modular, y no tiene incorporado el concepto de autenticación o autorización. Eso significa que no hay por defecto ninguna API ni nada que nos diga si el usuario está autenticado o bien poder autenticarlo. Todo eso se deja a implementaciones “externas” al core de NancyFx.

P. ej. si queremos autenticar nuestra aplicación basándonos en cookies (lo que en el mundo ASP.NET conocemos como autenticación por forms) debemos instalar el paquete Nancy.Authentication.Forms. Hay otros paquetes para otros tipos de autenticación (como puede ser Nancy.Authentication.Basic para autenticación básica de HTTP o bien Nancy.Authentication.Stateless para basar la autenticación en alguna cabecera específica de la petición). Todos estos paquetes (y más que hay para otros tipos de autenticación) se basan en el modelo de extensibilidad de NancyFx.

Por supuesto hay un paquete para integrarnos con oAuth que antes respondía al interesante nombre de Nancy.Authentication.WorldDomination pero que ahora tiene el aburrido y anodino nombre de Nancy.SimpleAuthentication. Así que añade este paquete a tu proyecto y ya estarás listo para iniciar un flujo para autenticación.

Al añadir este paquete tu web.config se habrá modificado y se habrán añadido las siguientes líneas:

Code Snippet
  1.   <configSections>
  2.     <section name="authenticationProviders" type="SimpleAuthentication.Core.Config.ProviderConfiguration, SimpleAuthentication.Core" />
  3.   </configSections>
  4.   <authenticationProviders>
  5.     <providers>
  6.       <add name="Facebook" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  7.       <add name="Google" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  8.       <add name="Twitter" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  9.       <add name="WindowsLive" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  10.     </providers>
  11.   </authenticationProviders>

En nuestro caso podemos dejar solo la información del provider de Twitter y debeis rellenar el valor de consumer key y el consumer sectret que os provee Twitter. Acuérdate tambien de mover el tag <configSections> para que sea el primero dentro del <configuration> en el web.config, si no IIS os dará un error!

Ahora el siguiente paso es crear una clase que implemente IAuthenticationCallbackProvider:

Code Snippet
  1. public class MyCustomAuthCallbackProvider : IAuthenticationCallbackProvider
  2. {
  3.     public dynamic Process(NancyModule nancyModule, AuthenticateCallbackData model)
  4.     {
  5.         // En caso de autenticacin OK
  6.     }
  7.  
  8.     public dynamic OnRedirectToAuthenticationProviderError(NancyModule nancyModule, string errorMessage)
  9.     {
  10.         // En caso de error
  11.     }
  12. }

Ejecuta de nuevo tu aplicación. Ahora si pulsas sobre el enlace de login with twitter… ¡deberías ver la página de Twitter!:

image

Una vez el usuario haya dado sus datos y haya autorizado a tu aplicación entonces se ejecuta el método Process de la clase que acabamos de crear. En el parámetro “model” tendrás toda la información necesaria:

image

Una vez sabes cual es el usuario autenticado debes autenticarlo en tu propia web. Es decir Twitter nos ha dado el OK, pero ahora lo que nos falta es utilizar algún mecanismo para autenticar todas las peticiones que realice este usuario en nuestra web.

Nota: Para que todo funcione recuerda que la URL de callback de la aplicación en twitter debe apuntar a /authentication/authenticatecallback y que el enlace de “Login with twitter” debe apuntar a /authentication/redirect/twitter. Esas dos URLs son gestionadas automáticamente por el paquete WorldDomination.

Si queremos utilizar una cookie, tenemos que instalar el paquete Nancy.Authentication.Forms, así… que nada, a por él!

Añadiendo la seguridad por Forms

Una vez tenemos el paquete Nancy.Authentication.Forms añadido ya podemos configurarlo. Son necesarios 3 pasos para que todo funcione correctamente.

El primero es crear una clase que implemente la interfaz IUserIdentity. Esta interfaz es toda la información que el core de Nancy mantiene sobre un usuario autenticado (nombre y permisos):

  1. public class AuthenticatedUser : IUserIdentity
  2. {
  3.     public string UserName { get; set; }
  4.     public IEnumerable<string> Claims { get; set; }
  5. }

El segundo es crear un “User Mapper”. Esto vendría a ser el equivalente (solo lectura) del Membership Provider en ASP.NET. Es decir el encargado de obtener los datos del usuario del repositorio donde se guarden:

  1. public class MyUserMapper : IUserMapper
  2. {
  3.  
  4.     public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
  5.     {
  6.         // Obtendriamos el usuario de la BBDD
  7.         return new AuthenticatedUser()
  8.         {
  9.             UserName = "eiximenis",
  10.             Claims = new[] {"Read", "Write", "Admin"}
  11.         };
  12.     }
  13. }

En Nancy todos los usuarios se identifican por un Guid (por supuesto esto no significa que en la BBDD este Guid tenga que ser la PK de la tabla de usuarios!).

El tercer y último paso es indicar al core de Nancy que queremos autenticación por formularios:

  1. public class Bootstrapper : DefaultNancyBootstrapper
  2. {
  3.     protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
  4.     {
  5.         base.RequestStartup(container, pipelines, context);
  6.         var formsAuthConfiguration = new FormsAuthenticationConfiguration
  7.         {
  8.             DisableRedirect = true,
  9.             UserMapper = new MyUserMapper()
  10.         };
  11.         FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
  12.     }
  13. }

El Bootstrapper es la clase que inicializa todo el core de Nancy. Hasta ahora no teníamos, así que lo añadimos y listos (no hay que indicar en ningún sitio cual es nuestro Bootstrapper, se descubre automáticamente). Con esto ya tenemos la autenticación por forms habilitada en nuestro proyecto.

Volvamos ahora a nuestro CallbackProvider. Lo que haríamos en el método Process es:

  • Consultar la BBDD de usuarios para encontrar el usuario que se corresponda con los datos que nos ha devuelto twitter.
  • En caso de no existir crearlo y obtener su Guid.
  • En caso de existir obtener su Guid.
  • Una vez tenemos el Guid llamar al método LoginAndRedirect. Este método vendría a ser el equivalente al SetAuthCookie de ASP.NET:
  1. public dynamic Process(NancyModule nancyModule, AuthenticateCallbackData model)
  2. {
  3.     // Accederiamos a BBDD para obtener el GUID del usuario
  4.     // O si no lo crearamos
  5.     var userGuid = Guid.NewGuid();
  6.     return nancyModule.LoginAndRedirect(userGuid);
  7. }

¡Y listos! ¡Has terminado!

Añadir contenido securizado

Vamos a añadir un módulo a Nancy que solo se ejecute si el usuario está autenticado. En ASP.NET MVC meterías un [Authorize] en la acción del controlador. En Nancy lo equivalente es usar el module hook Before. Cada modulo de Nancy tiene una propiedad Before en la que puedes poner código que se ejecuta antes de que se ejecute cualquier otro código del módulo (es decir, el código correspondiente a la petición). Desde este código puedes verificar si el usuario está autenticado:

  1. public class SecureModule : NancyModule
  2. {
  3.     public SecureModule()
  4.     {
  5.         Before += ctx =>
  6.         {
  7.             if (ctx.CurrentUser == null)
  8.                 return new Response()
  9.                 {
  10.                     StatusCode = HttpStatusCode.Unauthorized
  11.                 };
  12.             return null;
  13.         };
  14.  
  15.         Get["/Secure"] = _ => View["secure.html",
  16.             new
  17.             {
  18.                 Name = Context.CurrentUser.UserName
  19.             }];
  20.     }
  21. }

Si la propiedad del CurrentUser del contexto es igual a null devolvemos un 401. En caso contrario no hacemos nada y dejamos que siga el pipeline de Nancy (por lo que se ejecutará el código que toque según la URL).

Para terminar añade el archivo secure.html:

  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4.     <title></title>
  5. </head>
  6. <body>
  7.     Hello @Model.Name
  8. </body>
  9. </html>

(Aunque lo parezca esto NO es Razor. Es el view engine por defecto de Nancy que se llama SSVE (Super Simple View Engine)).

Ya estamos listos para probar! Ejecuta el proyecto y navega a /secure. Deberías ver una página sin nada, pero un vistazo a la pestaña Network de las developer tools nos informa que estamos teniendo un 401:

image

Pefecto. Vuelve a la raíz, y autentícate por twitter. Después del proceso de autenticación volverás a la raiz. Navega a /secure de nuevo y ahora deberías ver la vista secure.html:

image

(Recuerda que todos los usuarios se llamarán eiximenis ya que nuestro IUserMapper siempre devuelve lo mismo ;))

Y hemos finalizado! Ya tienes tu web con Nancy autenticada mediante oAuth. Hemos elegido twitter pero para el resto de proveedores no hay muchas diferencias (el mérito es todo de WorldDomination).

Unas palabras finales

Hemos utilizado Katana para usar NancyFx en “modo OWIN” bajo el pipeline de ASP.NET en IIS. Esto nos permitiría integrarnos con otro middleware OWIN. Así pues, y esta es precisamente la idea de OWIN, podría usar un middleware OWIN para autenticar y autorizar las peticiones. Es decir, no delegar la autenticación y la autorización en el propio NancyFx si no tener un módulo OWIN encargado precisamente de esto…

… en el siguiente post veremos como ;)

Saludos!

PD: Tienes el proyecto completo (VS2013) en http://sdrv.ms/18brcO7 (Archivo NancyKatanaIIS.zip)

con 1 comment(s)
Archivado en: ,,,

Buenas! Este post es para describir un fallo que he encontrado en el helper Html.DropDownFor y el workaround asociado. Quizá alguien entiende que no es un fallo y quizá es capaz de decirme que razón se esconde bajo este comportamiento… Desde mi punto de vista ninguno, pero bueno… ni tengo (ni pretendo tener) la verdad absoluta.

El problema…

Veamos… Para el helper Html.DropDownFor se usa para crear combos y tiene varias formas de uso (yo mismo escribí un post hace algún tiempo al respecto sobre las combos en ASP.NET MVC). En una de sus formas de uso, podemos mostrar una lista de cervezas y guardar la cerveza seleccionada con el siguiente código:

Clase Beer
  1.     public class Beer
  2.     {
  3.         public int Id { get; set; }
  4.         public string Name { get; set; }
  5.     }

En el controlador tenemos una lista de cervezas (_beers) y un par de acciones para mandar una SelectList con esas cervezas.

Acciones del controlador
  1.         public ActionResult Test()
  2.         {
  3.             ViewBag.Beers = new SelectList(_beers, "Id", "Name", 2);
  4.             return View();
  5.         }
  6.  
  7.         [HttpPost]
  8.         public ActionResult Test(BeerSelectViewModel data)
  9.         {
  10.             ViewBag.Beers = new SelectList(_beers, "Id", "Name", data.SelectedBeerId);
  11.             return View();
  12.         }

La vista y el método que gestionan el POST usan un ViewModel para mantener el ID de la cerveza seleccionada:

ViewModel
  1.     public class BeerSelectViewModel
  2.     {
  3.         public int SelectedBeerId { get; set; }
  4.     }

El código de la vista es sencillo:

Vista Test.cshtml
  1. @model WebApplication3.Controllers.BeerSelectViewModel
  2.  
  3. @using (Html.BeginForm())
  4. {
  5.     @Html.DropDownListFor(m => m.SelectedBeerId, ViewBag.Beers as SelectList)
  6.     <p>
  7.         <input type="submit" class="btn-default" value="submit" />
  8.     </p>
  9. }

Fijaos que usamos el constructor de SelectList que acepta el objeto seleccionado (en este caso el ID de la cerveza seleccionada). La primera vez se usa el 2, de forma que Epidor será la cerveza seleccionada por defecto, la primera vez.

Ahora extendamos a que el usuario pueda seleccionar no una, si no DOS cervezas seleccionadas (con dos combos).

Para ello hacemos los siguientes cambios (que al menos a mi me parecen lógicos). Extendemos el ViewModel para que tenga un array de elementos seleccionados:

Nuevo ViewModel
  1. public class BeerSelectViewModel
  2.   {
  3.       public IEnumerable<int> SelectedBeerIds { get; set; }
  4.   }

En el controlador pasamos en el ViewBag la lista de cervezas (en lugar del SelectList), ya que el SelectList lo construiremos en la vista:

Acciones del controlador
  1. public ActionResult Test()
  2.   {
  3.       ViewBag.Beers = _beers;
  4.       var model = new BeerSelectViewModel()
  5.       {
  6.           SelectedBeerIds = new[] {1, 2}
  7.       };
  8.       return View(model);
  9.   }
  10.  
  11.   [HttpPost]
  12.   public ActionResult Test(BeerSelectViewModel data)
  13.   {
  14.       ViewBag.Beers = _beers;
  15.       return View(data);
  16.   }

En la vista iteramos sobre la propiedad SelectedBeersIds y por cada valor construimos una SelectList cuyo cuarto parámetro (elemento seleccionado) sea el ID por el que estamos iterando:

Vista Test.cshtml
  1. @using WebApplication3.Controllers
  2. @model WebApplication3.Controllers.BeerSelectViewModel
  3. @{
  4.     var beers = ViewBag.Beers as IEnumerable<Beer>;
  5. }
  6.  
  7. @using (Html.BeginForm())
  8. {
  9.     foreach (var id in Model.SelectedBeerIds)
  10.     {
  11.         <p>
  12.             @Html.DropDownListFor(m => m.SelectedBeerIds, new SelectList(beers, "Id", "Name", id))
  13.         </p>
  14.     }
  15.     <p>
  16.         <input type="submit" class="btn-default" value="submit" />
  17.     </p>
  18. }

Recordad eso: Estoy indicando a cada Html.DropDownListFor cual es su elemento seleccionado a través del cuarto parámetro de la SelectList que le asocio. Eso debería funcionar… pero no. NO FUNCIONA. En el código HTML generado ningún tag <option> tiene el atributo selected, así que ambas combos muestran el primer elemento… Ahí está el fallo. Le digo a Html.DropDownFor cual debe ser su elemento seleccionado pero el helper hace caso omiso a esta indicación…

… Y la solución

Después de dar vueltas al asunto, llegué a una solución… Primero lo intenté sin usar el helper Html.DropDownFor y usar tan solo Html.DropDown pero el error era el mismo. Al final la solución que encontré fue usar Html.DropDownFor pero contra otra propiedad del ViewModel. Es decir usar una propiedad (SelectedBeerIds para rellenar el elemento seleccionado de las combos y otra propiedad (SelectedBeerIdsNew) para obtener el valor de vuelta (los nuevos elementos seleccionados). Pero ojo, si desde el el método que gestiona el POST debía devolver de nuevo la vista (p. ej. en el caso de que el ModelState no sea válido) entonces debía hacer lo siguiente:

  • Copiar el valor de la propiedad SelectedBeerIdsNew en SelectedBeerIds
  • Eliminar (poner a null) el valor de SelectedBeerIdsNew
  • Eliminar del ModelState la propiedad SelectedBeerIdsNew.

Si no hacemos las dos últimas cosas las combos no respetarán el elemento seleccionado que les pasamos en el SelectList (si no hacemos la primera nos mostrarán los elementos seleccionados anteriores).

El código en el controlador es:

Acciones Controlador
  1. public ActionResult Test()
  2. {
  3.     ViewBag.Beers = _beers;
  4.     var model = new BeerSelectViewModel()
  5.     {
  6.         SelectedBeerIds = new[] {1, 2}
  7.     };
  8.     return View(model);
  9. }
  10.  
  11. [HttpPost]
  12. public ActionResult Test(BeerSelectViewModel data)
  13. {
  14.     ViewBag.Beers = _beers;
  15.     // En este punto en data.SelectedBeerIdsNew tenemos
  16.     // las nuevas cervezas seleccionadas
  17.     data.SelectedBeerIds = new List<int>(data.SelectedBeerIdsNew);
  18.     data.SelectedBeerIdsNew = null;
  19.     ModelState.Remove("SelectedBeerIdsNew");
  20.     return View(data);
  21. }

Fíjate en el código necesario en el método que gestiona el POST. Si no establecemos SelectedBeerIdsNew a null y no eliminamos la clave SelectedBeerIdsNew del ModelState no funciona.

El resto de código es igual excepto que en la vista el Html.DropDownFor es para la propiedad SelectedBeerIdsNew (aunque iteramos sobre SelectedBeerIds):

Codigo de la vista
  1. @using WebApplication3.Controllers
  2. @model WebApplication3.Controllers.BeerSelectViewModel
  3. @{
  4.     var beers = ViewBag.Beers as IEnumerable<Beer>;
  5. }
  6.  
  7. @using (Html.BeginForm())
  8. {
  9.     foreach (var id in Model.SelectedBeerIds)
  10.     {
  11.         <p>
  12.             @Html.DropDownListFor(m => m.SelectedBeerIdsNew, new SelectList(beers, "Id", "Name", id))
  13.         </p>
  14.     }
  15.     <p>
  16.         <input type="submit" class="btn-default" value="submit" />
  17.     </p>
  18. }

Y esto es mas o menos todo… En mi opinión es un bug, porque insisto: en todo momento uso la sobrecarga de SelectList que le indica el elemento seleccionado. Si no la usase entendería el comportamiento (hasta sería lógico), pero la estoy usando. No entiendo porque no hace caso de lo que le indica el SelectList en este caso.

¿Qué opináis vosotros?

Saludos!

con 1 comment(s)
Archivado en:

Si vienes de un lenguaje orientado a objetos “clásico” como C# o Java, tendrás claro el concepto de this: Se refiere al propio objeto. Dado que todos los objetos son instancias de una clase en concreto y el código se define a nivel de clase el valor de this está claramente definido. Sólo leyendo el código puedes saber a que se refiere this en cada momento.

JavaScript también tiene la palabra this pero su significado es un poco más “complejo” que el de C#.

Antes que nada mencionar que this en JavaScript NO es opcional. Siempre que quieras acceder a una propiedad de this, debes usarla. Si no usas this, JavaScript asume siempre que estás accediendo a una variable local (o global).

El valor de this en JavaScript es parecido al de C#, pero a la vez es tan distinto que lo normal es que alguien que venga de C# termine sin entender nada.

Empecemos diciendo que el valor de this dentro de una función es el valor del objeto en el que se define esta función. Veamos un ejemplo:

  1. var persona = {
  2.     name: 'edu',
  3.     twitter: 'eiximenis',
  4.     twitterUrl: 'http://twitter.com/' + this.twitter
  5. };
  6.  
  7. console.log(persona.twitterUrl);

Este código lo que imprime por pantalla es… http://twitter.com/undefined

Pero… a ver: No valía this el valor del objeto sobre el que estaba definido el código? No está twitterUrl definido dentro del objeto persona? Qué narices ocurre?

Sencillo: Si relees mi definición de this, verás que empezaba diciendo “el valor de this dentro de una función…”. La propiedad twitterUrl no es una función, así que eso no se aplica. En este caso el valor de this es el contexto en el cual se esté ejecutando este código, que por defecto es el contexto global (window en los navegadores).

Para solucionarlo, debemos convertir twitterUrl a una función:

  1. var persona = {
  2.     name: 'edu',
  3.     twitter: 'eiximenis',
  4.     twitterUrl: function () {
  5.         return 'http://twitter.com/' + this.twitter;
  6.     }
  7. };
  8.  
  9. console.log(persona.twitterUrl());

Con este cambio, ahora si que el código imprime la respuesta esperada.

Funciones constructoras

Si leíste mi post sobre si JavaScript era orientado a objetos, ya sabrás que llamo función constructora a aquella función que se usa para crear objetos que compartan un mismo prototipo. Las funciones constructoras se invocan con new y es precisamente el uso de new lo que convierte a una función en constructora, no la función en sí.

El siguiente código también muestra el valor esperado:

Code Snippet
  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return 'http://twitter.com/' + this.twitter;
  6.     }
  7. };
  8.  
  9. var persona = new Persona('edu','eiximenis');
  10. console.log(persona.twitterUrl());

Insisto: Es el uso de new el que hace que this sea el objeto que se está creando. ¿Que pasaría si me olvidase el new al llamar a la función Persona?

  1. var foo = Persona('edu', 'eiximenis');
  2. console.log(foo.name);
  3. console.log(name);

Ahora llamo a la función Persona pero sin usar new. Ahora el valor de this dentro de Persona ya NO es el objeto que se está creando. Ahora el valor de this dentro de persona es el valor del objeto dentro del cual la función Persona está definido… Como Persona es una función global, pues ahora this es el contexto global.

De hecho el primer console.log dará error porque foo ahora vale undefined (¡la función Persona no devuelve nada!) y el segundo console.log mostrará “edu” porque la llamada a Persona ha creado la variable “name” en el contexto global.

Recuerda: No hay clases en JavaScript. Si quieres crear objetos a través de una función constructora asegúrate de usar siempre new. Si no obtendrás resultados imprevistos. Es el uso de new el que convierte una función en constructora.

Call / Apply

Vamos a ponernos un poco más serios. Este ejemplo anterior muestra una característica fundamental de this: Su valor depende de como se llame a una función. Hemos visto que dentro de Persona el valor de this dependía de si llamábamos a la función con new o sin new.

Pero oye… ya que el valor de this dentro de una función depende de como esta sea llamada, porque no explotar al máximo esta capacidad y permitir que el valor de this sea el que el desarrollador quiera? Hasta ahora hemos visto que el valor de this podía ser:

  1. El objeto dentro del cual estaba definida la función
  2. Un objeto nuevo que se crea en aquel momento (al usar new).

Pues bien call y apply añaden un tercer escenario: el valor de this puede ser cualquier objeto que el desarrollador desee… Veamos un ejemplo:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return 'http://twitter.com/' + this.twitter;
  6.     }
  7. };
  8.  
  9. var p = {
  10.     blog:'http://geeks.ms/blogs/etomas'
  11. };
  12.  
  13. Persona.call(p, 'edu','eiximenis');
  14. console.log(p.twitterUrl());
  15. console.log(p.blog);

La función Persona es exactamente la misma que teníamos antes. Luego definimos un objeto p con una propiedad blog. Y en la línea 13 tenemos la clave: Usamos call para llamar a la función Persona. La función call permite especificar el valor de this dentro de una función. El primer parámetro de call es el valor que tomará this, mientras que el resto de parámetros son los que se pasan a la función.

Por lo tanto, como el primer parámetro de Persona.call es el objeto p, el valor de this dentro de Persona será el objeto p… por lo tanto las propiedades name, twitter y twitterUrl serán añadidas al objeto p.

Apply hace exactamente lo mismo que call, salvo que los parámetros que se mandan a la función se especifican en un array:

  1. Persona.apply(p, ['edu','eiximenis']);

¿P: ej. no te has preguntado nunca porque si te suscribes usando jQuery a un evento, puedes usar “this” dentro de la función gestora del evento para acceder al elemento del DOM que ha lanzado el evento? Pues ahí tienes la respuesta: Los que crearon jQuery conocen call y apply ;)

El problema de la pérdida de this

Este es un problema bastante molesto y difícil de detectar si no lo conoces y se entiende mejor con un ejemplo:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return (function () {
  6.             return 'http://twitter.com/' + this.twitter;
  7.         }());
  8.     }
  9. };
  10.  
  11. var p = new Persona('edu', 'eiximenis');
  12. console.log(p.twitterUrl());

El ejemplo es un pelín forzado pero ilustra la problemática de la pérdida de this.

Primero, que sepas que este código imprime http://twitter.com/undefined en lugar de http://twitter.com/eiximenis como sería de esperar. Si entiendes porque entenderás la problemática de pérdida de this.

La propiedad twitterUrl sigue siendo una función, así que… ¿porque ahora this.twitter no tiene valor? Pues porque ahora estamos accediendo a this dentro de una función que está dentro de una función. Fíjate que ahora twitterUrl es una función que define internamente a otra función anónima, la invoca y devuelve su resultado. La función interna NO está definida dentro del mismo objeto en el cual se define la propiedad twitterUrl y por lo tanto el valor de this dentro de la función interna NO es el propio objeto…  De hecho en este caso this es el contexto global. Y claro el contexto global no tiene definida ninguna propiedad twitter.

Como digo el ejemplo está muy forzado porque nadie haría eso… pero tener una función dentro de otra función es muy habitual en el caso de callbacks, así que te terminarás encontrando con este caso. Y cuando te encuentres con él… te acordarás de mi! :P

Hay dos soluciones para esto:

  1. Hacer que el valor de this dentro de la función interna sea el correcto (a través de call/apply). Algunas librerías de javascript se encargan de ello con las funciones de callback que gestionan ellos.
  2. Guardar el valor de this en una variable.

Veamos como quedaría el código para el primer caso:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return (function () {
  6.             return 'http://twitter.com/' + this.twitter;
  7.         }.apply(this));
  8.     }
  9. };
  10.  
  11. var p = new Persona('edu', 'eiximenis');
  12. console.log(p.twitterUrl());

Llamadme friki, pero a mi esto me encanta… Como puedes ver la única diferencia que hay respecto al código anterior es que ahora invocamos a la función anónima interna usando apply. Da igual que la función sea anónima y la defina justo en este lugar: puedo usar apply igualmente… Una maravilla. Si vienes de C# igual te cuesta entender esto, pero ten presente que en JavaScript las funciones son ciudadanas de primer orden. No necesitamos artificios raros como los delegates para poder pasar funciones como parámetro. Y las funciones tienen un prototipo común (de hecho apply está definido en el prototipo de Function).

El código para la segunda opción (guardar el valor de this) sería el siguiente:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl = function () {
  5.         var self = this;
  6.         return (function () {
  7.             return 'http://twitter.com/' + self.twitter;
  8.         }());
  9.     }
  10. };
  11.  
  12. var p = new Persona('edu', 'eiximenis');
  13. console.log(p.twitterUrl());

El código sigue siendo muy parecido con la salvedad de que ahora en la línea 5 guardamos el valor de this en una variable llamada self. En la línea 5 el valor de this es el objeto que queremos (pues estamos dentro de la funcion twitterUrl y no dentro de la función interna). Y dentro de la función interna (línea 7) no usamos this si no la variable anterior (self). Es una convención usar los nombres that o self para esta variable que guarda el valor de this.

Bind

Bueno… vamos a ponernos ya en plan superlativo. Si creías que ya lo habíamos visto todo sobre JavaScript y this, todavía nos queda una sorpresa más: La función bind.

Bind es muy similar a call o apply, pero en lugar de devolver el resultado de la función que se llama, devuelve… otra función. ¿Y qué es esta otra función? Pues es la función original pero con el valor this por defecto establecido siempre al valor del parámetro pasado a bind.

Veamos un ejemplo:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4. };
  5.  
  6. function getTwitterUri() {
  7.     return 'http://twitter.com/' + this.twitter;
  8. }
  9.  
  10. var p = new Persona('edu', 'eiximenis');
  11. console.log(getTwitterUri.apply(p));
  12.  
  13. var ftw = getTwitterUri.bind(p);
  14. console.log(ftw());

¿Cual es la salida de este código? ¡Exacto! Se imprime dos veces ‘http://twitter.com/eiximenis’:

  1. La primera vez es porque usamos apply para invocar a la función getTwitterUri con el objeto p, por lo que el valor de this dentro de getTwitterUri es el objeto p.
  2. La segunda es porque invocamos la funcion ftw. La función ftw es devuelta por la llamada a getTwitterUri.bind por lo que al invocar la función ftw el valor de this es el propio objeto p.

Ahora añadamos las siguientes dos líneas más de código:

  1. console.log(getTwitterUri.apply({ twitter: 'msdev_es' }));
  2. console.log(ftw.apply({ twitter: 'msdev_es' }));

¿Qué crees que imprimen esas dos líneas de código adicionales?

Si has respondido que ambas van a imprimir ‘http://twitter.com/msdev_es’ déjame darte una buena noticia y una mala:

  • La buena es que has entendido como funcionan call y apply! :)
  • La mala es que la respuesta no es correcta :P

La primera línea SI que imprime ‘http://twitter.com/msdev_es’, ya que llamamos a getTwitterUri usando apply y pasándole un objeto cuya propiedad twitter es ‘msdev_es’…

… Pero la segunda línea NO imprime ‘http://twitter.com/msdev_es’. Debería hacerlo si ftw fuese una funcion “normal”. Pero ftw es una función obtenida a través de bind, por lo que sigue conservando su valor de this con independencia de call y apply.

Así que ya ves… Para saber el valor de this que usa una función ya ni te basta con mirar si es invocada usando call y apply: debes además saber si esta función ha sido generada a través de bind o no :P

En fin… Creo que más o menos con esto cubrimos todos los aspectos de this en JavaScript. Como puedes ver, aunque su significado es parecido al de C# realmente es tan distinto que lo más normal es que si asumes que el this de JavaScript funciona como el de C# termines por no entender nada y maldecir a JavaScript… ;)

Un saludo! ;-)

con no comments
Archivado en:

Ayer tuve el placer de participar en un hangout de #JsIO junto a dos bestias pardas como Erick Ruiz y Tomás Corral discutiendo si JavaScript es o no es un lenguaje orientado a objetos.

Así que aprovecho para escribir este post y hacer algunas reflexiones más al respecto sin las prisas ni la improvisación del “directo”.

¿Qué es la orientación a objetos?

Es complicado definir que significa exactamente “orientación a objetos”. Seguro que si le preguntas a varia gente distinta obtendrás diferentes respuestas. Con puntos coincidentes, pero también puntos divergentes. Esto se debe a que el concepto es lo suficientemente vago como para admitir varias interpretaciones y además de que la gran mayoría de lenguajes no son 100% orientados a objetos.

Si yo hubiese de definir orientación a objetos diría básicamente que se trata de modelar el problema, y por lo tanto de diseñar un programa, basándose en la interacción entre distintos objetos que tienen determinadas propiedades y con los cuales se pueden realizar determinadas acciones. Los objetos tienen tres propiedades fundamentales:

  1. Comportamiento: Se define como “lo que es posible hacer con un objeto”. En terminología más purista, el conjunto de mensajes a los que este objeto responde (en OOP purista se usa la terminología mensaje para referirse al paso de información entre objetos).
  2. Estado: Los objetos tienen un estado en todo momento, definido por el conjunto de las variables o campos que contienen. El estado es pues la información contenida en un objeto.
  3. Identidad: Cada objeto existe independientemente del resto. Pueden haber dos objetos iguales pero no tienen porque ser el mismo (de igual forma que dos mellizos pueden ser idénticos, pero no por ello dejan de ser dos personas).

No iría mucho más allá, porque para mi esta definición contiene la clave que diferencia de la POO de la programación procedural: Los datos (estado) y funciones relacionadas (comportamiento) están agrupados en una sola entidad (objeto), en lugar de estar dispersos en el código. Es decir, el objeto encapsula los datos y el comportamiento. Como desarrollador debemos pensar en modelar nuestro sistema como un conjunto de objetos, en lugar de como un conjunto de funciones (procedimientos) invocadas una tras otra y pasándose datos más o menos arbitrarios para solucionar el problema.

El resto son detalles: herencia, polimorfismo, visibilidad… son detalles. No son menores, ciertamente, pero desde un punto de visto teórico, son meros detalles.

Y fíjate: he sido capaz de definir POO sin usar la palabra clase una sola vez. Y es que las clases son un mecanismo para implementar la POO, pero nada más. Y por supuesto, no son el único.

JavaScript tiene objetos, y estos objetos tienen comportamiento, estado e identidad. Así que desde punto de vista… Es orientado a objetos.

Pero como entiendo que esta respuesta puede no colmar a todos, especialmente a aquellos que vengan de Java, C# o algún otro lenguaje orientado a objetos “tradicional” (debería decir basado en clases) vayamos a ver como en JavaScript también tenemos esos detalles que mencionaba antes disponibles… ;)

¿Herencia en JavaScript?

Vale… Antes que nada: Si has aprendido POO con Java, C#, C++ o algún otro lenguaje basado en clases recuerda el concepto fundamental: No hay clases en JavaScript. Por lo tanto, hazte un favor y deja de pensar en clases cuando desarrolles en JavaScript. Te ahorrarás muchos dolores de cabeza.

La herencia en JavaScript es herencia entre objetos.

  • En un lenguaje basado en clases, la herencia es una relación definida entre clases. Si una clase Rectángulo deriva de la clase Figura, todos los objetos Rectángulo heredarán todo el comportamiento y estado definidos en la clase Figura.
  • En JavaScript la herencia es una relación definida entre objetos. Si un objeto deriva de otro, heredará todo el comportamiento y estado definido en el objeto base.

El mecanismo de herencia que se usa en JavaScript se conoce como herencia por prototipo y para resumirlo diré básicamente que cada objeto tiene asociado un prototipo. Si el desarrollador invoca un método sobre un objeto y este método NO está definido en el propio objeto, el motor de JavaScript buscará el método en el prototipo de este objeto. Por supuesto el prototipo es un objeto y puede tener otro prototipo y así sucesivamente, creando lo que se conoce como cadena de prototipado. La pregunta es pues como crear y asignar el prototipo de un objeto. Hay varias maneras de hacerlo, pero veamos una rápida y sencilla:

Herencia por prototipo (1)
  1. var figura = {
  2.     draw: function() {
  3.         console.log("figura::draw");
  4.     }
  5. };
  6. var rect = Object.create(figura);
  7. rect.draw();

Si ejecutas este código, la salida por la consola es “figura::draw”. La clave es el método Object.create. Este método crea un objeto vacío cuyo prototipo es el objeto pasado por parámetro. Esta es una forma sencilla de tener herencia en JavaScript.

Igual te preguntas si el objeto rect puede sobreescribir la implementación de draw (si vienes de Java sabrás que una clase hija puede redefinir los métodos de su clase base; si vienes de C# sabrás eso mismo siempre que los métodos sean virtuales). La respuesta es claro que sí:

Redefinicion de metodos
  1. var figura = {
  2.     draw: function() {
  3.         console.log("figura::draw");
  4.     }
  5. };
  6. var rect = Object.create(figura);
  7. rect.draw = function() {
  8.     console.log("rect::draw");
  9. };
  10. rect.draw();

Ahora la salida por pantalla es “rect::draw”. ¿Qué ha sucedido? Pues como hemos llamado al método draw del objeto rect y este ya lo tiene definido, JavaScript ya lo invoca y no lo busca por la cadena de prototipos.

¿Lo ves? Herencia sin clases. ¡Es posible! ;)

¿Polimorfismo en JavaScript?

Una de las preguntas que salieron en el hangout fue si había polimorfismo en JavaScript. Respondí diciendo que esta pregunta implica una forma de pensar “basada en clases” pero que sí que era posible simular el polimorfismo en JavaScript.

Pero es que una de las claves es que no es necesario el concepto de polimorfismo en JavaScript. Antes que nada aclaremos que es el polimorfismo: La posibilidad de enviar un mismo mensaje (es decir, de invocar a un método) a varios objetos de naturaleza homogénea (wikipedia). Me gusta especialmente esta definición porque define polimorfismo sin usar la palabra “clase”. Si habéis aprendido POO con un lenguaje basado en clases quizá tenéis una definición de polimorfismo redactada de forma ligeramente distinta, algo como: Que los objetos de una clase derivada pueden tratarse como objetos de la clase base, pero manteniendo su comportamiento distintivo.

Supón que estás en un lenguaje basado en clases, como C#, y tienes la clase Figura y su derivada la clase Rect. La clase Figura define un método virtual llamado Draw() que es redefinido en la clase Rect. El polimorfismo implica que puedo pasar un objeto de la clase Rect a un método que acepte como parámetro un objeto de la clase Figura. Y que si este método llama al método Draw del parámetro Figura que ha recibido… se ejecutará el método Draw de la clase Rect, porque aunque el parámetro es de tipo Figura, el objeto real es de tipo Rect.

¿Y en JavaScript? Veamos:

Polimorfismo
  1. var figura = {
  2.     draw: function() {
  3.         console.log("figura::draw");
  4.     }
  5. };
  6. var rect = Object.create(figura);
  7. rect.draw = function() {
  8.     console.log("rect::draw");
  9. };
  10.  
  11. function Polimorfismo(figura) {
  12.     figura.draw();
  13. }
  14.  
  15. Polimorfismo(rect);

La función Polimorfismo llama al método draw() del objeto que recibe. Como lo paso el objeto rect lo que se ejecuta es el método draw del objeto rect. Por lo tanto… tenemos polimorfismo automático en JavaScript. De hecho JavaScript tiene una característica conocida como Duck Typing, que bueno… es dificil de definir, pero una manera de hacerlo es: “Si camina como un pato, grazna como un pato y luce como un pato… es un pato”. O dicho de otro modo: Para el método Polimorfismo lo único que importa del parámetro figura es que tenga un método draw(). Nada más. Por lo tanto cualquier objeto que tenga un método draw es válido para ser usado como parámetro del método Polimorfismo.

El Duck Typing se suele asociar a los lenguajes dinámicos… pero no es exclusivo de ellos. Java y C# (sin usar dynamic) no tienen Duck Typing, pero p. ej. C++ lo ofrece a través de los templates.

¿Clases en JavaScript?

A esta altura del post ya debes tener claro que no. JavaScript no tiene clases, solo objetos y relaciones entre objetos. Pero existe un método de crear objetos a través de una función constructora y la palabra clave new que se parece mucho a como se hace en un lenguaje basado en clases. Eso que a priori es buena idea, ha ayudado mucho a la confusión de JavaScript… ¿Uso new para crear objetos pero no hay clases? ¿Y eso?

Déjame mostrarte un trozo de código de como crear un objeto basado en función constructora y el uso de new:

Funciones constructoras y new
  1. var Figura = function() {
  2.     this.draw = function() {
  3.         console.log("Figura::draw");
  4.     };
  5. };
  6.  
  7. var figura = new Figura();
  8. var rect = Object.create(figura);
  9.  
  10. rect.draw();

Ahora la variable Figura no es nada más que una función. Pero un tipo especial de función que llamamos función constructora. Pero ¡ojo! lo que convierte Figura en una función constructora no es nada que defina la propia función. Es como se invoca. Es el hecho de usar new lo que convierte Figura en una función constructora. El uso de new implica varias cosillas, cuyo ámbito se escapa de este post, pero la norma básica es que es la forma para invocar funciones cuya funcionalidad es crear objetos. En este ejemplo pues tengo:

  • Figura: Función que sirve para construir objetos que tienen un método draw.
  • figura (con la f minúscula): Objeto creado a partir de Figura
  • rect: Objeto cuyo prototipo es el objeto figura.

Lo interesante de usar funciones constructoras es que todos los objetos creados a través de ellas comparten el mismo prototipo. Si tenemos:

  1. var figura = new Figura();
  2. var figura2 = new Figura();

Ambos objetos comparten el mismo prototipo. Si ahora quiero añadir un método, p.ej. clear() que esté disponible para todos los objetos creados a partir de la función constructora Figura puedo añadirlo al prototipo. Y como hago esto? Pues así:

  1. var Figura = function() {
  2.     this.draw = function() {
  3.         console.log("Figura::draw");
  4.     };
  5. };
  6.  
  7. var figura = new Figura();
  8. var figura2 = new Figura();
  9.  
  10. Figura.prototype.clear = function() {
  11.     console.log("Figura::clear");
  12. };
  13.  
  14. var rect = Object.create(figura);
  15.  
  16. figura.clear();
  17. figura2.clear();
  18. rect.clear();

Figura.prototype es el nombre del prototipo de todos los objetos creados a través de new Figura. Este codigo imprime tres veces “Figura::clear”. Fíjate que funciona incluso cuando el método clear ha sido añadido al prototipo después de crear figura y figura2. Y es interesante el caso del objeto rect. Que ocurre cuando hacemos rect.clear()?

  • JavaScript busca el método clear() en el objeto rect. No lo encuentra y
  • Busca el método en el prototipo de rect que es figura. No lo encuentra y
  • Busca el método en el prototipo de figura que es Figura.prototype. Lo encuentra y lo ejecuta.

Aquí tienes a la cadena de prototipado en acción.

Fíjate que seguimos teniendo tan solo objetos. Figura no es una clase. Figura es una función. Una función para crear objetos que comparten un prototipo. Nada más.

¿Variables privadas en JavaScript?

JavaScript no incorpora de serie ningún mecanismo de visibilidad para los miembros de un objeto. Todo es público por defecto. Pero por supuesto no hay nada que no podamos conseguir… :)

Visibilidades en JavaScript
  1. var Figura = function (c, stroke) {
  2.     // Variable privada
  3.     var _color = c;
  4.     // Variable pblica
  5.     this.stroke = null;
  6.     // Necesario para poder acceder
  7.     // a this desde los mtodos privados
  8.     var self = this;
  9.     // Mtodo privado
  10.     var _setup = function(s) {
  11.         // Ah podemos acceder a mtodos pblicos
  12.         // a travs de self. Y a los privados directamente
  13.         self.stroke = s;
  14.     };
  15.     _setup(stroke);
  16.     // Mtodos pblicos
  17.     this.draw = function() {
  18.         console.log("Figura::draw in color " + _color  + " and stroke " + this.stroke );
  19.     };
  20.     this.getColor = function() {
  21.         return _color;
  22.     };
  23. };
  24.  
  25.  
  26. var f = new Figura("red", "thin");
  27. console.log(f._color); // undefined
  28. console.log(f.getColor()); // red
  29. console.log(f.stroke); // thin
  30. //f._setup("thick");  // Error: Object has no method _setup
  31.  
  32. var f2 = new Figura("blue","thick");
  33. console.log(f2._color); // undefined
  34. console.log(f2.getColor()); // blue
  35. console.log(f2.stroke); // thick
  36. //f._setup("thick");  // Error: Object has no method _setup
  37. f.draw();
  38. f2.draw();

Este ejemplo muestra como definir métodos y variables privadas. ¡Hay otras técnicas y alternativas!

Básicamente la regla es:

  • Usar la técnica de función constructora
  • Los métodos/variables privados son funciones o variables declaradas dentro de la función constructora.
  • Los métodos/variables públicas se asignan a this.
  • Guardar el valor de this en una variable privada (usualmente se usa that o self).
  • Desde las funciones privadas debes usar self para acceder a las variables públicas.
  • Desde las funciones públicas puedes usar self o this para acceder a las variables públicas.
  • En ambos casos puedes acceder a las variables privadas directamente con su nombre.

¿Herencia múltiple en JavaScript?

Si vienes de C# o Java igual alzas una ceja ahora… ¿No era peligrosa la herencia múltiple? C# y Java no incorporan este concepto debido a su posible peligrosidad y porque muchas veces da más problemas de los que puede solucionar. El peligro de la herencia múltiple es conocido como el problema de la herencia en diamante. Todos los lenguajes que soportan herencia múltiple se deben enfrentar a este problema, así que para evitarlo algunos lenguajes como Java o C# han decidido prescindir de ella.

JavaScript no soporta por defecto herencia múltiple pero si que soporta un tipo especial de herencia múltiple llamada mixin. Con los mixins ya entramos en un terreno pantanoso si vienes de C# o Java puesto que estos dos lenguajes no soportan este concepto.

Resumiendo, un Mixin es una clase que está pensada para ser incorporada dentro de otra clase ofreciendo funcionalidad adicional. Es como si tuvieras 3 clases A, B y C y las “combinases” todas ellas en una clase D (que además añadiría su propia funcionalidad). Algunos lenguajes como Lisp o Python soportan Mixins nativamente. Otros como C++ no, pero pueden imitarlos fácilmente debido al soporte de herencia múltiple (del que el uso de mixins es un tipo específico). Usar mixins en Java o C# es realmente complicado (aunque en Java8 el uso de default methods en interfaces lo hace posible). Si estás interesado en Mixins y C# echa un vistazo a heredar de José F. Romaniello.

¿Y como usar Mixins en C#? Aquí tienes un ejemplo:

Mixins
  1. var asCircle = function() {
  2.     this.area = function() {
  3.         return Math.PI * this.radius * this.radius;
  4.     };
  5.  
  6.     return this;
  7. };
  8.  
  9. var asButton = function() {
  10.     this.click = function() {
  11.         console.log("Button has been clicked");
  12.     };
  13.     return this;
  14. };
  15.  
  16. var a = { radius: 10 };
  17. asCircle.call(a);
  18. asButton.call(a);
  19. console.log(a.area());
  20. console.log(a.click());

En este cas asCircle y asButton son los dos Mixins. El primero añade una funcionalidad area a todos los objetos que tengan una propiedad llamada radius. El segundo añade un método click.

Para aplicar los Mixins sobre un objeto usamos la función call. No entraré en detalles de call ahora porque excede el objetivo de este post. Pero la clave está en que después de aplicar los Mixins, el objeto a tiene los métodos area y click.

Y más o menos… ¿con esto podemos dar por terminado el post no? Espero, que esto os haya ayudado a entender un poco mejor la POO bajo JavaScript y que bueno… os hayáis convencido de que JavaScript no tendrá clases pero orientado a objetos es :)

Saludos!

PD: Os paso el enlace del video de youtube donde podeis ver el hangout: http://www.desarrolloweb.com/en-directo/particularidades-programacion-orientada-objetos-poo-devio-8452.html

con 3 comment(s)
Archivado en:

Aviso: Este post es un divertimento que ha surgido a raíz del siguiente tweet de Juan Quijano. En este tweet básicamente Juan preguntaba si había alguna manera de limitar la longitud de una cadena. Por supuesto todas las respuestas que le dan son correctísimas, a saber:

  1. Validarlo en el setter
  2. Usar DataAnnotations y validación con atributos
  3. Usar [StringLength] en el caso de ASP.NET MVC
  4. Y otras que se podrían dar aquí.

Pero me he preguntado cuan sencillo sería crear en C# una clase cadena de longitud fija pero que se comportase como una cadena. Es decir que desde el punto de vista del usuario no haya diferencia entre objetos de esta clase y las cadenas estándar.

En este post os cuento a la solución a la que he llegado, que no tiene porque ser la única ni la mejor, y los “problemas” que me he encontrado. Además la solución me da una excusa para contar una capacidad de C# que mucha gente no conoce ni usa que son las conversiones implícitas personalizadas :)

Conversiones implícitas personalizadas

Las conversiones implícitas personalizadas son uno de los dos puntos clave de la solución a la que he llegado (el otro son los genéricos).

Una conversión implícita personalizada es la capacidad de los valores de un tipo para convertirse automáticamente en valores de otro tipo cuando es necesario. P. ej. hay una conversión implícita de int a float:

  1. float f = 10;

En esta línea la constante 10 es de tipo int, pero en cambio f es de tipo float. La asignación funciona no porque 10 sea un float si no porque hay una conversión implícita entre int (10) y float.

En cambio no hay una conversión implícita de double a float y es por ello que esa línea no compila:

  1. float f = 100.0;

Esta línea no compila porque las constantes decimales son de tipo double. Y aunque 100.0 es un valor válido para un float (pues entra dentro de su rango y capacidad), para el compilador es un double y no puede transformar un double en un float porque no hay una transformación implícita.

Por supuesto podemos asignar 100.0 a un float, usando este código:

  1. float f = (float)100.0;

Ahora estamos usando una conversión explícita. Y dado que hay definida una conversión explícita entre double (100.0) y float, el código compila. Que la conversión sea explícita tiene su lógica: la conversión de double a float puede generar una pérdida de rango y/o precisión. Por ello no hay una conversión implícita (que sucedería automáticamente y podría generar errores). Por ello la conversión es explícita, obligando al desarrollador a indicar (mediante el casting) que quiere realizarla y que es consciente de los peligros que pueda haber.

El operador de casting en C# pues invoca a una conversión explícita, que debe estar definida. P. ej. el siguiente código no compila, por más que usemos el casting:

  1. float f = (float) "100.0";

Y no compila porque NO hay definida ninguna conversión explícita de string (“100.0”) a float.

Pues bien, en C# una clase puede definir conversiones explícitas e implícitas desde y a otros tipos.

Yo empecé mi solución con una clase muy simple: Una clase que contuviera nada más que una cadena pero que se convirtiese implícitamente desde y a string:

Code Snippet
  1. sealed class FixedLengthString
  2. {
  3.     private string _buffer;
  4.  
  5.     public FixedLengthString()
  6.     {
  7.         _buffer = string.Empty;
  8.     }
  9.  
  10.     public FixedLengthString(string initialValue)
  11.     {
  12.         _buffer = initialValue;
  13.     }
  14.  
  15.  
  16.     public override string ToString()
  17.     {
  18.         return _buffer;
  19.     }
  20.  
  21.     public static implicit operator string(FixedLengthString value)
  22.     {
  23.         return value._buffer;
  24.     }
  25.  
  26.     public static implicit operator FixedLengthString(string value)
  27.     {
  28.         return new FixedLengthString(value);
  29.     }
  30.  
  31.     public override bool Equals(object obj)
  32.     {
  33.         if (obj == null)
  34.         {
  35.             return false;
  36.         }
  37.  
  38.         if (obj is string)
  39.         {
  40.             return obj.Equals(_buffer);
  41.         }
  42.  
  43.         return (obj is FixedLengthString) ?
  44.             ((FixedLengthString)obj)._buffer == _buffer :
  45.             base.Equals(obj);
  46.     }
  47.  
  48.     public override int GetHashCode()
  49.     {
  50.         return (_buffer ?? string.Empty).GetHashCode();
  51.     }
  52. }

Fíjate que esta clase no es nada más que un contenedor para una cadena (_buffer), pero la clave está en los dos métodos estáticos:

Conversiones Implicitas
  1. public static implicit operator string(FixedLengthString value)
  2. {
  3.     return value._buffer;
  4. }
  5.  
  6. public static implicit operator FixedLengthString(string value)
  7. {
  8.     return new FixedLengthString(value);
  9. }

El primero de los dos define la conversion de FixedLengthString a cadena y el segundo la conversión desde cadena a FixedLengthString.

Fíjate la sintaxis:

  • El método es static
  • Se usa implicit operator para indicar que es una conversión implícita (usaríamos explicit operator para indicar una de explícita).
  • Como valor de retorno colocamos el del tipo al que nos convertimos
  • Recibimos un parámetro del tipo desde el que nos convertimos.

Gracias a estas conversiones implícitas, el siguiente código es válido:

  1. FixedLengthString str = "YYY";
  2. Console.WriteLine(str);

En la primera línea estamos invocando la conversión implícita de cadena (“YYY”) a FixedLengthString y en la segunda la conversión contraria (Console.WriteLine espera un parámetro de tipo string y str es un FixedLengthString).

Bien, ahora tenía una clase que envolvía una cadena y que para el desarrollador se comportaba como una cadena. Sólo había que añadir la longitud máxima y listos.

Pero no era tan fácil.

La primera solución que se nos puede ocurrir pasa por declarar un campo con la longitud máxima de la cadena y en el constructor de FixedLengthString pasar que longitud máxima queremos. Crear la clase FixedLengthString para que contenga cadenas de como máximo una longitud determinada es fácil y no tiene ningún secreto. El problema está en mantener las conversiones implícitas, especialmente la conversión implícita desde una cadena hacia una FixedLengthString.

Supongamos que definimos la clase FixedLengthString para que acepte un parámetro en el constructor que defina la longitud máxima. Entonces podríamos declarar una variable para contener el DNI así:

  1. var dni = new FixedLengthString(9);

Ahora si usáramos métodos definidos en la clase (p. ej. supongamos que la clase FixedLengthString definiese un método SetValue o algo así) podríamos controlar fácilmente que el valor del buffer interno no excediese nunca de 9 carácteres. Pero yo no quería eso: yo quería que la clase se pudiese usar como una cadena estándar se tratase. Es decir poder hacer:

  1. dni = "12345678A";

En esta línea se invoca la conversión implícita desde cadena hacia FixedLengthString… ¿Y cual es el problema? Que es estática. Mira de nuevo el código de dicha conversion:

  1. public static implicit operator FixedLengthString(string value)
  2. {
  3.     return new FixedLengthString(value);
  4. }

Dentro de la conversión no puedo saber cual es el valor de la longitud máxima porque la conversión es estática y el valor de la longitud máxima está en un campo de instancia (el valor de longitud máxima puede ser distinto en cada objeto). Hablando claro: La conversión implícita devuelve un nuevo objeto, y NO puede acceder a las propiedades del objeto anterior si lo hubiese (en mi caso el objeto anterior guardado en dni).

Parece que estamos en un callejon sin salida…

En este punto he empezado un proceso de pensamiento que ha discurrido más o menos así:

  1. El problema es que el campo de longitud máxima es un campo de objeto (no estático) y la conversión es estática.
  2. Entonces si guardo el campo de longitud máxima en una variable estática, podré acceder a dicho valor en la conversión…
  3. … Aunque claro, este enfoque tiene un problema: El valor de longitud máxima es compartido por todos los objetos de la clase. No puedo tener un objeto FixedLengthString de longitud máxima 9 (para un DNI p. ej.) y otro de longitud máxima 5 (p. ej. para un código postal).

Evidentemente el punto 3, parece descartar la idea pero… ¿Y si pudiésemos tener varias clases distintas pero todas con el mismo código, pero tan solo cambiando el valor de longitud máxima? Entonces… ¡todo funcionaría!

Y… qué mecanismo conocéis en C# que permite generar clases distintas con el mismo código? Exacto: Los genéricos.

Si había una solución pasaba por usar genéricos.

Genéricos al rescate

Pero había un pequeño temilla: el parámetro que yo quería generalizar era el valor de longitud máxima, que es un int y esto no está permitido en genéricos. En los templates de C++ (que vienen a ser como los genéricos de .NET pero hipervitaminados) es posible generalizar parámetros de un tipo específico, pero en .NET no. En .NET los parámetros de los genéricos definen siempre un tipo, no un valor. P. ej. en el genérico List<T> el parámetro T es siempre un tipo (si T vale int tienes List<int> y si T vale string tienes List<string>). Y lo mismo ocurre en cualquier genérico que definas en .NET.

En fin… no era perfecto pero ya tenía la idea montada en mi mente. No era perfecta porque me obligaba a crear un tipo específico, distinto, por cada valor de longitud máxima que quisiera, pero bueno, al menos serían tipos muy sencillos. De hecho, serían tipos vacíos, tan solo decorados con un atributo.

Empecé definiendo el atributo que usaría:

FixedLengthMaxAttribute
  1. public class FixedStringLengthMaxAttribute : Attribute
  2. {
  3.     public int Length { get; set; }
  4.  
  5.     public FixedStringLengthMaxAttribute(int len)
  6.     {
  7.         this.Length = len;
  8.     }
  9. }

La idea era la siguiente: Por cada valor de longitud máxima que se quisiera se crea una clase vacía y se decora con este atributo con el valor máximo deseado.

Luego se crea una instancia de la clase FixedLengthString<T> y se pasa como valor del tipo genérico T la clase creada y decorada con el atributo. Para declarar un DNI de 9 carácteres sería:

  1. [FixedStringLengthMaxAttribute(9)]
  2. internal class NifSize
  3. {
  4. }
  1. var nif = new FixedLengthString<NifSize>();

Una vez tenemos el objeto nif podemos convertirlo desde y a cadena sin ningún problema (como hemos visto antes) y se mantiene la longitud máxima de 9 carácteres (en mi implementación se trunca si la cadena desde la que convertimos es más larga).

Ah si… Y falta la implementación de la clase FixedLenghString<T>. Básicamente es la misma que la original FixedLengthString pero con un constructor estático que lee via reflection el atributo [FixedStringLength] aplicado al tipo T y guarda el valor de la propiedad Length de este atributo en un campo estático:

FixedLengthString<T>
  1. sealed class FixedLengthString<T>
  2. {
  3.     private string _buffer;
  4.     private static int _maxlen;
  5.  
  6.     static FixedLengthString()
  7.     {
  8.         var type = typeof (T);
  9.         var attr = type.GetCustomAttribute<FixedStringLengthMaxAttribute>();
  10.         if (attr == null)
  11.         {
  12.             _maxlen = Int32.MaxValue;
  13.         }
  14.         else
  15.         {
  16.             _maxlen = attr.Length;
  17.         }
  18.     }
  19.  
  20.  
  21.     public FixedLengthString()
  22.     {
  23.         _buffer = string.Empty;
  24.     }
  25.  
  26.     public FixedLengthString(string initialValue)
  27.     {
  28.         _buffer = initialValue.Length < _maxlen ? initialValue : initialValue.Substring(0, _maxlen);
  29.     }
  30.  
  31.      
  32.     public override string ToString()
  33.     {
  34.         return _buffer;
  35.     }
  36.  
  37.     public static implicit operator string(FixedLengthString<T> value)
  38.     {
  39.         return value._buffer;
  40.     }
  41.  
  42.     public static implicit operator FixedLengthString<T>(string value)
  43.     {
  44.         return new FixedLengthString<T>(value);
  45.     }
  46.  
  47.     public override bool Equals(object obj)
  48.     {
  49.         if (obj == null)
  50.         {
  51.             return false;
  52.         }
  53.  
  54.         if (obj is string)
  55.         {
  56.             return obj.Equals(_buffer);
  57.         }
  58.  
  59.         return (obj is FixedLengthString<T>) ?
  60.             ((FixedLengthString<T>)obj)._buffer == _buffer :
  61.             base.Equals(obj);
  62.     }
  63.  
  64.     public override int GetHashCode()
  65.     {
  66.  
  67.         return (_buffer ?? string.Empty).GetHashCode();
  68.     }
  69. }

¡Y listos!

Por supuesto puedo crear una clase con una propiedad FixedLengthString<T>:

  1. class Persona
  2. {
  3.     public string Nombre { get; set; }
  4.     public FixedLengthString<NifSize> NIF { get; set; }
  5.     public Persona()
  6.     {
  7.         NIF = new FixedLengthString<NifSize>();
  8.     }
  9. }

Y operar con el NIF de esas personas como si fuesen cadenas normales:

  1. Persona p = new Persona();
  2. Console.WriteLine(p.NIF);
  3. p.NIF = "12345678A";
  4. Console.WriteLine(p.NIF);
  5. p.NIF = "1234567890987654321Z";
  6. Console.WriteLine(p.NIF);

La salida de este programa es:

image

Se puede observar como la cadena se trunca a 9 caracteres.

Bueno… Llegamos al final de este post, espero que os haya resultado interesante. Por supuesto no digo que esta sea la solución para cadenas de tamaño máximo, si no que como he dicho al principio es un simple divertimento ;)

Saludos!

con 2 comment(s)
Archivado en: ,

Muy buenas! En este post vamos a ver como habilitar un custom model binder para una propiedad en concreto de un viewmodel.

De serie es posible configurar un Custom Model Binder de dos maneras:

  1. Añadiéndolo a la colección Binders de la clase ModelBinders. Con esto conseguimos que todos los valores de un tipo concreto se enlacen usando nuestro ModelBinder.
  2. Usando el atributo [ModelBinder]. Con este atributo podemos especificar un Model Binder a usar para un viewmodel en concreto o para un parámetro en concreto de un controlador. Pero desgraciadamente no para una propiedad concreta de un viewmodel:

image

Por suerte conseguir usar un custom model binder para una propiedad en concreto de un viewmodel no es tan complicado :)

Lo primero es crearnos un atributo propio ya que el ModelBinderAttribute no podemos usarlo, así que vamos a ello:

Atributo [PropertyModelBinder]
  1. AttributeUsage(AttributeTargets.Property)]
  2.     public class PropertyModelBinderAttribute : CustomModelBinderAttribute
  3.     {
  4.         private readonly Type _typeToUse;
  5.  
  6.         public PropertyModelBinderAttribute(Type typeToUse)
  7.         {
  8.             _typeToUse = typeToUse;
  9.         }
  10.  
  11.         public override IModelBinder GetBinder()
  12.         {
  13.             var binder = DependencyResolver.Current.GetService(_typeToUse) as IModelBinder;
  14.             return binder ?? new DefaultModelBinder();
  15.         }
  16.     }

Una vez lo tenemos creado, lo aplicamos a nuestro viewmodel:

ViewModel de prueba
  1. public class SomeViewModel
  2. {
  3.     [PropertyModelBinder(typeof(RomanNumberModelBinder))]
  4.     [Range(1,21)]
  5.     public int Century { get; set; }
  6.     public string Name { get; set; }
  7. }

Ahora debemos implementar el custom model binder. En este caso he implementado un model binder que convierte cadenas en formato de número romano (tal como XX o VIII) al entero correspondiente.

Nota: El código de conversión lo he sacado de http://vadivel.blogspot.com.es/2011/09/how-to-convert-roman-numerals-to.html. Tan solo lo he corregido para usar un SortedDictionary y así asegurarme de que el orden por el que se itera sobre las claves es el esperado (en un Dictionary<,> el orden de iteración no está definido).

El código entero de mi ModelBinder es el siguiente:

RomanNumberModelBinder
  1. public class RomanNumberModelBinder : IModelBinder
  2. {
  3.     private static List<string> _romanTokens = new List<string>
  4.     {
  5.         "M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I",
  6.     };
  7.  
  8.     private class RomanKeyComparer : IComparer<string>
  9.     {
  10.         public int Compare(string x, string y)
  11.         {
  12.  
  13.             var idx = _romanTokens.IndexOf(x);
  14.             var idx2 = _romanTokens.IndexOf(y);
  15.             if (idx == idx2) return 0;
  16.             return idx < idx2 ? -1 : 1;
  17.  
  18.         }
  19.     }
  20.  
  21.     private static SortedDictionary<string, int> RomanNumbers =
  22.         new SortedDictionary<string, int>(new RomanKeyComparer());
  23.  
  24.     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  25.     {
  26.         if (bindingContext.ModelType != typeof (int)) return null;
  27.         var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
  28.  
  29.         return RomanToInt(value);
  30.     }
  31.  
  32.     private static int RomanToInt(string roman)
  33.     {
  34.         if (!RomanNumbers.Any())
  35.         {
  36.             RomanNumbers.Add(_romanTokens[0], 1000);
  37.             RomanNumbers.Add(_romanTokens[1], 900);
  38.             RomanNumbers.Add(_romanTokens[2], 500);
  39.             RomanNumbers.Add(_romanTokens[3], 400);
  40.             RomanNumbers.Add(_romanTokens[4], 100);
  41.             RomanNumbers.Add(_romanTokens[5], 90);
  42.             RomanNumbers.Add(_romanTokens[6], 50);
  43.             RomanNumbers.Add(_romanTokens[7], 40);
  44.             RomanNumbers.Add(_romanTokens[8], 10);
  45.             RomanNumbers.Add(_romanTokens[9], 9);
  46.             RomanNumbers.Add(_romanTokens[10], 5);
  47.             RomanNumbers.Add(_romanTokens[11], 4);
  48.             RomanNumbers.Add(_romanTokens[12], 1);
  49.         }
  50.         int result = 0;
  51.  
  52.  
  53.         foreach (var pair in RomanNumbers)
  54.         {
  55.             while (roman.IndexOf(pair.Key.ToString()) == 0)
  56.             {
  57.                 result += int.Parse(pair.Value.ToString());
  58.                 roman = roman.Substring(pair.Key.ToString().Length);
  59.             }
  60.         }
  61.         return result;
  62.     }
  63. }

Teóricamente lo tenemos todo montado. Pero si lo probamos veremos que NO funciona. Para la prueba me he generado una pequeña vista como la siguiente:

Vista de prueba
  1. @model WebApplication1.Models.SomeViewModel
  2.  
  3. @using (Html.BeginForm())
  4. {
  5.     <p>
  6.         @Html.LabelFor(m => m.Name)
  7.         @Html.EditorFor(m => m.Name)
  8.     </p>
  9.     <p>
  10.         @Html.LabelFor(m => m.Century)
  11.         @Html.TextBoxFor(m => m.Century, new {placeholder = "In Romna numbers, like XX or XIX"})
  12.     </p>
  13.     
  14.     <input type="submit" value="send"/>
  15. }

Y luego las acciones correspondientes en el controlador para mostrar la vista y recibir los resultados. Si lo probáis veréis que nuestro RomanNumberModelBinder no se invoca :(

El “culpable” de que no funcione es el ModelBinder por defecto (DefaultModelBinder). Dado que el atributo [ModelBinder] origina no puede aplicarse a propiedades, el DefaultModelBinder no tiene en cuenta la posibilidad de que una propiedad en concreto use un model binder distinto. Así pues nos toca reescribir parte del DefaultModelBinder.

Para reescribir parte del ModelBinder lo más sencillo es heredar de él y redefinir el método que necesitemos. En este caso el método necesario es BindProperty que enlaza una propiedad. Veamos como queda el código:

DefaultModelBinderEx
  1. public class DefaultModelBinderEx : DefaultModelBinder
  2. {
  3.     protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext,
  4.         PropertyDescriptor propertyDescriptor)
  5.     {
  6.  
  7.         var cmbattr = propertyDescriptor.Attributes.OfType<CustomModelBinderAttribute>().FirstOrDefault();
  8.         IModelBinder binder;
  9.         if (cmbattr != null && (binder = cmbattr.GetBinder()) != null)
  10.         {
  11.             var subPropertyName = DefaultModelBinder.CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
  12.             var obj = propertyDescriptor.GetValue(bindingContext.Model);
  13.             var modelMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
  14.             modelMetadata.Model = obj;
  15.             var bindingContext1 = new ModelBindingContext()
  16.             {
  17.                 ModelMetadata = modelMetadata,
  18.                 ModelName = subPropertyName,
  19.                 ModelState = bindingContext.ModelState,
  20.                 ValueProvider = bindingContext.ValueProvider
  21.             };
  22.             var propertyValue = this.GetPropertyValue(controllerContext, bindingContext1, propertyDescriptor, binder);
  23.             SetProperty(controllerContext, bindingContext, propertyDescriptor, propertyValue);
  24.             return;
  25.         }
  26.  
  27.         base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
  28.     }
  29. }

Vale… el código luce un “pelín” complicado, pero básicamente hace lo siguiente:

  1. Mira si la propiedad a enlazar está decorada con algún atributo que sea [ModelBinder] o derivado (en este caso será precisamente el [PropertyModelBinder]).
  2. Si dicha propiedad está enlazada obtiene el binder (mediante el método GetBinder() del atributo) y:
    1. Crea un “subcontexto” de binding, que contenga tan solo esta propiedad.
    2. Obtiene el valor a bindea, mediante la llamada a GetPropertyValue, un método definido en el propio DefaultModelBinder, pasándole el subcontexto de binding y el binder a usar.
    3. Llama a SetProperty (otro método de DefaultModelBinder) para establecer el valor de la propiedad
  3. En caso de que la propiedad NO esté decorada con ningún atributo [ModelBinder] o derivado, llama a base.BindProperty para procesarla de forma normal.

Nota: Este es el MÍNIMO código para que funcione, pero ¡ojo! no estamos tratando todas las casuísticas posibles ;)

Por supuesto para que funcione debemos decirle a ASP.NET MVC que el nuevo ModelBinder por defecto es nuestro DefaultModelBinderEx:

  1. ModelBinders.Binders.DefaultBinder = new DefaultModelBinderEx();

Y con esto ya tendríamos habilitados nuestros ModelBinders por propiedad!

Una nota final…

¿Y como tratar todas las casuísticas posibles que antes he dicho que nuestro método BindProperty no trataba y tener así un código “más completo”? Bien, pues la forma más sencilla y rápida es coger el código fuente de ASP.NET MVC y COPIAR TODO el método BindProperty del DefaultModelBinder en el DefaultModelBinderEx. Y luego una vez lo habéis echo localizad la línea:

  1. IModelBinder binder = this.Binders.GetBinder(propertyDescriptor.PropertyType);

Y modificadla por:

  1. var cmbattr = propertyDescriptor.Attributes.
  2.     OfType<CustomModelBinderAttribute>().
  3.     FirstOrDefault();
  4. IModelBinder binder = cmbattr != null
  5.     ? cmbattr.GetBinder()
  6.     : this.Binders.GetBinder(propertyDescriptor.PropertyType);

Y con esto ya tendréis el soporte completo. La verdad es una pena que el DefaultModelBinder no ofrezca un método virtual GetBinder(PropertyDescriptor) ya que si lo tuviese tan solo redefiniendo este método hubiese sido suficiente… Pero en fin… ¡esto es lo que hay!

Espero que os haya resultado interesante!

con no comments
Archivado en:

Muy buenas!

Imagina que tienes la siguiente entidad de un modelo de datos de Entity Framework:

   1: public class Product
   2: {
   3:     public int Id {get; set;}
   4:     [Required]
   5:     public string Name { get; set; }
   6:     public string ShortDescription { get; set; }
   7:     public string LongDescription { get; set; }
   8:     public string Remmarks { get; set; }
   9:     public string LegalNotices { get; set; }
  10:     public string TermsOfUse { get; set; }
  11:     [Column(TypeName="Image")]
  12:     public byte[] Icon { get; set; }
  13:     [Column(TypeName = "Image")]
  14:     public byte[] Screenshot { get; set; }
  15:     [Column(TypeName = "Image")]
  16:     public byte[] ScreenshotHD { get; set; }
  17:     public decimal Price { get; set; }
  18: }

Y que tienes un controlador que recoge los productos que tengas en la BBDD y los pasa a una vista:

   1: public ActionResult Index()
   2: {
   3:     IEnumerable<Product> products = null;
   4:     using (var ctx = new MyContext())
   5:     {
   6:         products = ctx.Products.ToList();
   7:     }
   8:  
   9:     return View(products);
  10: }

Donde la vista se limita a mostrar usar tan solo un par de propiedades de la clase Product:

   1: <h2>Produtcs list</h2>
   2:  
   3: <ul>
   4:     @foreach (var item in Model)
   5:     {
   6:         <li>@item.Name - @Html.ActionLink("View", "Details", new {id=@item.Id})</li>
   7:     }
   8: </ul>

Probablemente habrás visto código muy parecido en muchos blogs o ejemplos. Dicho código funciona correctamente, pero… ¿cual es el problema?

Pues bien, en el caso de entidades grandes, como puede ser esta Product (fíjate que hay 3 campos de imágen, y un par de campos de texto que podrían ser muy largos), el problema es que la consulta que está generando Entity Framework es:

SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[ShortDescription] AS [ShortDescription], [Extent1].[LongDescription] AS [LongDescription], [Extent1].[Remmarks] AS [Remmarks], [Extent1].[LegalNotices] AS [LegalNotices], [Extent1].[TermsOfUse] AS [TermsOfUse], [Extent1].[Icon] AS [Icon], [Extent1].[Screenshot] AS [Screenshot], [Extent1].[ScreenshotHD] AS [ScreenshotHD], [Extent1].[Price] AS [Price]FROM [dbo].[Products] AS [Extent1]

Es decir, estamos trasladando todos los campos desde la base de datos hacia el servidor web para tan solo usar un par de campos en la vissta (Name e Id).

Esto, en el caso de que seleccionemos muchas entidades grandes puede ser un problema. Por supuesto, la solución es muy sencilla, decirle a Entity Framework que tan solo seleccione los campos que deseemos:

   1: products = ctx.Products.Select(p => new { p.Id, p.Name}).ToList();

Con esto la query generada contiene tan solo los campos deseados:

SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name]FROM [dbo].[Products] AS [Extent1]

El tema está ahora en como pasamos esta información a la vista. Anteriormente la vista tenia la declaración @model IEnumerable<Product> pero ahora esto ya no nos sirve y ASP.NET MVC se quejará (con razón) de que el tipo del modelo de la vista no es el que le pasamos, y nos dará un error.

Quitar la directiva @model de la vista no funciona y recibirás un error parecido al siguiente ‘object’ does not contain a definition for 'Name’. Si quitamos la directiva @model, el modelo de la vista pasa a ser de tipo Object lo que no nos sirve porque ni Name ni Id están definidas en Object.

Una solución es generar un ViewModel, es decir una clase que contenga los datos que la vista requiere:

   1: public class ProductListViewModel
   2: {
   3:     public int Id { get; set; }
   4:     public string Name { get; set; }
   5: }

Luego en EF debemos crear una colección de este objeto, en lugar del objeto anónimo:

   1: var products = ctx.Products.Select(p => new ProductListViewModel 
   2: { 
   3:     Id = p.Id, 
   4:     Name = p.Name 
   5: }).ToList();

Ahora colocando la directiva @model IEnumerable<ProductListViewModel> en nuestra vista todo funciona correctamente.

El problema ahora viene si tenemos muchas vistas que listen distintos campos Product (p. ej. una con Name e Id, otra con Name, Id, Icon y ShortDescription, otra con Name, LongDescription y Remmarks, etc…). Si sigues esta técnica te vas a encontrar con muchos view models distintos, lo cual puede ser tedioso de mantener.

ASP.NET MVC tiene el concepto de tipo de modelo dinámico. Para ello en la vista puedes poner @model dynamic.

Pero… no es oro todo lo que reluce. Si cambias la directiva a @model dynamic y vuelves al código donde usábamos un objeto anónimo… Obtendrás de nuevo el error ‘object’ does not contain a definition for 'Name’. Es decir, a pesar de que declaras el tipo de modelo como dinámico en la vista, parece que para ASP.NET MVC el modelo sigue siendo un object.

La razón es que cuando usas @model dynamic el objeto que pasas debe estar preparado para interaccionar con el DLR y los objetos anónimos no lo están. La solución pasa por usar ExpandoObject que si que lo está. Entonces el código del controlador pasa a:

   1: var products = ctx.Products.Select(p => new
   2: {
   3:     p.Id,
   4:     p.Name
   5: }).ToList();
   6:  
   7: List<dynamic> model = new List<dynamic>();
   8: foreach (var item in products)
   9: {
  10:     dynamic data = new ExpandoObject();
  11:     data.Id = item.Id;
  12:     data.Name = item.Name;
  13:     model.Add(data);
  14: }
  15:  
  16: return View(model);

Ahora usando @model dynamic todo funciona correctamente. Por supuesto parece que el remedio es peor que la enfermedad: ¡El código es bastante más pesado y tienes que realizar esta copia de “propiedades” del objeto anónimo al ExpandoObject!

Ahí es donde una característica de ExpandoObject y un método de extensión te pueden ayudar. La característica es que ExpandoObject implementa IDictionary<string,object> y agregar una propiedad a ExpandoObject es equivalente a agregar una entrada en el diccionario. Así podemos tener un par de métodos de extensión sencillos:

   1: static class ExpandoObjectExtensions
   2: {
   3:     public static dynamic CopyFrom(this ExpandoObject source, object data)
   4:     {
   5:         var dict = source as IDictionary<string, object>;
   6:         foreach (var property in data.GetType().GetProperties())
   7:         {
   8:             dict.Add(property.Name, property.GetValue(data, null));
   9:         }
  10:  
  11:         return source;
  12:     }
  13:  
  14:     public static IEnumerable<dynamic> Dynamize<T>(this IEnumerable<T> source)
  15:     {
  16:         foreach (var entry in source)
  17:         {
  18:             var expando = new ExpandoObject();
  19:             yield return expando.CopyFrom(entry);
  20:         }
  21:     }
  22: }

Y ahora nuestro código en el controlador queda simplemente como:

   1: var products = ctx.Products.Select(p => new
   2:                 {
   3:                     p.Id,
   4:                     p.Name
   5:                 }).ToList().Dynamize();

¡Y listos! Usando @model dynamic nuestra vista sigue funcionando y nos olvidamos de ir creando viewmodels y de copiar propiedades arriba y abajo.

Espero que os haya sido interesante! ;-)

¡Un saludo!

con 7 comment(s)
Archivado en:

Buenas! En este post vamos a ver como empezar a trabajar con Katana. En un post anterior hablé un poco de Katana y mis fantasías (más o menos húmedas) de lo que podría ser un un futuro.

Antes que nada hagamos un repaso rápido:

  1. OWIN: Open Web Interface for .NET. Especificación que define un estándard para comunicar servidores web y aplicaciones web (en tecnología .NET).
  2. Katana: Implementación de Microsoft de la especificación OWIN.

¿Cuál es la ventaja principal de hacer que una aplicación web sea compatible con OWIN? Pues simplemente que desacoplas esta aplicación web del servidor usado. Cualquier servidor (que sea compatible con OWIN) podrá hospedar tu aplicación. Esto abre la puerta a tener aplicaciones web self-hosted.

Empezando con Owin y Visual Studio 2012

En este primer post vamos a realizar la aplicación más posible sencilla (un hello world, originalidad a tope).

Para empezar abre VS2012 (o VS2013 si lo tienes, para este post da igual) y crea una aplicación de consola. Luego añade con NuGet los siguientes paquetes:

  1. Microsoft.Owin.Hosting
  2. Microsoft.Owin.Host.HttpListener
  3. Microsoft.Owin.Diagnostics
  4. Owin.Extensions

Actualmente están en prerelase así que incluye el flag –IncludePreRelase  cuando lances el comando Install-Package desde la consola de NuGet.

Una vez tengos estos paquetes instalados, ya podemos desarrollar nuestra aplicación. Lo primero que necesitamos es una clase que ponga en marcha nuestra aplicación:

class Program

{

    public static void Main(string[] args)

    {

        var uri = "http://localhost:8080/";

 

        using (WebApp.Start<Startup>(uri))

        {

            Console.WriteLine("Started");

            Console.ReadKey();

            Console.WriteLine("Stopping");

        }

    }

}

Usamos la clase WebApp del paquete Microsoft.Owin.Hosting para poner en marcha nuestra aplicación. El parámetro genérico (Startup) es el nombre de una clase que será la que configurará nuestra aplicación.

Veamos el código:

public class Startup

{

    public void Configuration(IAppBuilder app)

    {

        app.UseHandlerAsync((req, res) =>

        {

            res.ContentType = "text/plain";

            return res.WriteAsync("Hello Katana. You reached " + req.Uri);

        });

 

    }

}

El método Configuration se invoca automáticamente y se recibe un parámetro IAppBuilder. Dicha interfaz estaba definida en la specificación de OWIN (http://owin.org/spec/owin-0.12.0.html#_2.10._IAppBuilder) pero desapareció en la versión final.

Katana usa esta interfaz para permitir a la aplicación web configurar el pipeline de procesamiento de peticiones. De momento nuestra aplicación es muy simple: Por cada petición, construirá una respuesta con el texto “Hello Katana. You reached “ seguido de la URL navegada.

Si ejecutamos el proyecto, y abrimos un navegador y nos vamos a localhost:8080, vemos que nuestra aplicación ya está en marcha:

image

Fíjate que nuestra aplicación es un ejecutable. No hay servidor web, ni cassini, ni IIS Express, ni IIS, ni nada :)

Agregando un módulo

OWIN se define de forma totalmente modular. Por un lado tenemos un Host (en este caso es nuestro ejecutable a través del objeto WebApp del paquete Microsoft.Owin.Host), varios módulos y finalmente la aplicación en si.

Los módulos implementan un “delegado” que se conoce como AppFunc (aunque no hay ningún delegado real con este nombre). AppFunc es realmente Func<IDictionary<string, object>, Task>, es decir recibir un IDictionary<string, object> y devolver un Task.

La idea es que un módulo recibe un diccionario (claves cadenas, valores objects) que es el entorno del servidor y devuelve una Task que es el código que este módulo debe ejecutar.

Los módulos están encadenados y cada módulo debe llamar al siguiente. El aspecto genérico de un módulo queda así:

using AppFunc = Func<IDictionary<string, object>, Task>;

public class OwinConsoleLog

{

    private readonly AppFunc _next;

    public OwinConsoleLog(AppFunc next)

    {

        _next = next;

    }

    public Task Invoke(IDictionary<string, object> environment)

    {

        Console.WriteLine("Path requested: {0}", environment["owin.RequestPath"]);

        return _next(environment);

    }

}

El módulo define el método Invoke y recibe como parámetro el diccionario que contiene el entorno del servidor. Luego llama al siguiente módulo y le pasa el entorno. Fíjate que la clase OwinConsoleLog no implementa ninguna interfaz ni nada, pero debe tener un método llamado Invoke que sea conforme al “delegado” AppFunc (es decir que devuelva un Task y reciba un diccionario).

Para añadir el módulo simplemente llamamos al método Use de IAppBuilder pasándole el Type de nuestro módulo:

public void Configuration(IAppBuilder app)

{

    app.Use(typeof (OwinConsoleLog));

    app.UseHandlerAsync((req, res) =>

    {

        res.ContentType = "text/plain";

        return res.WriteAsync("Hello Katana. You reached " + req.Uri);

    });

}

Si ahora ejecutas el proyecto y navegas a localhost:8080 verás como se imprimen las distintas peticiones recibidas:

image

¡Listos! Hemos creado nuestra primera aplicación web, compatible con OWIN y auto hospedada :)

En sucesivos posts iremos desgranando más cosillas sobre OWIN y Katana…

Saludos!

con 2 comment(s)
Archivado en:

Hace nada mi compi Javier Torrecilla (Little Tower para los amigos) ha escrito un post sobre los helpers de ASP.NET MVC.

En este post quiero centrarme en por qué debes usar los helpers para formularios de ASP.NET MVC. La respuesta “por qué están ahí” no vale. Hay muchas cosas que están por ahí y no deberían usarse salvo casos muy concretos (incluso cosas del .NET Framework).

Iremos como los New Kids on the Block, es decir paso a paso. Por supuesto si ya sabes todo lo que te aportan los helpers entonces este post no te aportará mucho, pero eres bienvenido a seguir leyendo por supuesto (y a comentar, claro) :)

Pregunta: Como recibo en un controlador los datos mandados en un <form>?

Esa es una pregunta típica que se hace cualquiera que empieza con ASP.NET MVC. Si vienes de ASP antiguo (el clásico, el de Interdev) o de otra tecnología como PHP, pues igual empiezas a buscar alguna propiedad llamada Form en la Request o algo así. ¡No busques! No lo hagas porque la encontrarás y entonces la usarás y te perderás uno de los elementos más poderosos de ASP.NET MVC: el model binder.

Para responder a esta pregunta vamos a usar esta vista:

Da de alta una cerveza:

 

<form method="POST">

    Nombre:

    <input type="text" name="Name" />

    <br />

    Categoría:

    <input type="text" name="Category" />

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Todo HTML, que de moment no usamos Helpers :)

Veamos como no acceder a los valores de un formulario. Si vienes de PHP o Interdev probablemente habrás llegado a teclear algo como esto:

public ActionResult Index()

{

    if (Request.Form["Name"] != null && Request.Form["Category"] != null)

    {

        // Procesar alta

        return null;

    }

    else

    {

        return View();

    }

}

Parece lógico ¿no? Buscamos si los datos de formulario existen, y si exiten es que venimos del POST y los procesamos. En caso contrario es que nos han hecho un GET por lo que devolvemos la vista.

Vale… olvida este código. Así no se hacen las cosas en ASP.NET MVC. Primero la lógica del GET (mostrar una vista) y del POST (procesar una alta) están mezclados y eso no es buena señal (¿conoces el SRP)? Por suerte ASP.NET MVC nos permite separar la lógica del GET de la del POST definiendo dos métodos y decorando el que gestiona el POST con el atributo [HttpPost].

Tener la lógica separada sería un poco mejor y de hecho hay por Internet código parecido al siguiente:

public ActionResult Index()

{

    return View();

}

 

[HttpPost]

public ActionResult Index(FormCollection form)

{

    if (form["Name"] != null && form["Category"] != null)

    {

        // Procesar alta

    }

    return null;

}

La clase FormCollection que aparece es una clase propia de ASP.NET MVC que tiene la misma información que Request.Form. Este código funciona y de hecho verás código así por Internet (se ve de todo en este mundo) pero la realidad es que hay muy pocas razones para usar FormCollection hoy en día. ASP.NET MVC tiene un concepto mucho más poderoso, un amigo al que debes conocer y entender: el model binder.

Ya he hablado en este blog sobre el model binder, así que ahora solo introduciré la regla más básica: Si tu acción tiene un parámetro cuyo nombre es idéntico al de un atributo name de un campo de un formulario, el model binder enlazará el valor del campo al parámetro automáticamente.

Es decir, el código anterior nos puede quedar como:

[HttpPost]

public ActionResult Index(string name, string category)

{

    // procesar alta

    return null;

}

Esa es la regla básica del model binder. Estoy seguro que esto te parece precioso pero a la vez es posible que una inquietud atormente tu nueva felicidad: si tengo un formulario con 20 campos… se deben declarar 20 parámetros en la acción?

Por suerte el equipo de ASP.NET MVC se dio cuenta de ello y así surge la segunda (y última que veremos en este post) regla del model binder. Ojo, que ahí va: Si tu accíón recibe como parámetro, un objeto de una clase que tiene una propiedad cuyo nombre es igual al de un atributo name de un campo del formulario, el model binder enlazará automáticamente el valor de este campo a la propiedad correspondiente del objeto recibido como parámetro. Quizá te parezca un poco rebuscado, pero lo que significa es que puedo tener una clase como la que sigue:

public class BeerModel

{

    public string Name { get; set; }

    public string Category { get; set; }

}

Y ahora en mi controlador recibir los datos simplemente usando un parámetro de tipo BeerModel:

[HttpPost]

public ActionResult Index(BeerModel beer)

{

    // procesar alta

    return null;

}

El model binder se encarga de crear el objeto beer y de rellenarlo con los parámetros del formulario. ¿Genial, no?

Pregunta: Vale, pero esto que tiene que ver con los helpers?

De momento nada, pero ahora veamos que ocurre si no los ponemos. Imagina que alguien me da de alta una cerveza pero hace submit del formulario sin entrar el nombre que es obligatorio. Evidentemente yo tengo que mostrarle de nuevo la vista de dar de alta una cerveza. Una forma no muy correcta de hacerlo es la siguiente:

[HttpPost]

public ActionResult Index(BeerModel beer)

{

    if (string.IsNullOrEmpty(beer.Name))

    {

        return View(beer);

    }

    // procesar alta

    return null;

}

Si el nombre de la cerveza está vacío devuelvo la vista como resultado, pero le paso la propia cerveza. ¿Para qué? Pues porque ahora tengo que meter código para recuperar el estado de los controles. Es decir, debo rellenar el atributo value de los dos <input> con los valores que me vienen de beer, ya que si no el usuario recibiría… ¡una vista completamente vacía!. Si vienes de Webforms este es un buen punto para empezar a odiar ASP.NET MVC, pero si lo sobrepasas luego, estoy seguro, lo empezarás a amar irremediablemente.

En resumen nuestra vista ahora queda algo como así:

@model MvcApplication1.Models.BeerModel

@{

    ViewBag.Title = "Index";

    var nameValue = Model != null ? Model.Name : null;

    var categoryValue = Model != null ? Model.Category : null;

}

Da de alta una cerveza:

 

<form method="POST">

    Nombre:

    <input type="text" name="Name" value="@nameValue"/>

    <br />

    Categoría:

    <input type="text" name="Category" value="@categoryValue"/>

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Aquí me estoy aprovechando de una característica de Razor que es que si igualamos un atributo de un elemento HTML a null, entonces Razor ni nos renderiza el atributo. Es decir si nameValue o categoryValue vale null, el atributo value ni aparece en el HTML generado.

Vale… ahora imagínate un formulario con 20 campos y ya puedes empezar a llorar.

Y esta es la primera razón por la que usar helpers. Si modificamos la vista para usar los helpers:

@model MvcApplication1.Models.BeerModel

@{

    ViewBag.Title = "Index";

}

Da de alta una cerveza:

 

<form method="POST">

    Nombre:

    @Html.TextBoxFor(m=>m.Name)

    <br />

    Categoría:

    @Html.TextBoxFor(m=>m.Category)

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Automáticamente los controles del formulario recuperan su estado automáticamente. ¡Hey! El que venía de Webforms… sigues ahí? ;-)

Pregunta: Y como muestro que campo es el que ha producido el error?

Jejejeee… muy buena pregunta. Hemos redirigido de nuevo al usuario porque se ha dejado de introducir el nombre de la cerveza, pero estaría bien que le informáramos de ello. Antes he dicho que el código del controlador no era muy correcto. Y no lo es porque no usa un objeto de ASP.NET MVC llamado ModelState. El ModelState guarda entre otras cosas todos los errores que se producen al validar los datos por parte del controlador.

Así que lo suyo es introducir el error en el ModelState cuando lo detectamos:

[HttpPost]

public ActionResult Index(BeerModel beer)

{

    if (string.IsNullOrEmpty(beer.Name))

    {

        ModelState.AddModelError("Name", "¿Donde has visto una cerveza sin nombre?");

        return View(beer);

    }

    // procesar alta

    return null;

}

El método AddModelError añade un error a la propiedad indicada (el segundo parámetro es el mensaje de error). Ahora si ejecutamos el proyecto y envías el formulario dejando el campo nombre vacío, el código HTML generado de vuelta será algo como:

<form method="POST">

    Nombre:

    <input class="input-validation-error" id="Name" name="Name" type="text" value="" />

    <br />

    Categoría:

    <input id="Category" name="Category" type="text" value="sdsd" />

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Fíjate que el <input> generado para tener el nombre de la cerveza ahora incluye una clase llamada “input-validation-error”. Esta clase la añade el helper cuando ve que hay un error asociado en el ModelState.

Luego ya por CSS es cosa tuya personalizar esta clase para que haga algo útil (p. ej. mostrar el campo en rojo). Lo importante es que el helper la añade automáticamente sin que tu tengas que hacer nada.

Ahora imagínate sin los helpers… para un formulario con 20 campos, tener que consultar para cada uno si existe un error en el ModelState… buf, sería para morirse.

Y es por esto, básicamente, que debemos utilizar los helpers!

Un saludo!

PD: Ojo, hay mejores maneras de validar la entrada de datos en ASP.NET MVC. Basta decir que el código de validación no debería estar en el controlador y que ASP.NET MVC ofrece dos mecanismos built-in que son DataAnnotations y la interfaz IValidatableObject. No hemos visto nada de esto porque no es el objetivo de este post.

con 1 comment(s)
Archivado en:

La verdad es que el tema de los enums y ASP.NET MVC da para hablar bastante (yo mismo hice un post hace ni mucho). Pero hace algunos días mi buen amigo y a veces rival, Marc Rubiño publicó en su blog un interesante artículo sobre como crear combos que mostrasen valores de enums.

En este post voy a mostrar una técnica parecida, pero a través de las data list, un concepto nuevo de HTML5 que como pasa muchas veces está recibiendo menos atención de la que merece. Y es que las data list nos dan una manera fácil y sencilla de tener cajas de texto que se autocompleten.

Qué son las data lists de HTML5?

Bueno, pues como ya debes deducir de su nombre las data list no es nada más que la posibilidad de definir un concepto nuevo, que es esto: una lista de datos.

Una lista de datos se usa como fuente de datos para un control que pueda tener una y lo bueno es que el viejo y a veces injustamente denostado <input type=”text”> entran en esta categoría. Honestamente no entiendo como el <select> no tiene soporte para data lists. Quiero suponer que hay alguna explicación, pero la verdad no la encuentro. Bueno, da igual… ¡al tajo!

La definición de una data list es muy simple:

<datalist id="sexo">

    <option value="N">Nada</option>

    <option value="P">Poco</option>

    <option value="M">Mucho</option>

    <option value="D">Demasiado</option>

</datalist>

Ahora en HTML5 podemos usar el atributo list de la etiqueta <input> para asociar a un <input> (p. ej. una caja de texto) una lista de valores:

<input type="text" id="txtSexo" list="sexo" placeholder="Sexo" autocomplete="on" />

Básicamente tan solo se trata de usar el atributo list con el valor del id de la data list a usar.

El atributo id es requerido por Chrome (si no se aplica al <input> Chrome ignora el atributo list). Este es el resultado en Chrome, Opera e IE10:

image

Podemos ver las diferencias entre navegadores:

  • Chrome nos muestra el value a la izquierda y la descripción a la derecha
  • Opera tan solo nos muestra el value
  • IE10 tan solo nos muestra la descripción

Sin duda el mejor mecanismo, en mi opinión, es el de Chrome, luego el de Opera y por último el de IE10. No he podido probar FF ni Safari por no tenerlos a mano en el momento de escribir el post.

Igual te sorprende que prefiera que el desplegable muestre N, P, M y D como hace Opera en lugar de Nada, Poco,… que muestra IE10. Eso es porque el valor que se debe teclear en el textbox es el de value. El valor de value es el que se almacena en el textbox, y por lo tanto es el que debe teclearse (a diferencia de la <select> que permite mostrar un valor y mandar otro el <input type=”file” /> no tiene esta opción).

Ten presente siempre que estamos usando un <input type=”text” /> por lo que el usuario puede entrar lo que quiera. No es como una <select> donde debe elegir un elemento de los que le indiquemos. Así pues los elementos de la data list son ¡más una sugerencia que una obligación!

Un ejemplo.

Veamos como podríamos usar esto con enums y MVC…

Lo primero es definirnos un enum:

    public enum Beers

    {

        Estrella_Damm,

        Voll_Damm,

        Moritz,

        Epidor,

        Mahou,

        Cruzcampo

    }

Ahora vamos a extender la clase HtmlHelper con un método de extensión nuevo que llamaremos TextBoxEnumerableFor:

    public static class HtmlHelperEnumExtensions

    {

        public static IHtmlString TextBoxEnumerableFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Type enumerationType, Expression<Func<TModel, TProperty>> expression)

        {

            var httpContext = helper.ViewContext.HttpContext;

            var html = new StringBuilder();

            if (httpContext.Items[enumerationType.FullName] == null)

            {

 

                httpContext.Items.Add(enumerationType.FullName, GenerateDataList(enumerationType, html));

            }

            var textbox = helper.TextBoxFor(expression, new {list = enumerationType.FullName});

            html.AppendLine(textbox.ToString());

            return MvcHtmlString.Create(html.ToString());

        }

 

        private static string GenerateDataList(Type enumerationType, StringBuilder html)

        {

            var id = enumerationType.FullName;

            html.AppendFormat(@"<datalist id=""{0}"">", id);

            html.AppendLine();

            foreach (var item in Enum.GetNames(enumerationType))

            {

                html.AppendFormat(@"<option>{0}</option>", item.Replace('_', ' '));

                html.AppendLine();

            }

            html.AppendLine("</datalist>");

 

            return id;

        }

    }

Aspectos a comentar de este código:

  1. Uso HttpContext.Items para “hacer un seguimiento” de si ya se ha creado una datalist. Esto es para no definir en el mismo HTML dos veces la misma datalist si se generan dos (o más) cajas de texto vinculadas al mismo enum.
  2. Sustituyo los guiones bajos (_) por un espacio. Eso debería tenerse en cuenta luego al recibir los datos.

Este código es muy sencillo y realmente debería sobrecargarse el método para dar más opciones (p. ej. especificar atributos html adicionales que ahora no es posible). Pero como ejemplo, creo que sirve.

Su uso es muy sencillo:

@using MvcApplication1

@using MvcApplication1.Models

@model MvcApplication1.Models.BeerModel

 

@{

    ViewBag.Title = "Index";

}

Entra el nombre de tu cerveza preferida aquí:

 

@Html.TextBoxEnumerableFor(typeof(Beers), m=>m.BeerName)

BeerName es una propiedad (string) del modelo BeerModel. Y este es el HTML generado:

Entra el nombre de tu cerveza preferida aquí:

<datalist id="MvcApplication1.Models.Beers">
<option>Estrella Damm</option>
<option>Voll Damm</option>
<option>Moritz</option>
<option>Epidor</option>
<option>Mahou</option>
<option>Cruzcampo</option>
</datalist>
<input id="BeerName" 
    list="MvcApplication1.Models.Beers" 
    name="BeerName" 
    type="text" value="" />

No generamos atributo value, en este caso dicho atributo toma el valor del propio texto del option.

Y un pantallazo de como se ve en Opera:

image

Puedes ver como se usa los elementos del enum para mostrar una sugerencia de autocompletado del textbox. Pero recuerda: el usuario puede entrar el valor que quiera.

Como digo, el código se puede mejorar mucho, pero como idea y ejemplo creo que es suficiente.

Un saludo!

con no comments
Archivado en: ,

Muy buenas! En este post voy a contar (o al menos intentarlo) como usar las herramientas de Git para VS2012 y trabajar con un repositorio Git instalado en TFS Services.

Nota: Este post está muy orientado a gente que viene de TFS, está acostumbrada a TFS y se siente un poco “perdida” con esto de Git. No pretende ser, ni mucho menos, un tutorial de Git.

Requerimientos previos

Debes tener instalado VS2012 y al menos el Update 2. Ve a la página de Updates de VS2012 para descargarte el último update (en la actualidad es el 3).

Una vez tengas el VS2012 actualizado, debes instalarte las herramientas para Git de VS2012, que te puedes descargar desde el gestor de extensiones de VS2012 o bien desde la propia página de las Visual Studio Tools for Git.

Finalmente debes tener una cuenta de TFS Services, que te puedes abrir en http://tfs.visualstudio.com. Abrir una cuenta es totalmente gratuito y te da acceso a un TFS listo para 5 usuarios de forma totalmente gratuita.

Antes que nada: diferencias entre Git y TFS clásico

El control de código fuente clásico de TFS es lo que se conoce como un CVS (Concurrent Version System), mientras que Git es un DCVS (Distributed Concurrent Version System). En TFS está claro quien es el servidor: te connectas a un servidor TFS, te bajas código de él y subes código en él.

Con Git, cada máquina es también un repositorio de código fuente. Vas a tener tu propio repositorio de código fuente en local. No tendrás solo “la última versión” si no todo el control de código fuente. Esto hace que operaciones como Branch o Merge sean mega rápidas (y puedas hacerlas offline).

De hecho, no hay en Git, un flujo que obligue a que haya un “servidor de Git centralizado”: puedes tener varios repositorios remotos con los cuales te sincronizas.

De todos modos, dado que es bastante habitual un flujo donde haya un servidor Git “central”, este es el que veremos en este post. En nuestro caso este “servidor git central” será TFS Services con el control de código fuente Git habilitado.

En Git no hay check-in o check-out, ni tampoco lock. En su lugar las operaciones básicas que tenemos son:

  1. Commit: Pasa cambios de tu working folder a tu repositorio git local pero NO al remoto.
  2. Push: Pasa cambios de tu repositorio git local al remoto
  3. Pull: Pasa cambios del repositorio git remoto al local y a la working folder.

Ahora sí…. empecemos.

Primer paso: Crear el team project

Para tener un repositorio Git en TFS debemos tener un team project. Así que dale al botón “New Team Project” desde la página principal de TFS Services y en las propiedades del proyecto asegúrate de seleccionar Git como gestor de código fuente:

image

Una vez tengas el team project creado estás listo para empezar a trabajar con Git.

Añadir la solución al control de código duente.

Para ello desde el solution explorer seleccionamos la opción “Add solution to Source control”. VS2012 nos preguntará si deseamos usar Git o bien el control de código fuente de TFS. Hasta ahora simplemente habíamos creado un repositorio de Git, pero no le habíamos indicado a VS2012 que íbamos a usarlo en nuestra solución:

image

Asegúrate de marcar la opción de Git y dale a Ok. Ahora si que estamos listos para hacer commits.

Al marcar la opción de Git, VS2012 nos crea un repositorio de Git local que está en el mismo directorio que nuestra solución. De hecho si abres con el explorador de archivos la carpeta de la solución, verás una carpeta oculta, llamada .git que es la que contiene el repositorio local.

Commit

Vamos a guardar nuestro código o en el repositorio de Git local. A diferencia del TFS clásico en Git no se protegen o desprotegen ficheros, así que no hay opciones de check-in o check-out. Básicamente Git mira todos los cambios que se producen entre la working folder y el repositorio Git local y son los ficheros que tengan cambios (de cualquier tipo) los que podemos “commitear” y enviar al repositorio Git local. Para hacerlo vete a la página inicial del team explorer pulsando el icono de home:

image

 

Ahora pulsa sobre de “Changes”. Con eso VS2012 te mostrará todos los archivos de la working folder que tengan algún cambio respecto al repositorio de Git local (es decir que se hayan añadido, borrado o modificado).

image

 

Los ficheros que aparecen en Included Changes son los ficheros que se incluirán en el commit. Los que aparecen en Excluded Changes son los que no se incluirán en el commit. Puedes pasar ficheros de un sitio a otro arrastrándolos. Una vez tengas el commit listo, debes añadir un mensaje de commit y ya podrás darle al botón “Commit”.

Una vez lo hayas hecho VS2012 te mostrará un mensaje diciendo que el commit ha sido correcto y continuarás en la misma página de commits, para seguir haciendo commits si quedan ficheros con cambios (es decir, ficheros que antes habías colocado en “Excluded Changes”). Eso es así porque con Git es costumbre hacer muchos commits y muy pequeños.

Push

Si ahora vuelves a la página inicial del Team Explorer (con el icono de la casita) y pulsas sobre el enlace “Commits” te aparecerá una ventana como la que sigue:

image

VS2012 nos pide que repositorio Git va a ser el remoto. Este debe estar vacío. Recuerda que antes hemos creado uno en TFS Services, pero todavía no habíamos dicho en ningún sitio que queríamos usarlo. Ahora ha llegado el momento. Copia la URL del repositorio Git remoto (la puedes encontrar en la página de TFS Services si vas al team project creado y al apartado “Code”) y dale al botón “Publish”. Con este proceso asociarás tu repositorio de Git local con el repositorio de Git remoto y además harás un push de los commits pendientes (es decir pasarás el código de tu repositorio Git local al remoto).

Cuando haya terminado te aparecerá una página parecida a:

image

¡Felicidades! Ya tienes un repositorio de Git local y remoto enlazado a tu solución.

Veamos ahora como añadir un cambio.

Para ello modifica un par de archivos del proyecto. Puedes hacerlo directamente: recuerda, olvida el concepto de check-out (desproteger). Vete a la página de Changes del team explorer (ve a la página principal a través del icono de casa y luego pulsa en Changes) y te aparecerán los dos archivos modificados:

image 

Arrastra uno de los dos a Excluded Changes, añade un mensaje de commit y dale a commit. Te aparecerá una pantalla como la siguiente:

image

Ahora arrastra el otro archivo a Included Changes, pon otro comentario y dale a commit de nuevo. Con esto has generado dos commits en el repositorio git local.

 

Si ahora te vas a la página de Push (vete a la inicial con el icono de la casa) y dale Commits (debajo de Changes) verás algo parecido a:

image

En outgoing commits te aparecerán los commits que has hecho en el repositorio local y que no están en el repositorio remoto. Si le das a “Push” subirás los cambios (los commits) desde el repositorio local al remoto.

Obtener los cambios de otro usuario

Vale, a no ser que estés tu solo en un proyecto (y sí, si estás tu solo en un proyecto te recomiendo encarecidamente también usar control de código fuente) en algún momento deberás incorporar los cambios que hay en el repositorio remoto al tu repositorio local.

Vete a la página de commits del team explorer (ya sabes, desde la home le das al enlace de Commits):

image

Debajo de “Incoming Commits” hay dos opciones:

  1. Fetch: Te permite obtener el listado de cambios que tienes pendientes de integrar. Es decir, el listado de commits que están en el repositorio remoto pero NO en tu repositorio local.
  2. Pull: Se trae los commits desde el repositorio remoto al repositorio local y actualiza la working folder.

Si le das a Pull el proceso es automático (Git se trae los commits del repositorio remoto al local y desde el local hacia la working folder resolviendo los conflictos si hiciese falta).

Si le das a Fetch, te aparecerán los commits que existen en el repositorio remoto, pero están pendientes de ser integrados a tu repositorio local:

image

Viendo los detalles de un commit

A veces, antes de integrar el código del repositorio remoto (es decir, antes de hacer pull) te interesa ver como este commit puede afectar a tu trabajo. Para ello, una vez has obtenidos los commits pendientes de integrar (es decir, una vez has hecho fetch) puedes ver los detalles de un commit pulsando con el botón derecho sobre él y seleccionando la opción “View Commit Details”:

image

Esto te permite ver los ficheros que conforman el commit, ver su contenido y compararlo con la versión anterior de este mismo fichero.

Resolviendo conflictos.

Imagina la situación en que tienes modificado un fichero en tu carpeta local pero NO has hecho commit de este fichero en tu repositorio git local. Imagina que haces pull y hay un commit que tiene este fichero modificado.

Cuando esto ocurrer recibirás el siguiente mensaje de error:

image

Si te aparece este mensaje debes primero hacer commit de tus cambios pendientes en tu repositorio Git local. Una vez hayas hecho el commit (no es necesario que hagas el push hacia el repositorio remoto) ya puedes volver a hacer el pull.

Entonces, Git intentará hacer merge automático entre el contenido de tu repositorio local y los commits que vienen del repositorio remoto). Pero puede que el merge automático no sea posible:

image

Nota: En esta pantalla “Incoming Commits” son los dos commits que estoy intentando integrar y en “Outgoing Commits” hay el commit que he hecho para colocar los cambios de la carpeta local hacia mi repositorio remoto. Como solo he hecho commit pero no push, por eso me aparece aquí.

Ahora pulsas sobre el enlace “Resolve the conflicts” y te aparece la página de resolución de conflictos:

image

Pulsando sobre cada uno de los archivos en “Conflicts” tendremos las opciones clásicas de tomar el archivo local (el de nuestro repositorio Git local), el remoto (el del repositorio Git remoto) o combinarlos:

image

Si le damos a “Merge” nos aparecerá la clásica ventana de Merge:

image

 

 

 

Una vez hayamos resuelto todos los conflictos, aceptamos el merge y repetimos la operación por todos los archivos. Al final llegaremos a una página parecida a:

image

Donde no habrá nada en “Conflicts” y todo estará en “Resolved”. Ahora tenemos estos cambios en nuestra working folder, pero NO en nuestro repositorio local de Git (y mucho menos en el remoto obviamente). Así que le damos a “Commit Merge” para pasar estos cambios de nuestra working folder a nuestro repositorio Gi local. Al hacerlo iréis a la página de Changes con el commit listo para ser enviado al repositorio local:

image

Fíjate que el commit que se me ha creado incluye tanto el archivo Program.cs (que es el que tenía conflictos) como el archivo App.config que pertenecía al otro “incoming commit”.

Una vez le deis a commit ya lo tendréis en el repositorio git local.

Finalmente os vais a la página de “Commits” y vereis los commits pendientes de integrar hacia el repositorio remoto (es decir, pendientes de hacer push):

image

Ahora puedes hacer push sin ningún problema y tu código integrado ya está en el reposiorio Git remoto.

Conclusiones finales

Cuando se trabaja en Git, no se trabaja a nivel de fichero, como con el control de código fuente clásico de TFS, si no que trabajamos a nivel de commit.

Debido a esto, haz commits pequeños.

Recuerda siempre el concepto de que hay un repositorio Git local y otro de remoto. A diferencia del control de código fuente clásico de TFS donde solo hay el remoto.

Si intentas hacer pull y tienes algún fichero modificado en tu working folder (del cual no has hecho commit hacia tu repositorio local) que está incluído en los commits que vas a integrar desde el servidor remoto, te dará error y deberás hacer primero un commit de este fichero (es decir pasarlo a tu repositorio local).

Espero que con esto os quede un poco más claro como funciona Git usando esta extensión de VS2012.

Finalmente: esta extensión no es, ni de lejos, el mejor cliente Git para Windows. Para flujos sencillos funciona bien, ya que está integrada en Visual Studio, pero para flujos de trabajo más complejos se queda corta. En estos casos es mejor usar un cliente de Git específico como SoureTree.

Saludos!

[Editado 20/07/2013] - Corregido un error (mirar comentario de Enrique Ortuño).

 

con 3 comment(s)
Archivado en: ,

Si eres desarrollador en tecnologías Microsoft y especialmente desarrollador web, acúerdate de esas dos palabras: Proyecto Katana. Este proyecto representa el futuro a corto plazo de todas las tecnologías de desarrollo web de Microsoft y, no me cabe duda de ello, el futuro a medio plazo del propio .NET.

No dejes que esa introducción te confunda: Katana no es una nueva tecnología, ni una nueva plataforma, ni un lenguaje nuevo, ni tan siquiera realmente una API nueva. Es simplemente un cambio de filosofía. Es como Microsoft entiende que debe ser la evolución de ASP.NET para poder seguir dando respuestas al, cada vez más, cambiante mundo del desarrollo de aplicaciones web. Katana se circumscribe sólo a ASP.NET, pero no me cabe duda de que al final todo .NET irá en la misma dirección.

¿Y qué dirección es esa? Para saber donde vamos hemos de saber de donde venimos…

Hace 12 años (año arriba, año abajo)…

Microsoft sacó .NET y con él una “nueva versión” de ASP. Conocida primero como ASP+, luego ASPX para adoptar la terminología de ASP.NET al final. La intención de MS con ASP.NET era doble. Por un lado que los desarrolladores de “toda la vida” en tecnologías web de Microsoft (que en un 95% usaban ASP) empezaran a usar la nueva versión. Pero también que esa nueva versión pudiese ser atractiva y usable rápidamente por la gran mayoría de los desarrolladores que usaban tecnologías Microsoft, lo que era equivalente a decir VB6 en aquella época.

Es este segundo objetivo el que hace que Microsoft apueste por el modelo de Webforms para que “desarrollar para web sea igual a hacerlo para escritorio”. No olvides eso: el assembly principal de ASP.NET (System.Web.dll) está totalmente acoplado a IIS.

ASP.NET se integró dentro de lo que se pasó a llamar (y se llama) .NET Framework: Un framework monolítico del que ASP.NET era tan solo una parte y que se actualizaba cada cierto tiempo (entre 1 y 2 años y pico), básicamente cada vez que había una nueva versión de Visual Studio.

En medio de todo este tiempo varios cambios en el paradigma del desarrollo web a los que ASP.NET no podía responder con prontitud por su modelo de distribución y actualización, hicieron que alguien en Redmond se diese cuenta de que alguna cosa se tenía que hacer.

Hace 4 años (año arriba, año abajo)…

Veía la luz ASP.NET MVC. Era un nuevo framework, basado en ASP.NET que venía a complementar a Webforms. Desde este momento había dos APIs para desarrollar aplicaciones web en tecnologías Microsoft: Webforms y ASP.NET MVC. Ambas funcionaban bajo toda la plataforma de ASP.NET (y el .NET Framework subyacente que era el 3.5SP1 por aquel entonces).

Pero la novedad principal de ASP.NET MVC no era que fuese una nueva API si no el hecho de que se distribuía independientemente del .NET Framework. Era la primera vez que una API de Microsoft veía la luz fuera del “paraguas” aglutinador del .NET Framework. Esto suponía la posibilidad para Microsoft de evolucionar ASP.NET MVC mucho más ágilmente. Para que te hagas una idea: junto con el .NET Framework 4, salió ASP.NET MVC2. Después salieron MVC3 y MVC4 sin que hubiese ninguna versión más del Framework, hasta que salió Visual Studio 2012 y el .NET Framework 4.5.

ASP.NET MVC marcaba una nueva tendencia (que luego ha seguido Entity Framework) en donde se intenta huir de un framework monolítico para pasar a otro “framework” formado por varios módulos que se actualizan de forma separada.

Pero recuerda: ASP.NET MVC seguía funcionando bajo System.Web.dll y por lo tanto estaba “atada” a IIS (y también al .NET Framework, ya que System.Web.dll forma parte del .NET Framework).

Hace tres año (año arriba, año abajo)…

La aparición de ASP.NET MVC3 supuso otro hito importante en esta historia, no por MVC3 en si mismo, si no por un componente que le acompañaba: NuPack.

NuPack, luego llamado NuGet, era una extensión de Visual Studio que permitía (permite) instalar paquetes de forma muy sencilla. Estos paquetes pueden ser cualquier cosa, usualmente librerías de terceros (pero también templates de proyectos, etc). Era como tener PEAR pero en .NET.

No sé si cuando salió alguien fue consciente de la importancia que adquiriría NuGet para el desarrollo en tecnologías Microsoft…

Hace un año (año arriba, año abajo)…

Salió ASP.NET MVC4 y el hito final de esta pequeña historia. No por MVC4 si no por un componente que le acompañaba (otra vez) y que parecía que formaba parte de MVC4 cuando realmente poco tenía que ver con ella: WebApi.

La gran novedad de WebApi es que no tenía ninguna dependencia contra System.Web.dll. Es decir, WebApi podía evolucionar de forma completamente independiente de ASP.NET (eso no era del todo cierto en ASP.NET MVC aunque se distribuyera via NuGet) y además al no depender de System.Web.dll implicaba que no se estaba atado a IIS por lo que se podía hospedar WebApi en otros procesos (p. ej. una aplicación de línea de comandos).

La idea de WebApi es pues la idea final: una parte del framework sin dependencias externas, que evoluciona a su propio ritmo y que se distribuye automáticamente via NuGet.

Hoy (día arriba, día abajo)…

El proyecto Katana es quien ha tomado el relevo de esta filosofía que se inaugura con WebApi. La idea del proyecto Katana es tener un conjunto de componentes cada uno de los cuales pueda evolucionar independientemente y por supuesto sin ninguna dependencia a System.Web.dll.

Katana es un conjunto de componentes, implementados por Microsoft y que son componentes OWIN. Estos componentes van desde hosts, servidores hasta componentes funcionales (p. ej. hay una versión de SignalR “katanizada” o la propia WebApi). Recuerda la clave: cada componente podrá evolucionar a su propio ritmo y podrá ser sustuído por otro en cualquier momento. Olvídate de decir que “esta web es ASP.NET 3.5” y que todo el mundo sepa que es esto. Tu web serà una combinación de distintos módulos, cada uno con su propia versión. Desde NuGet seleccionarás cual de los módulos quieres usar (p.ej. un nuevo módulo de autenticación).

Y a futuro… extiende esta visión a todo el framework. Cierra los ojos e imagina que ya no hay un .NET Framework 6 ó 7 o el que toque y que en su lugar tu proyecto usará System.Collections en una versión y también System.Net y el entity framework que toque y nada más. Incluso, porque no, seleccionar que CLR (o CLRs) quieres atacar. Yo al menos lo veo así (aunque desde que predije el fracaso del iPad tengo claro que como pitoniso no me ganaré la vida).

OWIN

Antes he mencionado que Katana es (será) básicamente un conjunto de componentes OWIN y me he quedado tan ancho. Pero… ¿qué es básicamente OWIN? Pues inspirándose de nuevo en la comunidad de Ruby (en concreto en Rack) la comunidad de .NET ha creado una abstracción entre el servidor web y los componentes del framework que se use. La idea es que:

  1. Se puedan (y usar) crear nuevos componentes de forma sencilla y fácil
  2. Sea más sencillo portar aplicaciones entre hosts.

No entraré en detalles sobre OWIN ahora (lo dejo para un post siguiente) pero básicamente sus puntos clave son:

  1. El servidor Web OWIN es el encargado de rellenar los datos OWIN (environment según la especificación) a partir de la petición. A la práctica estos datos son un diccionario (IDictionary<string,object>). Las cadenas usadas como claves están definidas en la propia especificación de OWIN.
  2. Los componentes deben implementar el application delegate (AppFunc). Básicamente la idea es que los componentes reciben el diccionario anterior y devuelven una Task con la definición de la tarea a realizar.

Estos dos puntos parecen una chorrada, pero quédate con un par de ideas al respecto:

  • OWIN está diseñado con visos de ser asíncrono (de ahí que los componentes devuelvan un Task). La idea es parecida a la de nodejs: procesar asíncronamente aumenta el rendimiento en aquellas peticiones que básicamente dependen de E/S que son la mayoría.
  • Los componentes OWIN son encadenables de forma sencilla.

Conclusión

El modelo de un framework monolítico podía ser válido hace, pongamos, siete años, pero cada vez es necesario poder evolucionar más rápidamente.

Evolución rápida implica que sea al margen del .NET Framework. ASP.NET MVC abrió el camino y el proyecto Katana es la culminación de esa idea: tener todas las APIs y componentes de ASP.NET (y algunos más) pero sin dependencias contra System.Web.dll (y por lo tanto con el .NET Framework y con IIS). Para la implementación de Katana, Microsoft se ha apoyado en OWIN, una iniciativa de la comunidad .NET, que define una interfaz entre servidores web y frameworks de desarrollo web modulares.

En próximos posts iremos ampliando la información sobre OWIN y Katana.

Saludos!

Como ya es habitual una nueva versión de Windows (en este caso la 8.1) viene acompañado de un nuevo IE11. Y como siempre IE11 viene con varias novedades siendo quizá las dos más destacadas el soporte para WebGL (gráficos 3D) y el tema de este post, el soporte del protocolo SPDY.

Este será un post ligeramente distinto a los habituales del blog porque no hablaré de nada de desarrollo :)

Un protocolo de Nivel 7

Niveles OSI (sacado de Wikipedia)

El nivel OSI de un protocolo de comunicaciones determina “cuan alejado del medio físico” está este protocolo. Apareció a mediados de los 80 y determina 7 capas por las cuales los datos deben pasar para llegar desde el medio físico (p. ej. un cable) hacia el usuario final.

Para una descripción detallada de cada nivel OSI podéis consultar este artículo de la microsoft. Por curiosidad os puedo comentar que el protocolo IP (tanto v4 como v6) está situado en el nivel 3, TCP está situado en el nivel 4, SSL (o su variante más moderna TLS) está situado en el nivel 5 y que HTTP está situado en el nivel 7. Por cierto, aprovecho para comentar que TCP e IP son dos protocolos, así que cuando decimos que internet “usa el protocolo TCP/IP” realmente queremos decir que usa “el protocolo TCP funcionando bajo el protocolo IP”. Pero bajo IP pueden funcionar otros protocolos (como UDP p. ej.) o al revés TCP puede funcionar bajo otros protocolos de nivel 3 como IPX.

Pues bien, SPDY (pronunciado speedy) es un nuevo protocolo, situado en la capa 7 de OSI que viene a reemplazar HTTP.

La frase en negrita del párrafo anterior es la frase sensacionalista, porque como ahora verás el hecho de que SPDY sustituya a HTTP no te afecta para nada como desarrollador de aplicaciones web. Dejemos esto bien claro a partir de ya, ¿ok? SPDY tiene impacto cero para los desarrolladores de aplicaciones web pues toda la semántica de HTTP se mantiene. De hecho familiarmente hay quien se refiere a SPDY como HTTP 2.0 pero eso no es ni mucho menos una denominación oficial y además se presta a confusión puesto que hay una especificación (en borrador) de HTTP 2.0 (que además se basa en SPDY pero eso es otra historia).

32x, 3.1Ghz, 12 Mpx…

Los seres humanos somos simplistas por naturaleza, generalmente nos agobia trabajar con demasiados datos y deseamos reducirlo todo a un solo indicador. Por supuesto los que se encargan de venderte cosas ya escogerán el indicador que les vaya mejor a ellos para venderte el producto con independencia de que dicho indicador sea el que mejor refleje la calidad global del producto. En los tiempos de las unidades de CD los fabricantes se esmeraban en tener un valor de “equis” lo más alto posible. Y así se asoció que una unidad de 50x era mucho mejor que una de 32x aunque esto era la velocidad punta y no la sostenida que sería mucho más importante (pero menos impactante) para dar cuenta del rendimiento de la unidad. Pero es que incluso la velocidad sostenida por si sola tampoco da toda la información. Algo parecido ocurre con los ordenadores que se suelen vender publicitando más los Ghz del procesador aunque este dato por si solo es totalmente irrelevante. O con las cámaras de fotos y los megapíxeles (solo hay que ver el revuelo que se está armando con este futuro nokia de 41 de megapíxeles). Los megapíxeles importan sí, pero es mucho más importante la calidad de las lentes, el estabilizador y, a partir de cierto número de Mpxs, el tamaño físico del CCD. Pero analizar todo esto son demasiados datos y como digo somos simplistas…

Esto mismo aplica a tu conexión de internet. Y es que… es mejor una conexión de 20 Megas que una de 15 no? Pues no, necesariamente. No voy a escribir el porque una conexión de 20 Megas puede ser peor que una de 15 (asumindo que ambas funcionen al 100% de su velocidad) porque ya lo hizo el fenómeno de José Manuel Alarcón en su blog. Léete el post porque es un must read.

Volvamos a SPDY. Seguramente la pregunta que nos podríamos hacer sería… ¿por qué un nuevo protocolo para reemplazar HTTP? HTTP funciona y es la base de Internet que es probablemente una de las revoluciones más importantes de la (corta) historia de la humanidad. ¿Entonces?

Hoy en día las páginas web se han echo muy complejas, para tener números actualizados acabo de probar varias páginas para contar cuantas peticiones http se realizan para cargarlas todas (el javascript, las imágenes, css, videos, etc). No quiero fijarme en el tamaño de los elementos transferidos, solo en cuantas peticiones http debe enviar el navegador. Pues bien, esos son algunos números:

  • facebook.com (con usuario logado): 153 peticiones http
  • marca.com: 455 peticiones http
  • twitter.com (con usuaruo logado): 44 peticiones http
  • google.com: 16 peticiones que se disparan a las 29 al empezar a teclear una búsqueda.

(Todas estas mediciones son usando las webs de producción y sin cache).

Como desarrollador web probablemente ya sabes lo importante que es minimizar el número de peticiones HTTP (usando CSS sprites, imágenes en data-uris, compactación de js, etc). Los navegadores también ayudan y suelen usar hasta 6 (antes eran 2) conexiones simultáneas.

Pero tenemos dos problemas ahí… veamos ambos.

Problema 1: Crear una conexión TCP es “lento”.

La “lentitud” de crear una conexión TCP viene dada porque usa un sistema conocido como 3-way-handshake para establecer dicha conexión:

El cliente primero debe enviar un paquete SYN, el servidor debe responder con un SYN ACK y finalmente el cliente debe responder con un ACK.

En este punto la conexión TCP está establecida y se pueden empezar a mandar datos.

 

Es decir para poder empezar a mandar datos hay una comunicación del cliente al servidor, una del servidor al cliente y otra del cliente al servidor. ¿Y cuando digo lento cuan lento quiero decir? Pues con un buen ADSL (y no me refiero a las megas) pues establecer una conexión TCP te puede costar de 20 a 30ms. Pero ojo… en un móvil estos tiempos se pueden disparar hasta los 300ms por cada conexión TCP.

En resumen: El ancho de banda NO es importante. Lo verdaderamente importante es la latencia (¿te ha dicho tu proveedor de adsl la latencia de tu conexión? ¿No, verdad?).

En HTTP1.0 cada petición HTTP abre y cierra una conexión TCP.

Solución: HTTP1.1 al rescate

En HTTP1.1 es posible usar conexiones keep-alive lo que básicamente significa que bajo una misma conexión TCP se pueden enviar varias peticiones HTTP solucionando así el problema anterior.

Actualmente (casi) todos los servidores y todos los navegadores soportan HTTP1.1 así que… problema arreglado :)

Problema #2: Todo el proceso es síncrono

Vale… gracias a las conexiones keep-alive de HTTP1.1 hemos “eliminado” el problema de la “lentitud” de abrir conexiones TCP. Pero nos queda el problema fundamental: Todo el proceso es síncrono:

HTTP sin pipeline

A pesar de que podemos utilizar una sola conexión TCP para enviar los datos, el navegador no puede enviar la segunda petición HTTP hasta haber recibido la primera y no puede enviar la tercera hasta haber recibio la segunda.

Si la segunda petición tarda mucho (por la razón que sea y en la dirección que sea) el navegador estará esperando y esperando hasta recibir la respuesta sin poder enviar la tercera petición.

 

Intento de solución: HTTP1.1 pipelining

HTTP1.1 además de conexiones keep-alive ofrecía un modo pipeline que básicamente viene a romper la sincronidad. Permite que el navegador envíe todas las peticiones de golpe y espere por las respuestas:

Pipelining en HTTP1.1

Con esto el problema #2 parece solucionado ¿no? El navegador puede enviar todas las peticiones y luego esperar a que lleguen todas las respuestas de golpe.

Pues no.

Primero hay un motivo práctico: Para que el pipeline de HTTP1.1 funcione es necesario que esté soportado por todos los dispositivos intermedios que hay entre el servidor y el funcione (o sea, los proxies) y hay algunos no lo soportan. Por esta razón HTTP1.1 pipelining apenas se usa (de hecho está soportado pero desactivado en casi todos los navegadores de la actualidad).

Pero incluso si HTTP1.1. pipelining se estuviese usando de forma masiva no es la solución ideal al problema. ¿Por qué? Pues porque HTTP obliga a una semántica FIFO en las peticiones. Es decir el navegador DEBE recibir la respuesta de la primera petición antes de recibir la respuesta de la segunda. Y DEBE recibir la respuesta de la segunda petición antes de la respuesta de la tercera. Y así sucesivamente.

Por lo tanto si hay una petición lenta todas las posteriores se verán retrasadas también, porque la respuesta a esta petición lenta debe ser enviada al navegador antes que las respuestas de las peticiones siguientes.

Solución: SPDY al rescate

Aquí es donde entra SPDY. Este protocolo, desarrollado inicialmente por Google, ofrece un pipelining real sobre una sola conexión TCP (además de otras mejoras) para de esta manera reducir los tiempos de latencia y espera.

Además SPDY añade más funcionalidades a HTTP (como server push) y toda la petición es comprimida (en HTTP se puede comprimir la respuesta pero no las cabeceras).

Y lo más importante de todo: SPDY no requiere ningún cambio en la infraestructura de red actual ni en las aplicaciones web desarrolladas. Insisto: a ti, como desarrollador web, que se use SPDY te es totalmente transparente. Recuerda: la semántica de HTTP (verbos, cabeceras, URLs) está totalmente mantenida. SPDY tan solo modifica en como se usa TCP por debajo (una sola conexión y pipelining real)

Para que se use SPDY tan solo es necesario que el servidor web y el navegador lo soporten. IE11 finalmente soporta SPDY y se une así a Firefox, Opera y obviamente Chrome. En Android la ultima versión de Chrome y Opera Mobile soportan SPDY. De Safari, la verdad no tengo ni idea. Y en el mundo de los servidores hay módulos SPDY para los principales servidores web. De IIS no tengo noticias, pero imagino que a partir de Windows 8.1 estará soportado (a nivel de http.sys supongo).

En fin, el soporte para SPDY de IE11 es una muy buena noticia que ayudará a que este protocolo se vaya extendiendo más y que todos tengamos una web (un poco) más rápida!

Como dije… un post diferente ;-)

Saludos!

con no comments
Archivado en:
Más artículos Página siguiente >