Muy buenas! Cuando preparo demos de HTML5 y JS, si no hay involucrado un servidor de por medio, no suelo utilizar VS para generar el proyecto si no algún editor más liviano, como Sublime Text o Notepad++ (personalmente prefiero el primero mil veces al segundo).

El único problema reside en que algunos navegadores, por seguridad, no ejecutan Javascript cuando el origen es file:// (es decir cuando estamos cargando un fichero del sistema de ficheros). P. ej. tengo una página que usa el API de geolocalización de HTML5 para mostrar mis coordenadas y cuando la cargo desde el sistema de ficheros, Chrome deniega la petición para geolocalización automáticamente, sin preguntar:

image 

Por otro lado IE no es tan restrictivo, pero me salta con un mensaje diciendo que los scripts (o ActiveX) se han bloqueado y un botón para permitir su ejecución:

image

Bien, aunque esto personalmente me gusta (es una buena medida de seguridad) a veces, cuando preparas demos, da un poco por el saco. La solución es, obviamente, servir los ficheros via http, desde un servidor web, así que busqué la manera más sencilla de hacerlo.

Una es, teniendo instalado IIS, copiar los ficheros al directorio Inetpub\wwwroot de IIS, pero hacer esto cada vez (además con un directorio protegido con derechos de administrador) es un peñazo.

Otra es crear un proyecto ASP.NET en Visual Studio, meter allí los html y ejecutarlo. Pero claro, iniciar VS tan solo para ejecutar un par de htmls y javascripts me parece excesivo. Pero vaya, eso es más o menos lo que iba haciendo, hasta que un día me dije “tiene que haber una manera más sencilla”.

Y nada, así di con HFS (Http File Server): un pequeño programa que al ejecutarlo crea un servidor http y empieza a servir los ficheros que tu le digas. Una vez lo descargas y ejecutas (no se instala ni nada), aparece la ventana principal:

image

Y luego tan solo arrastras los ficheros que quieres servir via http. P. ej. si arrastro el fichero c:\personal\geolocalizacion.html automáticamente aparece en la lista de la izquierda, indicando que ya se puede acceder a él, via http:

image

Lo bueno: El fichero NO se copia en ningún otro directorio, ni nada parecido. No hay nada más a configurar. Ahora ya puedo abrir un navegador y ver mi fichero servido via http:

image 

Fíjate como ahora, la página está servida via http y Chrome si que me pregunta si quiero compartir mi ubicación con localhost:8080 (tal y como manda la especificación de HTML5).

Personalmente me parece una herramienta muy sencilla y útil y la quería compartir con todos vosotros :)

Un saludo a todos!

Buenas! Hace algunos días, no muchos, que me estoy pegando (en el buen sentido de la palabra) con WaveEngine, esta maravilla que han creado los chicos de Plain Concepts.

Disclaimer: Este post (y todos los que puedan venir) no pretenden sustituir la documentación oficial. No me considero un experto en Wave ni de lejos, realmente soy un aprendiz de nivel 1 :) Simplemente voy a expresar mis experiencias y lo iré haciendo a medida que las vaya teniendo, así que bueno… puede haber inexactitudes, errores, omisiones, etc… en estos posts. Así que comentarios son más que bienvenidos.

Introducción – ¿Qué es Wave Engine?

Bueno, pues básicamente WaveEngine es un motor multiplataforma de videojuegos. No es el único hay una larga lista de ellos (algunos más multiplataforma que otros) como cocos2d (y sus derivados tales como cocos2dx), Delta Engine o el todopoderoso Unity3D. Todos ellos nacen con filosofías distintas, lo que termina redundando en características, y precios, distintos.

Wave Engine es totalmente gratuito: la descarga es gratuita y no hay que pagar licencia de ningún tipo ni royalty por juego publicado ni nada parecido. El único detalle a tener en cuenta es que Wave Engine permite desarrollar para iOS y Anrdoid a través de Monotouch y Monodroid (de Xamarin) y esos productos no son libres. Aquí pues hay un coste, que es la licencia de Monotouch y Monodroid. Por supuesto esto os aplica tan solo si quereis desplegar en Android o iOS.

Como todo motor de videojuegos, Wave nos ofrece una API de relativo alto nivel para evitar tener que lidiar directamente con DirectX (o OpenGL), además de integrar muchas otras facetas: animaciones, motores de física, etc. En definitiva, un ahorro de tiempo considerable.

Panorama actual del desarrollo de videojuegos en Windows 8

En Windows 7 y anteriores la situación del desarrollo de videojuegos era relativamente sencilla. Básicamente, motores de terceros aparte, había dos opciones básicas:

  1. Usar C++ y DirectX directamente. La opción más potente y la menos productiva ya que DirectX es una API de bajo nivel.
  2. Usar .NET (C#) y XNA. Una opción que ha sido muy usada por desarrolladores indie y pequeños estudios ya que XNA es una API de medio nivel, que evita que uno tenga que pegarse con DirectX directamente.

Con Windows 8 y la aparición de las nuevas aplicaciones para la Windows Store, el panorama ha cambiado. XNA no permite realizar aplicaciones para la Windows Store y además MS lo ha discontinuado. No habrá una futura versión de XNA.

El panorama oficial para desarrollar videojuegos para la Windows Store ahora es:

  1. Usar XAML y C#. No es óptimo ni de lejos, ya que no se usa toda la potencia gráfica del ordenador.
  2. Usar C++ y DirectX… Lo que después de venir usando XNA es un paso atrás en productividad descomunal.

Por suerte, la comunidad no se está quieta, y así ha surgido el proyecto SharpDX. SharpDX es un wrapper en .NET para DirectX. Usándolo podemos desarrollar videojuegos en C# y DirectX. Aunque es una mejora no te creas que es la panacea: DirectX es de bajo nivel por lo que SharpDX también lo es. Otra alternativa interesante es MonoGame que es un port de XNA. Como su nombre indica usa Mono (Monotouch y Monodroid) para permitir desarrollar videojuegos para iOS y Android y usa por debajo SharpDX para permitir hacer lo mismo para aplicaciones Windows Store.

Y finalmente un escalón por encima están los motores de videojuegos, como Wave. Por supuesto Wave por debajo usa SharpDX pero nosotros quedamos completamente al margen.

Estructura de un proyecto de Wave

Cuando instalamos Wave Engine nos aparecen nuevas plantillas de proyecto en VS2012:

image

Si seleccionamos la opción de “Game Project” VS2012 nos añadirá dos proyectos a nuestra solución. Uno con el nombre que hayamos elegido y otro con el añadido “Project” al final. No sé todavía porque se crean esos dos proyectos pero realmente el primero es el ejecutable y es una lanzadora del segundo. Supongo que esto es porque el primero es específico por cada plataforma mientras que el segundo (que tiene realmente todo el código) es el mismo por todas las plataformas. Sospecho que los tiros van por ahí.

A partir de ahí Wave usa conceptos muy simples de entender:

  1. Escena: Es toda la información de nuestro juego en un momento dado. P. ej. un videojuego que tuviese varios niveles  podría tener varias escenas. Otra opción sería tener una escena para el menú principal y otra para el juego en sí. En un momento dado se está ejecutando (por decirlo de algún modo) una escena.
  2. Componente: Es la unidad de modularización de Wave. Los componentes son como “piezas” que se añaden a las entidades. P.ej. para posicionar algo en pantalla (si estamos haciendo un juego 2D) vamos a necesitar un componente llamado Transform2D. Todas las entidades que tengan una posición 2D tendrán una instancia de este componente.
  3. Entidad: Cada uno de los elementos de los que se compone tu juego. El héroe, la princesa o los nubarrones del fondo son entidades.
  4. Comportamientos (Behaviors): Son componentes que permiten que una entidad tenga lógica, es decir se comporte de una manera u otra. Que hace que la princesa sea una princesa indefensa y el dragón un dragón que escupa fuego? Pues sus comportamientos.

Por defecto la plantilla de proyecto de Wave nos crea la clase que representa el juego y una escena vacía. Nuestra misión es crear entidades (con sus componentes y comportamientos) y añadirlas a la escena. Y con esto tendremos un juego :)

Hello World con Wave Engine

Venga, empecemos por lo básico de lo básico. Vamos a crear un pequeño programa en 2D que simplemente muestre un sprite. Luego más adelante veremos como animarlo y darle un poco de vida ;-)

Lo primero que debemos hacer es crear un nuevo proyecto de tipo WaveEngine Game Project. En mi caso he llamado “Mai” al proyecto.

Con esto VS2012 me va a crear los proyectos “Mai” y “MaiProject”. Como he dicho antes el segundo es el que contendrá “toda la chicha” :)

En MaiProject se me habrán creado los ficheros Game.cs que contiene la clase que pone en marcha el juego y MyScene.cs, la única escena que (de momento) tiene nuestro juego.

Ahora, lo único que vamos a hacer es mostrar un gráfico en pantalla. Para ello debemos introducir otro concepto de Wave: los assets.

Un asset no es nada más que un elemento que proviene de un fichero externo y que forma parte de nuestro juego. P. ej. si quiero mostrar un fichero .png este .png será un asset. Pero lo mismo ocurrirá si tengo un modelo 3D exportado en formato .x p.ej. WaveEngine no entiende de formatos gráficos o de formatos 3D o de cualquier otro formato externo. Wave entiende tan solo de un formato de asset genérico, el .wpk. Por lo tanto NO podemos usar directamente un .png, si no que debemos convertirlo antes a este formato .wpk.

Para ello debemos usar la herramienta (que se instala junto con Wave) llamada Assets Exporter. Si la ponemos en marcha veremos una interfaz muy, muy negra:

image

En mi caso tengo un fichero .png, llamado mai_idle (0).png y quiero convertirlo a un .wpk para poder usarlo desde Wave. Para ello, debo crear un proyecto nuevo de assets exporter. Así que le doy a File->New Project y selecciono una carpeta. Ello me crea un fichero .wproj y una estructura de carpetas dentro de la carpeta seleccionada. Una de esas carpetas es llamada Assets y contendrá los ficheros de origen (en mi caso el .png). Para añadir assets al proyecto basta con pulsar el botón de “+” (el primero por la izquierda) y seleccionar el fichero. Al hacerlo el fichero es copiado automáticamente a la carpeta Assets.

Una vez tenemos todos los Assets podemos darle a exportar (Project –> Export) y en la carpeta “Exports” dentro de la carpeta que hemos elegido al crear el proyecto del assets exporter tendremos el fichero .wpk.

Ahora debemos copiar este fichero a la carpeta “Content” del proyecto de VS2102 y establecer en las propiedades del fichero “Copy to output folder” a “Always”:

image

Bien! Esta es la forma habitual de proceder con los assets :)

A partir de ahora ya tan solo nos queda codificar. En nuestro caso vamos a mostrar tan solo una imagen. Para ello vamos a crear una entidad (todo son entidades en Wave) que va a tener varios componentes. Vamos a construirlo paso  a paso. Todo el código va en el método CreateScene de MyScene. Empezamos por crear la entidad:

var mai = new Entity("Mai");

Y ahora vamos a irle añadiendo componentes. Empezaremos por una posición. Haremos que mai aparezca en la esquina inferior izquierda de la pantalla. En Wave una posición es un componente de tipo Transform2D (estamos en un videojuego 2D, los 3D son otro mundo):

mai.AddComponent(new Transform2D()

    {

        X = 50,

        Y = WaveServices.Platform.ScreenHeight - 46,

        Origin = new Vector2(0.5f, 1)

    });

El siguiente paso es tener un asset gráfico. De hecho Wave, como buen motor, nos da el concepto de sprite, es decir un conjunto de gráficos:

mai.AddComponent(new Sprite("Content/mai_idle (0).wpk"));

Al constructor de Sprite se le pasa el nombre del fichero .wpk que contiene el gráfico (técnicamente la textura). Un tema a destacar, que ya veremos en otro post, es que un fichero .wpk puede contener más de un gráfico de nuestro sprite.

Finalmente tan solo nos queda añadir el renderizador, es decir el componente que se encarga de “dibujar” en pantalla. Te puede parecer extraño que los renderizadores sean componentes, pero esto permite que una misma entidad se dibuje (se renderice) en pantalla de formas distintas. ¡Modularidad ante todo!

mai.AddComponent(new SpriteRenderer(DefaultLayers.Alpha));

Nota: No la he usado en este ejemplo, pero Wave tiene una API fluent, de forma que en lugar de ir haciendo mai.XXX cada vez, podeis encadenar las llamadas a AddComponent una tras de otra.

Finalmente debemos agregar esta entidad que hemos creado a la escena:

EntityManager.Add(mai);

¡Y listos! Hemos terminado, ya podemos darle a F5 para ver nuestra obra de arte:

image

En el siguiente post veremos como darle un poco de movimiento… que si alguien se merce ser vista en pleno movimiento es Mai Shiranui :P :P

¡Buenas!

Empezamos con una pregunta:

¿Cual es el resultado de este programa?

class Program

{

    static void Main(string[] args)

    {

        var data = Foos;

        foreach (var foo in data)

        {

            ChangeFooValue(foo);

        }

 

        var firstFoo = data.First();

        Console.WriteLine(firstFoo.Value);

        Console.ReadLine();

    }

 

    private static IEnumerable<Foo> Foos

    {

        get

        {

            for (var idx = 1; idx <= 10; idx++)

            {

                yield return new Foo(idx);

            }

        }

    }

 

    private static void ChangeFooValue(Foo foo)

    {

        foo.Value = foo.Value + 10;

    }

}

 

internal class Foo

{

    public int Value { get; set; }

    public Foo(int i)

    {

        Value = i;

    }

}

¿Ya lo has meditado?

Pues ahora la solución…

Aunque el sentido común te pueda decir que el valor que se imprimirá es 11, el valor que realmente se imprime es 1.

¿Y eso? A priori todo parece claro: Tenemos una propiedad llamada Foos que nos devuelve 10 objetos Foo (con valores del 1 al 10). Nos guardamos dichos valores en data, iteramos sobre ellos y añadimos 10 al valor de cada objeto Foo. Luego imprimimos el valor del primero de esos objetos. Todo está perfecto, salvo que el resultado es 1 y no 11 como debería ser.

La clave es en como está definida la propiedad Foo (usando yield return) lo que impacta directamente en lo que es la variable data. Por qué… ¿Qué es data? ¿Es una lista? ¿Es un array? ¿Alguien lo sabe?

Cuando usamos yield return no se crea un espacio de almacenamiento para guardar los valores que vamos devolviendo. Se van creando uno tras otro tantas veces como se necesita. Realmente data no contiene ningún objeto (la frase que he usado antes de “Nos guardamos dichos valores en data” es totalmente inexacta), es como un apuntador a “una colección virtual (de 10 elementos)”. Por eso cuando iteramos con el foreach pasamos 10 veces por el yield return y cuando luego usamos el .First() pasamos otra vez más por el yield return. Aunque antes en el foreach se han recuperado los 10 elementos de Foos, como no están guardados en ningún sitio, al hacer .First() se vuelve a recuperar el primero. Lo que crea un Foo nuevo cuyo valor es 1. De ahí el resultado final.

Si usas Resharper, que sepas que te va avisar:

image

Este aviso, simplemente te indica que se está recorriendo más de una vez el mismo IEnumerable (en este ejemplo se recorre una vez con el foreach y otra vez al usar .First()). Recorrer dos veces un IEnumerable puede tener consecuencias no esperadas o puede ser perfectamente correcto. Como Resharper no tiene manera de saberlo, te avisa, para que le eches un vistazo.

Esto es así tan solo si en algún momento NO se guardan en ningún sitio los elementos recuperados. Basta con hacer:

var data = Foos.ToList();

Ahora el resultado final es 11, ya que el método .ToList() copia los valores a una List<T> por lo que ahora data si que tiene almacenados los 10 valores Foo. Y cuando hacemos data.First() recuperamos simplemente el primer valor almacenado en data.

Un caso real…

Bien, probablemente pienses que esto tampoco tiene mucha importancia porque tu no vas por ahí haciendo “colecciones virtuales” con yield, pero que sepas que hay alguien que si que lo hace: alguien llamado EF. En un proyecto en el que estuve nos encontramos con este comportamiento precisamente con el uso de EF: Usábamos EF para recuperar ciertos registros de la BBDD que luego convertíamos a DTOs con un método extensor que dado un IEnumerable<T> devolvía un IEnumerable<TDto>. Dicho método extensor iteraba sobre el IEnumerable<T> original y devolvia otro IEnumerable de Dtos:

public static IEnumerable<R> Map<T, R>(this IEnumerable<T> source)

{

    foreach (var t in source)

    {

        yield return Mapper.Map<T, R>(t);

    }

}

(En nuestro caso la clase Mapper era AutoMapper).

A priori parece todo correcto: Obtenemos datos de EF y los mapeamos a DTOs. Si nos limitamos a devolver el IEnumerable obtenido por Map<T,R> todo va perfecto. El problema es si modificamos alguno de los DTOs que hemos obtenido. Cuando volvemos a iterar sobre la variable que contiene los resultados volvemos a obtener los resultados originales. Eso es debido a que EF crea una colección virtual (es decir usa yield return) para devolvernos los resultados y nuestro Map<T,R> hace lo mismo, por lo que en ningún momento hemos “guardado” esos datos en ningún sitio. Estamos en la misma situación que el ejemplo inicial de este post.

La solución pasa por materializar (es decir llamar a .ToList() o .ToArray()) el resultado devuelto por EF.

Para finalizar solo comentar que el uso de yield return en C# es un ejemplo de lo que se conoce como “corutina”. Una corutina no deja de ser una subrutina (es decir una función o pedazo de código) pero que es reentrante en distintos puntos. Otro ejemplo de corutinas en C# lo puedes encontrar en el uso de la palabra clave await (en C# 5).

¡Un saludo!

con no comments
Archivado en: ,

Buenas! Una de las preguntas más referentes en ASP.NET MVC consiste en como crear combos, enlazarlas, etc, etc… La verdad es que la documentación sobre este punto es un poco difusa y dispersa así que intentaré en este post mostrar un poco las distintas formas que tenemos en ASP.NET MVC de crear combos.

Para ilustrar las distintas opciones partimos de una clase “Database” que simula un contexto de ORM. Es una clase que simplemente tiene dos listas (estáticas), una de ciudades (Cities) y otra de provincias (States). La definición de las clases City y State son:

    public class City

    {

        public int Id { get; set; }

        public string Name { get; set; }

        public string CP { get; set; }

        public int StateId { get; set; }

    }

 

    public class State

    {

        public int Id { get; set; }

        public string Name { get; set; }

    }

1. SelectListItem

En HTML una combo (<select>) contiene una lista de opciones que siempre son clave y texto que se muestra (ambos alfanuméricos). Para representar esta información en ASP.NET MVC disponemos de la clase SelectListItem. SelectListItem nos permite almacenar la clave (Value) y el texto (Text), así como un valor booleano (Selected) que indica si este es el elemento seleccionado por defecto a la combo (se corresponde al atributo selected del tag <option>).

Una posible forma de usarlo sería así:

var items = new List<SelectListItem>();

items = Database.Cities.Select(c => new SelectListItem()

    {

        Text = c.Name,

        Value = c.Id.ToString()

    }).ToList();

 

ViewBag.Cities = items;

return View();

Obtenemos las ciudades y luego convertimos cada objeto “City” en un objeto SelectListItem. Finalmente guardamos esta lista de SelectListItem en el ViewBag.

Para mostrar esta combo basta con usar en la vista:

@Html.DropDownList("Cities")

El nombre usado “Cities” es el nombre del campo usado en el ViewBag y donde se encuentra la lista de SelectListItem.

La combo generada tendrá un atributo name llamado Cities y esto es importante a la hora de recibir el valor seleccionado de la combo. Hay una gran confusión en esto. Una combo envia UN SOLO ELEMENTO al controlador: El ID del elemento seleccionado.

Veamos como podemos recibir la ciudad seleccionada:

[HttpPost]

public ActionResult Index(string Cities)

{

    var id = Int32.Parse(Cities);

    // Recuperamos la ciudad ==> Consulta a BBDD

    var city = Database.Cities.FirstOrDefault(c => c.Id == id);

    // Operamos con la ciudad

}

Dos cosas a destacar:

  1. Lo que recibimos en Cities NO es la lista de ciudades. Es el valor de la propiedad Value del SelectListItem seleccionado (en mi caso el ID de la ciudad seleccionada).
  2. El nombre del parámetro (Cities) es el mismo que el nombre del campo del ViewBag (y el mismo que pusimos en la llamada a Html.DropDownList).

2. IEnumerable

Tener que convertir siempre nuestros datos (en este caso una lista de ciudades) a una lista de SelectListItem es muy pesado. Por suerte hay una clase SelectList que hace esto por nosotros. Basta con pasarle el IEnumerable que queremos, el nombre de la propiedad que es la clave y el nombre de la propiedad que contiene el texto a mostrar.

public ActionResult Index()

{

    var items = Database.Cities;

    ViewBag.Cities = new SelectList(items, "Id", "Name");

    return View();

}

En este punto ViewBag.Cities contiene una SelectList (que implemente IEnumerable<SelectListItem>) y el resto del código ya es exactamente el mismo que antes.

3. Otros orígenes de datos

Hasta ahora en la vista hemos usado Html.DropDownList pasándole tan solo una cadena (Cities). Esta cadena determina:

  • El nombre del atributo name del <select> generado. Que a su vez es el nombre del parámetro cuando recibimos los datos
  • El nombre del campo del ViewBag que tiene los elementos.

Si no queremos que estos dos valores sean iguales, podemos espcificarle a Html.DropDown donde está el IEnumerable<SelectListItem> que contiene los datos de la combo. Así en la vista podríamos utilizar:

@Html.DropDownList("selectedCity", ViewBag.Cities as IEnumerable<SelectListItem>)

Con esto le estamos diciendo que nos genere un <select> cuyo atributo name valga “selectedCity” y que los datos están en ViewBag.Cities.

Ahora cuando recibimos los datos debemos tener presente que el parámetro ya NO se llama Cities, si no selectedCity:

[HttpPost]

public ActionResult Index(string selectedCity)

{

    var id = Int32.Parse(selectedCity);

    // Recuperamos la ciudad ==> Consulta a BBDD

    var city = Database.Cities.FirstOrDefault(c => c.Id == id);

    // Operamos con la ciudad

}

4. HtmlDropDownListFor

Este helper lía un poco porque tendemos a compararlo con el resto de helpers similares. Así, si yo hago Html.TextboxFor(m=>m.Name) me va a generar un Textbox vinculado a la propiedad Name del ViewModel de la vista. HtmlDropDownListFor también espera una propiedad del modelo pero NO es la propiedad que tiene los elementos, si no donde dejará el valor del elemento seleccionado.

Mucha gente se confunde y se cree que la propiedad que pasamos a Html.DropDownListFor es la propiedad que contiene los valores a mostrar. Imaginemos que tenemos el siguiente ViewModel:

public class ComboCitiesViewModel

{

    public IEnumerable<City> Cities { get; set; }

    public int SelectedCity { get; set; }

}

La acción Index nos queda ahora de la siguiente forma:

public ActionResult Index()

{

    var items = Database.Cities;

    var vm = new ComboCitiesViewModel();

    vm.Cities = items;

    return View(vm);

}

Para usar Html.DropDownListFor podemos hacerlo tal y como sigue:

@Html.DropDownListFor(m=>m.SelectedCity, new SelectList(Model.Cities, "Id", "Name"))

Le paso DOS parámetros a Html.DropDownListFor:

  1. La propiedad del ViewModel que contendrá el valor seleccionado
  2. El IEnumerable<SelectListItem> con los datos. Fijaos que en este caso dado que mi ViewModel contiene un IEnumerable<City> uso la clase SelectList que hemos visto antes para hacer la conversión.

Para recibir los datos puedo declarar la siguiente acción:

[HttpPost]

public ActionResult Index(ComboCitiesViewModel info)

{

    var id = info.SelectedCity;

    // Recuperamos la ciudad ==> Consulta a BBDD

    var city = Database.Cities.FirstOrDefault(c => c.Id == id);

    // Operamos con la ciudad

}

Y aquí es donde hay otro punto de confusión: en info NO VAS A RECIBIR la lista de ciudades. Es decir la propiedad Cities va a ser null:

image

¿Y eso? Pues bueno, simple y llanamente nadie manda estos valores de vuelta para el controlador. La lista de ciudades NO está en la petición POST que hace el navegador y por lo tanto el controlador no la recibe.

De hecho, podríamos modificar el parámetro ComboCitiesViewModel para que fuese un string llamado SelectedCity y funcionaría igual.

5. Combos encadenadas

Lo que vamos a ver es una implementación de combos encadenadas pero SIN ajax. Es decir, seleccionas provincia, envías la petición y te aparecen las ciudades. En una web “actual” seguramente se haría via Ajax, pero hacerlo de la “manera antigua” nos permitirá terminar de ver como funcionan las combos.

Antes que nada modificamos el viewmodel:

public class ComboCitiesViewModel

{

    public IEnumerable<City> Cities { get; set; }

    public int SelectedCity { get; set; }

    public IEnumerable<State> States { get; set; }

    public int SelectedState { get; set; }

}

La acción Index que envía la página inicial la modificamos para que rellene States:

public ActionResult Index()

{

    var items = Database.States;

    var vm = new ComboCitiesViewModel();

    vm.States = items;

    return View(vm);

}

La vista nos quedará de la siguiente forma:

@model MvcCombos.Models.ComboCitiesViewModel

 

@using (Html.BeginForm()) {

    <label for="Cities">Ciudad:</label>

 

        @Html.DropDownListFor(m=>m.SelectedState, new SelectList(Model.States, "Id", "Name"))

 

    if (Model.SelectedState != 0)

    {

        @Html.DropDownListFor(m=>m.SelectedCity, new SelectList(Model.Cities, "Id", "Name"))

    }

 

    <input type="submit" value="enviar" />

}

Es importante entender lo que hacemos en la vista:

  1. Si NO hay provincia seleccionada (Model.SelectedState vale 0) entonces mostramos la primera combo para seleccionar estado.
  2. Si hay estado seleccionado entonces generamos la combo para seleccionar la ciudad.

Nota: Este código tiene algunos problemas, como p.ej. que ocurre si el usuario selecciona una provincia, envía el formulario, y cuando aparece de nuevo la vista con las dos combos modifica la provincia seleccionada? En una aplicación real deberías, al menos, deshabilitar la combo de estados cuando ya haya estado seleccionado.

Y finalmente ahora la acción que recibe los resultados debe gestionar que será llamada dos veces (una para seleccionar el estado, la segunda con estado y ciudad):

[HttpPost]

public ActionResult Index(ComboCitiesViewModel info)

{

    if (info.SelectedState != 0 && info.SelectedCity == 0)

    {

        info.States = Database.States;

        info.Cities = Database.Cities.Where(c => c.StateId == info.SelectedState);

        return View(info);

    }

 

    var id = info.SelectedCity;

    // Recuperamos la ciudad ==> Consulta a BBDD

    var city = Database.Cities.FirstOrDefault(c => c.Id == id);

    // Operamos con la ciudad

}

Fíjate en un par de cosas:

  1. Debemos volver a cargar todos las provincias dentro del viewmodel. Si no cuando la vista intente renderizar la combo de provincias dará error.
  2. En las ciudades seleccionamos tan solo aquellas que son de la provincia que el usuario ha seleccionado.

Insisto, en una vista “real” la segunda vez no mostraríamos la combo de provincias, quizá mostraríamos el nombre de la ciudad seleccionada. Veamos como podríamos hacerlo.

Por un lado podemos modificar el viewmodel:

public class ComboCitiesViewModel

{

    public IEnumerable<City> Cities { get; set; }

    public int SelectedCity { get; set; }

    public IEnumerable<State> States { get; set; }

    public int SelectedState { get; set; }

    public string SelectedStateName { get; set; }

}

Añadimos la propiedad para guardar el nombre de la provincia seleccionada. Y en la vista usamos esta propiedad o Html.DropDownList en función de si hay o no provincia seleccionada:

@model MvcCombos.Models.ComboCitiesViewModel

 

@using (Html.BeginForm()) {

    <label for="Cities">Ciudad:</label>

 

    if (Model.SelectedState == 0)

    {

        @Html.DropDownListFor(m => m.SelectedState, new SelectList(Model.States, "Id", "Name"))

    }

    else

    {

        <text>Provincia @Model.SelectedStateName </text>

        @Html.DropDownListFor(m=>m.SelectedCity, new SelectList(Model.Cities, "Id", "Name"))

    }

 

    <input type="submit" value="enviar" />

}

Finalmente en la acción que recibe los datos nos ahorramos de rellenar de nuevo las provincias (ya que la vista ya no las usará la segunda vez) y ponemos el nombre de la provincia seleccionada (fíjate que hemos de ir a buscarlo a la BBDD ya que solo tenemos el ID):

[HttpPost]

public ActionResult Index(ComboCitiesViewModel info)

{

    if (info.SelectedState != 0 && info.SelectedCity == 0)

    {

        info.SelectedStateName = Database.States.Single(s => s.Id == info.SelectedState).Name;

        info.Cities = Database.Cities.Where(c => c.StateId == info.SelectedState);

        return View(info);

    }

 

    var id = info.SelectedCity;

    // Recuperamos la ciudad ==> Consulta a BBDD

    var city = Database.Cities.FirstOrDefault(c => c.Id == id);

    // Operamos con la ciudad

}

Con esta aproximación:

  1. La primera vez la vista mostrará la combo de provincias y el controlador recibirá en SelectedState el id de la provincia seleccionada.
  2. La segunda vez la vista mostrará un texto con el nombre de la provincia y la combo de ciudades y el controlador recibirá en SelectedCity el ID de la ciudad seleccionada. Esta segunda vez el controlador NO recibirá SelectedState ya que no se envía. En nuestro caso no es necesario ya que lo podemos sacar de la ciudad. Si fuese necesario deberíamos usar un campo Hidden.

Bueno… creo que esto más o menos es todo. ¡Espero que este post os ayude a resolver las dudas que podáis tener con las combos en ASP.NET MVC!

¡Un saludo!

con 3 comment(s)
Archivado en:

Venga, seguro que como la mitad de mortales tienes una idea de negocio que consiste en hacer una web y que te la compre Google (la otra mitad esperan que la compre Microsoft :p).

Si este es el caso, ya sabes que se trata de tener cuantos más usuarios mejor (ahí tienes el caso de Mammoth que han lanzado una campaña viral para que todos nos apuntemos allí aunque no tengamos ni idea de que va). Hoy en día cualquier web que se precie tiene un perfil donde el usuario puede subir una foto suya para que sea su avatar. Imagina la situación de que un usuario se registra a tu web y no tiene a mano ninguna foto suya para subir. ¿No estaría bien que se pudiese hacer una foto con la webcam del portátil y subirla directamente? Todo ello des de tu web, por supuesto.

Pues bien, eso es ni más ni menos lo que permite WebRTC. ;-)

WebRTC significa Web Real Time Communications y es una de las futuras APIs de HTML5 que más darán que hablar. Hablo en futuro porque actualmente no son un estándard terminado: la especificación actual es todavía un Working Draft. Eso significa que su soporte en navegadores es todavía muy escaso y en la mayoría de casos experimental. El código de este post ha sido probado en Chrome 26 y funciona. No he probado en otros navegadores, pero por lo que sé IE10 no soporta todavía WebRTC y por lo que he leído FF lo soporta a partir de su versión 20. Opera también parece que lo soporta pero no sé a partir de que versión. Al final no tengais ninguna duda de que IE terminará soportando WebRTC, pero como digo: no es un estándard terminado y su definición puede cambiar.

Manos a la obra

La implementación de WebRTC se basa básicamente en una función javascript llamada getUserMedia que está en el objeto navigator. De todos modos como ya digo el soporte puede ser experimental y así p.ej. en Chrome está función está prefijada y debe usarse webkitGetUserMedia. Cosas del desarrollo para web.

Nota: Personalmente lo de los vendor prefixes me parece una aberración. No sé, si una característica se soporta, se soporta y punto. No veo porque tenemos que andar prefijando cosas porque “están a medias” y tal. Al final el problema para el desarrollador es el mismo: debes acordarte de meter todos los prefijos que toquen. En CSS aún puede tener un pase pero… ¿en javascript? ¿De veras tenemos que prefijar una función javascript? A mi me parece que algo se nos está yendo de las manos, pero bueno los prefijos están ampliamente aceptados por el W3C así que supongo que será mejor tenerlos que no tenerlos.

La función getUserMedia permite obtener un stream de datos local. Resumiendo, con getUserMedia podemos obtener la imagen de la webcam o el sonido del microfono. Básicamente le pasamos dos o tres parámetros:

  1. Los streams locales que queremos capturar (p.ej audio y/o video).
  2. La función de callback a invocar cuando la captura haya empezado
  3. La función de callback en caso de error (opcional).

Así para capturar la webcam bastaría con:

navigator.getUserMedia({video: true}, onSucessCallback, onFailCallback);

Para no andar jugando con los prefijos es preferible hacer algo como:

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

Así a medida que los navegadores vayan incorporando la función (con o sin prefijo) pues ya la tendremos disponible.

Vale… capturamos un stream de video, pero lo suyo es poderlo mostrar ¿no? En HTML5 tenemos una etiqueta que nos permite mostrar vídeos (<video />). ¿No sería genial que la pudiesemos utilizar? Pues, por suerte, podemos. Para ello nos tenemos que apoyar en otra API de HTML5: window.URL

Con los métodos de window.URL podemos crear URLs “ficticias” que apunten a “objetos” (técnicamente Blobs) que están vivos dentro del documento. La idea viene a ser la siguiente: con getUserMedia capturamos un stream de video. Para mostrar videos tenemos la etiqueta <video />. Pero la etiqueta <video /> espera la URL del vídeo. Pues con window.URL vamos a poder crear esta URL.

El código es muy simple:

<!DOCTYPE html>

<html>

<head></head>

<body>

    <video autoplay></video>

    <script>

        var onErrorCallback = function (e) {

            console.log('Error!', e);

        };

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

        navigator.getUserMedia({ video: true}, function (localMediaStream) {

            var video = document.querySelector('video');

            video.src = window.URL.createObjectURL(localMediaStream);

        }, onErrorCallback);

    </script>

</body>

</html>

Bueno… Si ejecutais este código veréis algo como:

image

Por supuesto no vereis a este tipo en pantalla, seguramente el que aparezca sea más feo, pero bueno eso son cosas que pasan :P :P :P :P

La función getUserMedia pide permisos. Es decir el usuario debe confirmar que da acceso a la webcam en este caso:

image

Vale… ya estamos mostrando el vídeo de la webcam. Pero el objetivo era que el usuario pudiese subir una foto de su perfil a nuestra web, ¿recordáis?

Bueno, para ello acude en nuestra ayuda otro de los nuevos elementos de HTML5: el <canvas />. Como ya sabréis la mayoría el canvas de HTML5 es un espacio dentro del documento para dibujar gráficos en 2D o en 3D.

Pues bien, la idea es volcar el frame actual del video al canvas. Y por suerte nos basta con llamar al método drawImage del contexto 2D del canvas. Sí, tan simple como esto.

Veamos el código:

<!DOCTYPE html>

<html>

<head>

    <style>

        video {width: 300px; height: 300px;}

    </style>

 

</head>

<body>

    <video autoplay></video>

    <input type="button" value="snaphsot!" id="snap" />

    <canvas></canvas>

 

    <script>

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

        // Definición de variables globales

        var localUserMedia = null;

        var canvas = document.querySelector('canvas');

        var video = document.querySelector('video');

        var ctx = canvas.getContext('2d');

        // Cuando cargamos el vídeo guardamos la relación

        // de aspecto y ajustamos el tamaño del canvas

        // para que se mantenga

        video.addEventListener('loadedmetadata', function (e) {

            var relation =  e.target.videoWidth/e.target.videoHeight;

            canvas.width = 300;

            canvas.height = 300/relation;

        }, false);

        // Capturamos el frame actual del video

        document.getElementById('snap').addEventListener('click', function (e) {

            if (localUserMedia) {

                ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

            }

        }, false);

        // Callback de error de getUserMedia

        var onErrorCallback = function (e) {

            console.log('Error!', e);

        };

        // Capturamos el video con getUserMedia y lo

        // mandamos a un vídeo

        navigator.getUserMedia({ video: true }, function (localMediaStream) {

            var video = document.querySelector('video');

            video.src = window.URL.createObjectURL(localMediaStream);

            localUserMedia = localMediaStream;

        }, onErrorCallback);

    </script>

</body>

</html>

He puesto comentarios en el código para que sea más fácil de seguir. Pero la clave está en que ahora al pulsar el botón de “snapshot” utilizamos drawImage para volcar el contenido del frame actual del vídeo al canvas.

Una vez tenemos la imagen en un canvas ya tenemos vía libre! Por un lado podríamos utilizar el propio canvas para permitir que el usuario manipule la imagen y luego subirla via Ajax (ver mi post Crea tu propio Instagram) o bien podemos directamente volcar el contenido del canvas en una imagen (utilizando toDataURL del propio canvas).

No voy a poner en este post como subir los datos del canvas al servidor porque sería repetir lo que puse en el post de Crea tu propio Instagram (Paso 5 del post).

En fin… hemos visto como gracias a WebRTC podemos (podremos) hacer algo que hasta hace poco parecía fuera totalmente de las posibilidades de la web: el acceso a dispositivos locales tales como micro y webcam. Y WebRTC no se queda ahí! Según la especificación será posible montar meetings on-line utilizando plataforma 100% web! Pero esta parte está todavía en una fase muy experimental e inicial de implementación!

Un saludo!

con 2 comment(s)
Archivado en:

Aaahhh… los punteros son una de las bestias negras del desarrollo. Desterrados de los dominios de los lenguajes orientados a objetos “modernos” como Java por ser demasiado “próclives a errores” los punteros se han convertido en una especie de ser mitológico, temido por muchos desarrolladores que tiemblan cuando ven un asterisco dando vueltas por ahí… Incluso C# los tiene medio apartados por ahí, rodeados de unsafes por todas partes.

¿Qué es un puntero?

Un puntero no es nada más que una variable normal y corriente pero que su valor en lugar de ser un entero, o un carácter, o un número decimal es una dirección de memoria:

int *a;

int i=10;

a=&i;

La primera línea declara un puntero a int. No hay un tipo de datos específico para puntero, en su lugar se pone un asterisco que sigue al tipo de datos.

Nota: El tipo de datos de la variable a (el puntero) es int*. Que en mi código haya un espacio entre el int y el * y ninguno entre el * y la a es porque el parser es así de majo y me deja hacerlo. Verás otra gente que teclea int* a; y es equivalente (y estrictamente hablando más cierto).

La segunda línea es obvia (declara un int) y la tercera línea utiliza el operador de referencia (&) para obtener la dirección de la variable i y asignarla al puntero a.

En este punto el valor de a es la dirección de memoria de la variable i. Si tengo un puntero puedo modificar el contenido de dicho puntero utilizando el operador de dereferencia (*):

*a=11;

En este punto el contenido del puntero a vale 11. O dicho de otra manera: el valor de posición de memoria a la que apuntaba el puntero ha pasado a valer 11. ¿Y qué había en esta posición de memoria? Pues la variable i. Así si ahora imprimo la variable i veré que vale 11.

O de forma más clara, *a es equivalente a i. Modificar el valor de *a implica modificar el valor de i (y a la inversa, modificar el valor de i implica modificar el valor de *a).

¿Por qué son peligrosos los punteros?

Per se, los punteros no son peligrosos. Son simplemente un alias para acceder a una posición de memoria. Pues entonces, ¿qué es lo que los hace peligrosos?

Pues su peligro reside en lo que se llama aritmética de punteros y que permite modificar no el contenido de un puntero si no el propio puntero en sí. Eso significa que un puntero tiene la capacidad de apuntar a direcciones arbitrarias de memoria:

int *a;

int i=10;

a=&i;

a = a + 0x300;

*a=11;

Fíjate en la penúltima línea: Le estamos sumando 0x300 (768) a la dirección de a. De forma que ahora a apunta 768 elementos más allá de la dirección de memoria de i. Cuando modifico el contenido de a, estoy modificando una dirección de memoria arbitraria y lo más normal es que:

image

El otro problema típico de los punteros, no es tanto de los punteros en sí, si no del runtime del lenguaje (me da igual si es C/C++ o Objective-C). En runtimes dotados de garbage collector (como el CLR), el propio sistema se encarga de destruir aquellos objetos que no se usan. ¿Y como sabe el runtime que nadie usa un objeto? Pues, porque no hay referencias que apunten a él. El hecho de tener un GC nos garantiza de que las referencias siempre apuntan a un objeto válido (ya que el GC no destruirá un objeto mientras tenga referencias que lo apunten). Pero, que ocurre si no tenemos GC? Pues que puedo tener dos referencias que apunten a un objeto y alguien puede destruir este objeto. Cuando luego más tarde uso una de esas referencias para acceder al objeto, me puede dar un error ya que en la dirección de memoria apuntada por esta referencia ya no está el objeto, hay otros datos. Este error viene a ser el contrario del NullReferenceException: En lugar de tener un error porque la referencia NO apunta a nada (vale null), tengo un error porque la dirección de memoria a la que apunta la referencia ya no contiene el objeto que contenía porque este ha sido destruído.

He estado usando en todo este párrafo la palabra referencia y no puntero, para poner de manifiesto que podríamos tener este error también si usáramos un lenguaje basado en referencias (y no en punteros) como C# si tuviesemos una gestión manual de memoria. La principal causa de errores de C/C++ es la gestión manual de memoria, no los punteros en sí. En los lenguajes que tienen gestión manual de memoria se denomina con el término dangling pointer a un puntero que apunta a una dirección de memoria donde ya NO hay el objeto que había.

No voy a entrar en las diferencias entre punteros y referencias (porque son muy tenues y depende un poco de cada lenguaje). Quédate con que tanto un puntero como una referencia apuntan a una dirección de memoria. Si tienes GC puedes estar seguro de que en esta dirección habrá un objeto válido. Si no tienes GC entonces NO puedes estar seguro de esto y ahí empiezan realmente tus problemas. La única característica que tienen los punteros y no tienen las referencias es la aritmética de punteros.

¿Cual es la posición de Objective-C?

Primero, Objective-C es un superconjunto de C (eso significa que todo el código C es código Objective-C válido). C tiene punteros y aritmética de punteros así que Objective-C también.

Lo que diferencia Objective-C de C es la gestión de memoria. En C (y en C++) la gestión es totalmente manual: Cuando quieres crear un objeto debes reservar la memoria una sola vez y luego liberarla una sola vez cuando ya nadie más vaya a usar el objeto.

Òbjective-C usa un contador de referencias para ello: El runtime mantiene un contador que indica cuantos punteros apuntan a un objeto en un momento dado. Cuando dicho contador llega a 0 el runtime destruye el objeto. Hay dos modelos de gestión de memoria dentro del runtime:

  1. Manual: Nosotros somos los encargados de indicarle al runtime cuando queremos que incremente y decremente dicho contador. Este modelo de memoria adolece de los mismos problemas que tiene C/C++ para gestionar la memoria (memory leaks si no decrementamos suficientes veces el contador o dangling pointers si lo decrementamos demasiadas veces).
  2. Automática (ARC): El encargado de decrementar o incrementar el contador es el compilador. La verdad es que ARC es muy sencillo de usar y nos permite desarrollar casi, casi, casi como si tuviesemos un GC.

Nota: El runtime de Objective-C soporta también el uso de Garbage Collector. Pero como en iOS no se puede usar, no hablaré del GC de Objective-C. Además su uso está marcado como obsoleto en favor de ARC.

En esta serie de posts veremos tanto la gestión manual (MRC) como la automática (ARC) de memoria.

Desde el punto de vista de un desarrollador de C# debes quedarte con lo siguiente:

  1. Si usas ARC la forma de desarrollar será muy parecida a C#: vamos a crear objetos y nos despreocuparemos de liberar la memoria. ARC lo hará por nosotros.
  2. A diferencia de C++ que admite el paso por valor de objetos, en Objective-C se admite tan solo paso por referencia a través de punteros. Eso lo hace más parecido a C# (cuando se pasa un objeto no se pasa por valor, si no que se pasa una referencia a dicho objeto).

Bueno… lo dejamos aquí por el momento. En el siguiente post de la serie, más ;-)

Saludos a todos!

con 2 comment(s)
Archivado en: ,

Muy buenas!

En un proyecto en el que estoy trabajando ha surgido la necesidad de pasarle via GET una lista de ids con los que hacer algo. La acción del controlador FinishersController está declarada de la siguiente manera:

public IEnumerable<TrackingAndCompetitorDTO> GetByRaces(IEnumerable<int> id)

{

    return null;

}

Ahora viene el momento de llamar al controlador:

  • /api/Finishers/10,20,30 –> Devuelve un 404
  • /api/Finishers/10 –> Enlaza a al acción pero id es null
  • /api/Finishers/?id=10,20,30 –> Enlaza a la acción pero id es null
  • /api/Finishers/?id=10&id=20&id=30 –> Enlaza a la acción pero id es null
  • /api/Finishers/?id[0]=10&id[1]=20&id[2]=30 –> Enlaza a la acción pero id es null

Si tienes experiencia en ASP.NET MVC entenderás que las tres primeras fallen es comprensible (también fallarían en MVC). Pero las dos últimas en MVC funcionarían correctamente… ¿entonces por qué fallan en WebApi?

La forma en como se realiza el enlace de parámetros de la request hacia los controladores es totalmente diferente en WebApi que en MVC. Insisto: WebApi es otra cosa completamente distinta aunque luzca muy parecida a ASP.NET MVC y aunque vengan juntos. No des por supuesto nada de lo que sabes en ASP.NET MVC para WebApi. Algunas cosas funcionan igual, otras, completamente distinto.

En ASP.NET MVC el enlace desde la request hacia los controladores se realiza mediante los ValueProviders y los Model Binders. Los primeros son los encargados de inspeccionar la request (formdata, querystring, pathinfo, headers, …) y dejar los valores en un sitio común. Luego los model binders leen de “ese sitio común” y construyen los valores de los parámetros que el controlador recibe.

WebApi añade a estos dos conceptos, un tercero: los media type formatters. Bien, recuerda siempre la gran diferencia entre WebApi y MVC: En MVC se usa un buffer para guardar la petición (y la respuesta). Eso significa que los value providers pueden leer n veces el cuerpo de la petición sin que de error. En WebApi NO. WebApi es, por decirlo de algún modo, stream-based. Nuestros media type formatters reciben un stream y pueden leer de él una sola vez. Por lo tanto tan solo un media type formatter puede leer el cuerpo de la petición.

Pero ojo… he dicho que WebApi añade el concepto de media type formatters, porque en WebApi también se usan value providers y model binders. ¿Cuando? Pues para enlazar parámetros que no provienen del cuerpo de la petición (o sea, usualmente de la URL). Esa es la norma báscia:

  1. El parámetro no está en el cuerpo? Se enlaza vía un model binder
  2. El parámetro está en el cuerpo de la petición? Se enlaza via un media type formatter

Webapi hace ciertas asunciones sobre si un parámetro debe enlazarse via model binder o media type formatter. Básicamente: si es tipo simple se usará un model binder. Si es un tipo complejo se usará un media type formatter. Por lo tanto, por defecto nuestro parámetro IEnumerable<int> al NO ser un tipo simple se intenta enlazar mediante un media type formatter. De ahi que no se encuentren datos porque los media type formatters miran tan solo el cuerpo y en nuestro caso está vacío.

¿Y como podemos modificar este comportamiento? Pues bien:

  1. Si decoras un parámetro con [FromUri] indicas a WebApi que este parámetro vendrá en la URL
  2. Si decoras un parámetro con [FromBody] indicas a WebApi que este parámetro vendrá en el cuerpo de la petición
  3. Si decoras un parámetro con [ModelBinder] puedes especificar un Model Binder específico para tu parámetro. (De hecho [FromUri] deriva de [ModelBinder]).
  4. Y recuerda: El cuerpo de la petición tan solo puede leerse una vez. Por lo tanto si tu controlador recibe dos parámetros complejos tan solo uno puede ser procesado mediante un media type formatter (y venir en el cuerpo). El otro debe ser procesado mediante un model binder y venir en alguna otra parte que NO sea el cuerpo de la petición  (y estar decorado con [ModelBinder] o [FromUri]).

Bueno… ahora ya ves la solución a nuestro problema no? Basta con decorar el parámetro con [FromUri]:

public IEnumerable<TrackingAndCompetitorDTO> GetByRaces([FromUri] IEnumerable<int> id)

{

    return null;

}

Con esto estamos forzando a WebApi a que enlace este parámetro usando los value providers y model binders. Y ahora la URL

  • /api/Finishers/?id=10&id=20&id=30 –> Funciona correctamente.

Curiosamente la URL

  • /api/Finishers?id[0]=10&id[1]=20&id[2]=30 NO funciona, pero eso ya se debe a diferencias de como está implementado el Model binder de WebApi y el de MVC cuando tratan con colecciones…

Saludos!

con 1 comment(s)
Archivado en:

Bueno… Honestamente: este post viene a colación de que se estuvo hablando por Linkedin de dedicar hoy (4 de Marzo) un post o algo a WebApi. He de decir que personalmente, no suelo planificar de que escribo. Es decir, tengo varias series abiertas de posts, montones de artículos en borrador y luego voy escribiendo cosas según me van viniendo.

WebApi es un tema que he tratado bastante en mi blog. Uno de los temas que NO he tratado y que era un buen candidato era la exposición de servicios OData usando WebApi. Pero Marc Rubiño se me adelantó y lo contó de manera fenomenal en su blog. Así que pensando temas sobre los que poder hablar al final he optado por este:

image

Este error se produce si haces algo como p. ej:

public class BeersController : ApiController

{

    // GET api/values

    public IEnumerable Get()

    {

        return new ArrayList() {new {Name = "ufo", Value = 10}, new {Age = 20, Name = "Gag"}};

    }

}

Es decir si devuelves un objeto anónimo (en mi caso estoy devolviendo una colección de objetos anónimos pero si devuelves uno solo ocurre lo mismo).

La primera opción es pensar que WebApi no soporta objetos anónimos. Pero es falso. WebApi, como tal, no tiene problema alguno en devolver objetos anónimos. Si llamo al mismo servicio pero usando una cabecera Accept application/json para que me devuelva el resultado en json:

image

En json funciona correctamente. Y es que el problema no está en WebApi en sí… Si no en el serializador de XML que WebApi usa.

Básicamente y resumiendo: El serializador de XML de WebApi no soporta objetos anónimos.

¿Y hay solución para ello?

¡Pues claro! Crearte tu propio serializador de XML y luego un media type formatter asociado que lo use.

Lo primero es eliminar el media type formatter de XML que viene por defecto. Para ello, en Application_Start:

config.Formatters.Remove(config.Formatters.XmlFormatter);

Con esto eliminamos el media type formatter de XML y por lo tanto WebApi deja de dar soporte a XML. Este media type formatter que viene por defecto utiliza o bien DataContractSerializer o bien XmlSerializer para serializar los datos y ninguno de los dos tiene soporte para tipos anónimos. Lo primero es crearnos un serializador de XML propio que tenga soporte para tipos anónimos.

Yo he usado uno (muy sencillo y nada “personalizable”) que he encontrado en http://stackoverflow.com/questions/2404685/can-i-serialize-anonymous-types-as-xml. Simplemente lo he completado para que tenga soporte para colecciones de elementos. El código del serializador es:

public static class CustomXmlSerializer

    {

        private static readonly Type[] WriteTypes = new[] {

            typeof(string), typeof(DateTime), typeof(Enum),

            typeof(decimal), typeof(Guid) };

 

        public static bool IsEnumerable(this Type type)

        {

            return type.GetInterface("IEnumerable") != null;

        }

 

        public static bool IsSimpleType(this Type type)

        {

            return type.IsPrimitive || WriteTypes.Contains(type);

        }

        public static XElement ToXml(this object input)

        {

            return input.ToXml(null);

        }

 

        public static XElement ToXml(this object input, string element)

        {

            if (input == null)

                return null;

 

            if (string.IsNullOrEmpty(element))

                element = "object";

            element = XmlConvert.EncodeName(element);

            var ret = new XElement(element);

 

            if (input != null)

            {

                var type = input.GetType();

                var props = type.GetProperties();

 

                var elements = from prop in props

                               let name = XmlConvert.EncodeName(prop.Name)

                               let val = prop.GetValue(input, null)

                               let value = prop.PropertyType.IsSimpleType()

                                    ? new XElement(name, val)

                                    : val.ToXml(name)

                               where value != null

                               select value;

 

                ret.Add(elements);

            }

 

            return ret;

        }

 

    }

El código es bastante simple no? Básicamente itera sobre todas las propiedades del elemento y usa Linq to XML para convertirlas a XML.

Una vez tenemos un serializador que nos soporta tipos anónimos, ha llegado el momento de crear un media type formatter que lo use. Recuerda que los media type formatters son las clases que usa WebApi para leer/escribir desde/el stream de la petición/respuesta.

En nuestro caso nuestro media type formatter soportará escritura solamente. Eso signfica que podremos devolver tipos anónimos en XML pero NO soportaremos la lectura de XML en las peticiones (es decir no aceptaremos peticiones cuyo cuerpo sea un XML (al menos no con este media type formatter)).

El código es realmente simple:

public class CustomXmlFormatter : BufferedMediaTypeFormatter

{

    public CustomXmlFormatter()

    {

        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));

        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));

    }

 

    public override bool CanReadType(Type type)

    {

        return false;

    }

 

    public override bool CanWriteType(Type type)

    {

        return true;

    }

 

    public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)

    {

        using (var writer = new StreamWriter(writeStream))

        {

            WriteAny(writer, type, value);

        }

 

    }

 

    private void WriteAny(StreamWriter writer, Type type, object value)

    {

        if (type.IsEnumerable()) WriteCollection(writer, type, value);

        else WriteObject(writer, type, value);

    }

 

    private void WriteObject(StreamWriter writer, Type type, object value)

    {

        var xml = value.ToXml();

        writer.Write(xml.ToString());

    }

 

    private void WriteCollection(StreamWriter writer, Type type, object value)

    {

        var collection = value as IEnumerable;

        writer.Write("<collection>");

        foreach (var item in collection)

        {

            if (item != null)

            {

                WriteAny(writer, item.GetType(), item);

            }

        }

        writer.Write("</collection>");

    }

}

¡Listos! Simplemente redefinimos los métodos CanWriteType para indicar que podemos serializar cualquier tipo y CanReadType para indicar que NO podemos leer ninguno.

Luego redefinimos el método WriteToStream y usamos los métodos extensores definidos en nuestro serializador de xml propio para obtener el XML y escribirlo en el stream de salida.

Ahora tan solo nos queda registrar este media type formatter para que WebApi lo use:

config.Formatters.Add(new CustomXmlFormatter());

Y ahora ya estamos. Si ejecutamos de nuevo la llamada a nuestro servicio con una cabecera Accept que prefiera XML:

image

Resumen

  • WebApi usa el concepto de media type formatter para decidir que clase usar para serializar los datos devueltos por los controladores (también para leer los datos enviados a los controladores)
  • Un media type formatter básicamente se asocia a uno o varios tipos mime (eso se hace en el constructor de cada media type formatter)
  • El media type formatter por defecto que viene en WebApi para serializar XML usa DataContractSerializer o XmlSerializer y NO tiene soporte para tipos anónimos
  • Para habilitar el soporte de tipos anónimos debemos pues crearnos un media type formatter propio que use un serializador de XML que admita tipos anónimos.
  • Para JSON no es necesario hacer nada de esto: los tipos anónimos están soportados de serie.

Espero que te haya resultado interesante.

Saludos!

con 1 comment(s)
Archivado en:

Buenas! Este post surge a raíz del comentario de Felix Manuel en mi post anterior Inyección de dependencias per-Request. En él Felix comentaba que le gustaría ver algo sobre autenticación y autorización de WebApi… así que vamos a ello.

Todo lo que comentaré en este post va destinado a servicios WebApi que queramos tener en internet. No hablaré nada de otros escenarios como intranets que pueden tener otras soluciones.

Vamos a partir del template de proyecto ASP.NET MVC4 – WebApi

AuthorizeAttribute

En ASP.NET MVC si queremos añadir seguridad a un controlador, basta con que usemos [Authorize] para decorar todas aquellas acciones que requieran seguridad.

¿Qué ocurre si aplicamos lo mismo a WebApi?

public class SecureController : ApiController

{

    [Authorize]

    public string GetSecure()

    {

        return "Acces granted!";

    }

}

Asegúrate de usar el AuthorizeAttribute que está en System.Web.Http, no el que está en System.Web.Mvc!

Con esto, si realizo una llamada GET a /api/secure obtengo un mensaje de error (recuerda que puedes recibirlo en json o xml dependiendo de la cabecera accept que mandes):

<Error><Message>Authorization has been denied for this request.</Message></Error>

Bien… parece que el filtro realiza su trabajo, pero ¿qué hace exactamente? Para ello echemos un vistazo a su código fuente. El método que realiza el trabajo es IsAuthorized que tiene un código como el siguiente:

IPrincipal user = Thread.CurrentPrincipal;

if (user == null || !user.Identity.IsAuthenticated)

{

    return false;

}

Bueno… pues parece que está claro: [Authorize] se limita a validar que haya un CurrentPrincipal que esté autorizado.

¿Así que la pregunta nos rebota: quien crea y coloca el IPrincipal autorizado?

Para verlo, realicemos una prueba: Vamos a crear un controlador nuevo con un método GET (para llamarlo fácilmente desde el navegador) para autenticarnos, usando FormsAuthentication (el mecanismo clásico de autenticación de ASP.NET).

public class AuthController : ApiController

{

    public string Get(string id)

    {

        FormsAuthentication.SetAuthCookie(id ?? "FooUser", false);

        return "You are autenthicated now";

    }

}

Vale, ahora puedes probar de realizar desde el navegador:

  1. Una llamada a /api/Auth/XXX (XXX nombre de usuario que quieras)
  2. Otra llamada a api/Secure y ver… como sigues sin estar autenticado.

La llamada  a FormsAuthentication lo que hace es colocar una cookie (llamada comúnmente ASPXAUTH) que es la cookie de autenticación de asp.net. Pero por alguna razón la estamos obviando. Bien, esta razón es que NO hemos definido modelo de seguridad en web.config. Si lo abres verás que hay la línea:

<authentication mode="None" />

Modificala para que sea:

<authentication mode="Forms" />

¡Y listos! Si repites el par de llamadas anterior, ahora verás como la llamada a /api/Secure si que te funciona, ya que ahora el proveedor de autenticación Forms utiliza la cookie y en base a ella crea un IPrincipal y lo autentica.

¿Qué, sencillo no? Pues no tanto…

Cuando usar y cuando no usar autenticación por Forms

Usar la autenticación Forms NO es mala idea si tus servicios WebApi van a ser utilizados tan solo desde el contexto de una aplicación web que antes haya autenticado al usuario. ¿Y por qué? Bueno… pues porque las cookies son algo muy del mundo web.

Pero si creas una API para ser usada en otros dispositivos o aplicaciones (p. ej. una aplicación nativa de móvil) usar cookies para autenticarnos es una mala idea, ya que entonces obligas a quien usa tus servicios a que ponga código para leer las cookies recibidas y enviarlas de nuevo. Eso, si usas un navegador, lo hace el navegador automáticamente.

Así que lo dicho: si tus servicios WebApi van a ser utilizados tan solo dentro de una aplicación web (p. ej. para darte soporte a llamadas Ajax) puedes usar el mecanismo que acabamos de ver. En este caso, habitualmente, los controladores WebApi se despliegan en el mismo proyecto que los de ASP.NET MVC (si no recuerda que deberás lidiar con cross-domain). Y quien autentica al usuario (o sea quien tiene la llamada a FormsAuthentication.SetAuthCookie) es un controlador de MVC (el que responde al formulario de Login, claro). Y los controladores WebApi los tienes decorados tan solo con el [Authorize].

Pero vamos a olvidarnos de este escenario y planteemos otro: estás creando simplemente una API. El consumidor de esta API va a ser una aplicación de móvil (p. ej.) o dicho de otro modo, no va a ser un browser.

Entonces NO debemos usar autenticación por Forms y debemos buscar otros mecanismos. La pregunta es… ¿Cuales?

Autenticación básica

Bueno… exploremos esta opción. La autenticación básica de HTTP es muy sencilla. Aunque tiene un flujo que empieza por mandar un 401 con una cabecera específica todo eso lo obviaremos (eso es porque los navegadores sepan que hay un proceso de autenticación pero en nuestro caso el cliente no es un navegador).

Asi, si nos centramos en como es una petición autenticada que use autenticación básica, es muy sencillo: Tiene una cabecera Authorization con este formato:

Authorization: Basic username:password

La única salvedad es que username:password se envía codificado en BASE64.

Vale… ¿y como implementamos eso en WebApi? Pues hay varias maneras, p. ej. nos podríamos crear un filtro de autorización propio:

public class BasicAuthorizeAttribute : AuthorizationFilterAttribute

{

    public override void OnAuthorization(HttpActionContext actionContext)

    {

        var headers = actionContext.Request.Headers;

        if (headers.Authorization != null && headers.Authorization.Scheme == "Basic")

        {

            try

            {

                var userPwd = Encoding.UTF8.GetString(Convert.FromBase64String(headers.Authorization.Parameter));

                var user = userPwd.Substring(0, userPwd.IndexOf(':'));

                var password = userPwd.Substring(userPwd.IndexOf(':') + 1);

                // Validamos user y password (aquí asumimos que siempre son ok)

            }

            catch (Exception)

            {

                PutUnauthorizedResult(actionContext, "Invalid Authorization header");

            }

        }

        else

        {

            // No hay el header Authorization

            PutUnauthorizedResult(actionContext, "Auhtorization needed");

        }

    }

 

    private void PutUnauthorizedResult(HttpActionContext actionContext, string msg)

    {

        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)

        {

            Content = new StringContent(msg)

        };

    }

}

Nota: Para realizar tareas de autorización (como este caso) debemos usar un filtro de autorización. No uses para ello un filtro de acción (es decir, no derives de ActionFilterAttribute y redefinas OnActionExecuting). La razón es que los filtros de autorización se ejecutan antes que los filtros de acción, ya que precisamente su tarea es esa: autorizar la llamada a una acción. No está de más que te descargues el poster con el ciclo de vida de una petición WebApi de http://www.microsoft.com/en-us/download/details.aspx?id=36476

Ahora tan solo debemos decorar la acción Secure del controlador con [BasicAuthorize] en lugar de [Authorize] y ya tenemos la autenticación básica implementada (para pasar de cadena a base64 puedes usar cualquiera de las páginas que hay por ahí que lo hacen como http://www.motobit.com/util/base64-decoder-encoder.asp):

image

Si eliminar el tag Authorization verás como la respuesta pasa a ser un 401.

Por supuesto, la autenticación básica de HTTP es tan insegura que tan solo debería usarse sobre HTTPS.

Vale… pero quizá te estás preguntando si tenemos más alternativas que usar un filtro de autorización para validar si una petición está autorizada. Y la respuesta es que sí. Tenemos al menos dos más:

  1. Usar un Message Handler
  2. Usar un módulo de autenticación de ASP.NET

¿Te parece si exploramos ambas?

Usando un Message Handler

Los Message Handlers se ejecutan incluso antes que los filtros de autorización. Es más, se ejecutan incluso antes de que el pipeline de WebApi seleccione un controlador así que son un lugar propicio para poner código de autorización.

Los Message Handlers tienen la potestad de generar ellos mismos un resultado y entonces todo el resto de la pipeline de WebApi es eliminada. Es decir es posible que un Message Handler intercepte una petición, genere un resultado y entonces esta petición no será transferida al controlador a la cual debería haber llegado.

Esto puede parecer que hace que los Message Handlers sean un buen sitio para autorizar peticiones y lo son, pero NO son un buen sitio para rechazar peticiones no autorizadas. Me explico: sigamos con nuestro ejemplo de autenticación básica.

Si usas un Message Handler y rechazamos todas las peticiones que NO tengan la cabecera Authorize entonces tendremos un problema si necesitamos tener alguna parte de la API pública. Un Message Handler se ejecuta siempre. Para todas las peticiones.

Si quieres usar un Message Handler para autorización lo que debes hacer no es rechazar las peticiones que estén autorizadas (en nuestro caso que no tengan la cabecera Authorize). No. En su lugar debes autorizar aquellas peticiones válidas. ¿Como? Colocando un IPrincipal en el Thread. Y luego, en los controladores utilizas [Authorize].

Veamos un ejemplo rápido:

public class BasicAuthMessageHandler : DelegatingHandler

{

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,

        CancellationToken cancellationToken)

    {

        var headers = request.Headers;

        if (headers.Authorization != null && headers.Authorization.Scheme == "Basic")

        {

            var userPwd = Encoding.UTF8.GetString(Convert.FromBase64String(headers.Authorization.Parameter));

            var user = userPwd.Substring(0, userPwd.IndexOf(':'));

            var password = userPwd.Substring(userPwd.IndexOf(':') + 1);

            // Validamos user y password (aquí asumimos que siempre son ok)

            var principal = new GenericPrincipal(new GenericIdentity(user), null);

            PutPrincipal(principal);

        }

 

        return base.SendAsync(request, cancellationToken);

    }

 

    private void PutPrincipal(IPrincipal principal)

    {

        Thread.CurrentPrincipal = principal;

        if (HttpContext.Current != null)

        {

            HttpContext.Current.User = principal;

        }

    }

}

Este Message Handler crea y coloca un GenericPrincipal si la cabecera Authorize está presente. Luego en el controlador debemos usar [Authorize] para aquellas acciones que requieran de usuario autenticado.

Acuerdate de registrar el Message Handler desde Application_Start:

config.MessageHandlers.Add(new BasicAuthMessageHandler());

Por lo tanto estamos usando una combinación entre un Message Handler y un filtro de autorización (Authorize).

Usando un módulo de autenticación

Bien… exploremos esta otra alternativa. Ahora vamos a usar un módulo de autenticación (un HttpModule) para realizar las mismas tareas que realiza el Message Handler, es decir para crear un IPrincipal.

¿Y qué diferencias hay entre usar un HttpModule o un Message Handler? Pues buena pregunta, básicamente las siguientes:

  1. Un HttpModule es algo específico de IIS. No lo puedes usar en aplicaciones WebApi que no estén hospedadas en IIS. Por otro lado un Message Handler es algo propio de WebApi.
  2. Un HttpModule ve todas las peticiones que estén dirigidas al pipeline de ASP.NET. Sean peticiones de WebApi o no. Un Message Handler tan solo ve las peticiones WebApi.
  3. Un HttpModule se ejecuta antes en el pipeline que un Message Handler. De hecho se ejecuta antes que cualquier parte de WebApi.

Por norma general, es preferible usar un HttpModule si sabes que vas a hospedar tu WebApi siempre en un IIS. Si no, si quieres tener la posibilidad de hospedar tu WebApi en otros procesos entonces usa un Message Handler.

¿Listo para nuestro módulo de autenticación? Aquí lo tienes:

public class BasicAuthModule : IHttpModule

{

     public void Init(HttpApplication context)

     {

         context.AuthenticateRequest += OnAuthenticateRequest;

     }

 

     private void OnAuthenticateRequest(object sender, EventArgs e)

     {

         var application = (HttpApplication)sender;

         var request = new HttpRequestWrapper(application.Request);

 

         var headers = request.Headers;

         var authData = headers["Authorization"];

         if (!string.IsNullOrEmpty(authData))

         {

             var scheme = authData.Substring(0, authData.IndexOf(' '));

             var parameter = authData.Substring(scheme.Length).Trim();

             var userPwd = Encoding.UTF8.GetString(Convert.FromBase64String(parameter));

             var user = userPwd.Substring(0, userPwd.IndexOf(':'));

             var password = userPwd.Substring(userPwd.IndexOf(':') + 1);

             // Validamos user y password (aquí asumimos que siempre son ok)

             var principal = new GenericPrincipal(new GenericIdentity(user), null);

             PutPrincipal(principal);

         }

     }

 

     public void Dispose()

     {

 

     }

 

     private void PutPrincipal(IPrincipal principal)

     {

         Thread.CurrentPrincipal = principal;

         if (HttpContext.Current != null)

         {

             HttpContext.Current.User = principal;

         }

     }

}

Puedes ver como el código es muy parecido al del Message Handler. Ahora debemos registrarlo. Los HttpModules se registran en el web.config, dentro de la sección modules de system.webServer:

<system.webServer>

  <modules>

    <add name="BasicAuthHttpModule"

      type="MvcSecureDemo.BasicAuthModule, MvcSecureDemo"/>

  </modules>

</system.webServer>

La situación es la misma que teníamos en el caso del Message Handler: en los controladores debemos usar [Authorize] para proteger las acciones que requieran usuarios autenticados.

Más allá de autenticación básica

Vale… hemos explorado como securizar nuestros servicios WebApi (a través de filtros de autorización, Message Handlers y HttpModules). Lo hemos hecho a partir de la autenticación básica de HTTP porque es muy sencilla y así el código no queda liado. ;)

Pero autenticación básica NO es un buen mecanismo. No lo es, a menos que uses HTTPS, ya que el login y el password viajan en texto plano (recuerda que Base64 no es cifrado).

Bien, ¿qué debéríamos hacer si no tenemos HTTPS? Una solución pasa por encriptar realmente el login y el password. Esto sigue teniendo un riesgo, tan pequeño como seguro sea nuestro algoritmo de encriptación y segura esté la clave que usemos).

Exploremos otra alternativa, otra que signifique que ni el login ni el password viajan por la red. Nadie con un sniffer será capaz de saber nuestra password escuchando los mensajes que nos intercambiamos con el servidor… Veamos que deberíamos hacer.

Partamos de la suposición de que tanto el cliente (una aplicación móvil p. ej.) y el servidor comparten un código, llamésmole código de acceso. Da igual como lo han compartido (p. ej. a través de un email). Lo importante es que lo tienen.

Entonces básicamente si el cliente adjunta este código en una cabecera (sigamos suponiendo la misma cabecera Authorization) la llamada se considera autenticada y en caso contrario se considera válida. Este código (insisto: obtenido previamente) identifica al cliente (la aplicación móvil) y al usuario de dicho cliente, por lo que adjuntando este código una aplicación puede hacer peticiones en nombre del usuario.

Vale, no parece que hayamos progresado mucho verdad: alguien con un sniffer pilla nuestra petición y ya tiene el código. Muy seguro el sistema no parece.

Bueno… sigamos suponiendo. Sigamos suponiendo que además de este código, tanto cliente como servidor comparten otro código. Un código secreto. Lo de secreto viene porque este segundo código jamás viajará por la red. Nunca. Lo comparten al inicio de los tiempos (p. ej. por mail) y luego el cliente se lo guarda y el servidor también.

Ahora lo que debe hacer el cliente es mandar la petición pero usará el código secreto para generar un hash del código de acceso. Y la cabecera Authorization contendrá este hash. Cuando el servidor recibe una petición del cliente debe:

  • Calcular el hash del código de acceso del cliente usando el código secreto (que ambos comparten)
  • Si el resultado es el mismo que ha enviado el cliente, la petición se considera autenticada.

Vale… hay un problemilla ahora. El código secreto debe ser distinto por cada cliente, pero si tan solo recibimos en la cabecera Authorization el hash del código de acceso… ¿como sabemos qué código secreto debemos usar para calcular el hash?. Es decir, como sabemos de qué cliente es la petición.

Pues nada. Hacemos que cliente y servidor comparten otro elemento: un identificador de cliente. Ahora el cliente en la cabecera Authorization mandará su código identificador y el hash de su código de acceso (calculado con su código secreto). Cuando el servidor recibe la petición, sabe de que cliente es (por el identificador de cliente de la cabecera Authorization) y podrá usar el código secreto de este cliente para calcular el hash del código de acceso de este cliente y validar que sea el mismo que el clienté envía.

¿Te parece seguro el sistema ahora? No lo es en absoluto. Alguien que pille una petición podrá hacer lo que quiera, porque tendrá el hash del código de acceso. Estamos igual que al principio… pero a un paso de la solución.

El problema de que si nos pillan una petición luego ya nos puedan suplantar siempre es porque estamos calculando un hash de algo que jamás se modifica: el código de acceso. Porque en lugar de calcular un hash de dicho código no calculamos un hash de una cadena que esté compuesta de:

  1. La URL de destino
  2. Los datos en query string/formdata
  3. El código de acceso

Los datos 1 y2 son variables (distintos por cada petición que hagamos). Así cada petición tendrá un hash distinto. Cuando el servidor reciba la petición calculará esta cadena con la URL, la query string/formdata y el código de acceso del cliente y usará el código secreto del cliente para calcular el hash. Si coinciden la petición queda autenticada.

Ahora si alguien nos pilla una petición con un sniffer, tan solo podrá hacer una cosa: enviarla otra vez al servidor. No podrá modificarla, porque si lo hace (p. ej. modifica la querystring para cambiar un parámetro) automáticamente el hash pasa a ser inválido. Y no puede construir un hash nuevo porque no tiene acceso al código secreto.

Así pues un hacker nos puede pillar peticiones y puede ver su contenido. Hasta puede guardarlas y reenviarlas al servidor más adelante. Pero NO puede crear peticiones falsas en nuestro nombre.

Vayamos un paso más allá. ¿Por qué no adjuntamos el tiempo en que se realiza la petición? Es decir el cliente añade en una cabecera la fecha y hora en que realiza la petición. Y usa este dato también para calcular el hash. Ahora el servidor puede validar que la fecha/hora que envía el cliente sea (aproximadamente) la actual. Si el servidor recibe una petición de un cliente con una fecha/hora de hace 4 minutos, la puede aceptar pero si es de hace 1 hora la puede rechazar, ya que seguramente es una petición interceptada, guardada y mandada luego. Dado que la fecha/hora se usa también para calcular el hash, el hacker que nos intercepte la petición no puede falsear este dato.

Incluso podríamos hacer algo más: añadir un número (llamésmole nonce) de secuencia. La idea es que dos peticiones del mismo cliente jamás pueden repetir el nonce. Por supuesto el nonce se manda en otra cabecera y también se usa para calcular el hash. Si ahora un hacker nos intercepta la petición y la intenta mandar al cabo de 3 minutos… será rechazada porque este nonce ya ha sido usado.

Ahora estamos protegidos de todos los ataques, excepto de un Man-in-the-middle. Ojo, no tenemos privacidad (tanto las peticiones como las respuestas van sobre HTTP en texto llano), pero nuestras credenciales están seguras y nadie puede crear peticiones en nuestro nombre ni guardarse peticiones válidas y enviarlas más adelante. Si requerimos privacidad y protección contra ataques tipo Man-in-the-middle entonces lo más rápido es usar HTTPS.

Por supuesto este sistema que he descrito, no me lo he inventado yo. Este sistema lo inventó Blain Cook y le dio el nombre de oAuth. Lo que he descrito aquí es (rápido y mal, por supuesto) el flujo básico de oAuth 1.0.

Las ventajas de oAuth son que en ningún momento las credenciales del usuario circulan por la red y que ofrece un buen balance de seguridad siempre y cuando el código secreto no se vea comprometido. Es pues una muy buena opción para proteger tus servicios WebApi.

Y por supuesto, usar otros mecanismos de autenticación no modifica en nada lo dicho en este post, tan solo “complica” el código que has de poner en tu filtro de autorización, message handler o HttpModule.

Un saludo!

con 6 comment(s)
Archivado en: ,

Muy buenas! Con este post inicio una serie de posts (como siempre, ni idea de cuantos van a ser) dedicado especialmente a desarrolladores de C# que quieran empezar con Objective-C. No soy un experto en Objective-C ni esta serie pretende que te conviertas en un experto en este lenguaje. Tampoco es un tutorial de Objective-C. Es simplemente una ayuda para todos aquellos desarrolladores con un background de C# y .NET que tengan curiosidad por ver como es el lenguaje de la manzana.

A lo largo de esta serie intentaré tocar el máximo número de temas y eso incluirá:

  • Lenguajes: Vamos a comparar las características intrínsecas de ambos lenguajes. Por intrínsecas me refiero a las características que no dependen de ninguna API o librería externa como sintaxis, tipos básicos, etc…
  • Herramientas: Por mucho que sean posts de Objective-C y de C# ambos lenguajes están muy enfocados a plataformas muy concretas y se suelen usar con herramientas muy concretas. Así que hablaré de XCode y de Visual Studio. Pero eso no quita que existan otras herramientas para ambos lenguajes.
  • Plataforma: Los lenguajes, si nos ceñimos a su definición, son agnósticos de plataforma. Pero vamos a terminar hablando de plataforma, porque todo lenguaje necesita una que le de soporte. En el caso de C# tenemos la BCL y luego todo .NET. En Objective-C tenemos Foundation y luego Cocoa y las apis de iOS (no hablaré de desarrollo para MacOS porque no tengo ni idea). Así que, en algún momento, compararemos las plataformas asociadas a cada lenguaje. Por supuesto esta comparación no será, porque no lo pretende porque no puede, ser una comparación exhaustiva.

Todos los posts de la serie estarán escritos des del punto de vista de alguien que viene (y por lo tanto conoce) el mundo de C# y .NET. Esta serie no busca en ningún momento un ganador. Por eso que nadie busque en las comparaciones vencedores y perdedores. Eso queda en el juicio de cada uno.

Y finalmente… la pregunta del gritón de dólares: ¿Necesito un ordenador de Cupertino para seguir la serie?

Pues para seguirla toda, toda, toda pues obviamente sí. Cuando hablemos de XCode o de Cocoa vas a necesitar un Mac si quieres “hacer ejemplos en vivo”.

De todos modos doy por supuesto que si te ha picado la curiosidad por Objective-C es que te estés planteando el desarrollar para iOS, por lo que necesitarás un Mac tarde o temprano :P Pero, es posible hacer muchos de los ejemplos que presentaré en un PC con Windows. Existe el runtime de Objective-C para Windows, por lo que si lo que pretendes es simplemente trastear con Objective-C podrás hacerlo desde Windows. Obviamente con limitaciones.

Luego ya podrás decidir si prefieres comprarte un Mac o bien prefieres contactar con Juan Fco. Miranda quien te guiará por los sinuosos caminos del Hackintosh.

Instalación de Objective-C en Windows

Bueno, el primer paso es ir a la página de descargas para Windows de GNUStep y descargarte:

  1. GNUstep MSYS System
  2. GNUstep Core
  3. GNUstep Devel

Luego instalatelos en este mismo orden. Yo por ejemplo los tengo instalados en D:\GNUStep

GNUStep consiste en una serie de herramientas, como gcc para compilar Objective-C y también tiene ports de parte de la plataforma (está Foundation y parte de Cocoa implementado).

GNUStep es para hombres y no viene con ningún IDE ni nada parecido. Aunque técnicamente no necesitas ninguno ya que GNUStep te habrá instalado make.exe. Yo no conozco ningún IDE de Objective-C para Windows por ahí (personalmente no uso Objective-C bajo Windows).

Para probar si GNUStep está bien instalado vamos a realizar nuestro HelloWorld.

Abre tu editor de texto favorito y crea un fichero llamado HelloWorld.m con el siguiente código:

#import <Foundation/Foundation.h>
int main (void)
{
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        NSLog (@"Hello, World!");
        [pool drain];
        return;
}

Guardalo como HelloWorld.m (la extensión .m es la que se suele usar para crear archivos Objective-C)

Ahora nos toca usar gcc.exe para compilar este archivo. Ello deberemos hacerlo desde línea de comandos. Abres una línea de comandos y te vas donde tengas el archivo HelloWorld.m y tecleas (todo en una misma línea):

gcc HelloWorld.m -I D:/GNUstep/GNUstep/System/Library/Headers -L D:/GNUstep/GNUstep/System/Library/Libraries -fconstant-string-class=NSConstantString -o HelloWorld.exe -lobjc -lgnustep-base

Las opciones que estamos usando son las siguientes:

  • -I <path_includes>: Path raíz de los archivos de cabecera
  • -L <path_libraries>: Path raíz de los archivos de librería
  • -fconstant-string-class <clase>: Nombre de la clase a usar cuando haya constantes string.
  • -o <fichero>: Nombre del fichero de salida
  • -lobjc: Para enlazar con la librería de objective C
  • -lgnustep-base: Para enlazar con la librería de GnuStep

Esto te debería compilar y generar el HelloWorld.exe… Si es así, ya estás listo para empezar a programar con Objective-C bajo Windows ;-)

En sucesivos posts veremos como compilar usando XCode (por si tienes un Mac) y empezaremos a ver las diferencias entre Objective-C y C#).

Nos vemos!

con no comments
Archivado en: ,

Bueno… Está por ahí Quique, que como se aburre se ha decidido a picarme un poco (pobre mortal ^_^).

Se ve que junto con Álex se han embarcado en resolver los problemas de Euler en distintos lenguajes… Álex se ha quedado con F# (el patito desconocido de .NET), mientras que Quique se ha armado con todo el poder de (mi amado) C#.

Dado que Toni Recio se ha sumado y ha pillado Javascript (quien lo ha visto y quien lo ve) yo he decidido participar, pero esta vez armado con todo el potencial de un lenguaje de programación funcional de verdad. Y el elegido ha sido Scala.

Cada post que haga con la solución aprovecharé para intentar comentar algo de Scala. Ya, Scala no es .NET, pero lo pongo en este blog porque no tengo otro y me da muuuucha pereza abrirlo. Así que… :p

Bueno, al tajo. El primer reto es sencillo (si todos son así, vamos bien): Sumar todos los números del 1 al 1000 que sean divisibles entre 3 ó entre 5. Pues al tajo:

package duel.Euler

object Euler1 extends App {

      println(

          (1 until 1000).filter(n=> (n % 3 == 0) || (n % 5 ==0 )).foldLeft(0)(_+_)

      );

}

¡Listos!

Si conocéis LINQ no hay mucho que añadir:

  1. La construcción (1 until 1000) es equivalente a Enumerable.Range
  2. filter es nuestro amado Where
  3. foldLeft equivale al Aggregate de LINQ. Usado con la sintaxis (_+_) equivale al Sum() de LINQ.

Y que narices es esto de foldLeft

Pues bien, foldLeft es una función existente en todas las colecciones de Scala que básicamente permite ejecutar una función que acepte dos parámetros (que serán sacados iterando por la colección). Además foldLeft proporciona un valor inicial.

La sintaxis básica de foldLeft es:

coleccion.foldLeft(valor_inicial) función_a_ejecutar

En nuestro caso está claro:

  1. La colección son los números de 1 a 1000 que son divisibles entre 3 ó 5
  2. El valor inicial es 0
  3. La función a ejecutar es (_+_). Esta es una sintaxis especial de Scala que equivale a una función anónima que suma los dos operandos que se le pasan.

De hecho un código totalmente equivalente hubiese sido usar foldLeft(0)((a,b) => a+b). En este caso estamos pasando la función a usar (sumar los dos operandos) de forma más explícita.

Listos para el siguiente reto ;-)

con 1 comment(s)
Archivado en: ,

¡Muy buenas! Si desarrollais una aplicación web con MVC4 o bien una API REST con WebApi y usáis, pongamos, EF para acceder a la BBDD ya sabréis (y si no, os lo cuento ahora :P) que lo ideal es que el tiempo de vida del DbContext sea el de toda la petición web (lo mismo aplica a ISession si usáis NHibernate, por supuesto).

En muchos ejemplos y blogs se lee código parecido al siguiente (p.ej. en un repositorio/dao):

public IEnumerable<MyEntity> GetAll()

{

    using (_context = new MyDbContext())

    {

        return _context.MyEntities.ToList();

    }

}

Este código, aunque funciona, no es del todo correcto: Cada llamada a uno de esos métodos crea y destruye un contexto de Entity Framework. Sin entrar en consideraciones de rendimiento, esto hace que si dentro de una misma acción de un controlador llamamos a dos métodos de este repositorio (o bien de repositorios distintos) se crearán dos contextos de EF con todo lo que ello conlleva.

Por supuesto, irnos al extremo contrario, y guardar el contexto en una variable estática y abrirlo en el Application_Start y cerrarlo en Application_End es una idea terrible… para empezar todos los usuarios estarían compartiendo el mismo contexto de base de datos y además DbContext no es thread-safe (tampoco ISession de NHibernate lo es, si ya corrías a descargartelo).

¿Y guardar el contexto en una variable de sesión? ¡Peor! Entonces tendrías un contexto abierto por cada usuario… idea terrible, pues si tu aplicación crece y tiene bastantes usuarios conectados a la vez, eso tumbaría tu base de datos. Además de que no hay nada que te garantice que todas las peticiones de un mismo usuario sean procesadas en un mismo thread.

¿La solución? Crear un contexto de BBDD por cada petición y cerrarlo una vez la petición se termina. De este modo, si dentro de la acción de un controlador llamas a 4 repositorios (por decir algo), esos 4 repositorios compartirán la instancia del contexto de bbdd. Al abrir y cerrar el contexto en cada petición podemos asegurar que no nos quedan contextos abiertos. A estos objetos que se crean y se destruyen una vez por cada petición, les decimos que tienen un tiempo de vida per-request.

Bien… veamos como podemos conseguir esto tanto en MVC4 como en WebApi, usando el contenedor IoC de Microsoft: Unity (en su versión 2.1).

Si no conoces nada acerca del funcionamiento de Unity, he escrito varios posts sobre él en este mismo blog.

A. Solución de MVC4

MVC4 viene bastante bien preparada para IoC, de hecho hay varias maneras en como se puede configurar la inyección de dependencias en MVC4. Debemos tener presente que Unity, a diferencia de otros contenedores IoC no es capaz de gestionar automáticamente objetos con un tiempo de vida per-request, así que nos lo tendremos que currar nosotros, aunque no es excesivamente dificil. Para “simular” el tiempo de vida per-request, nos vamos a aprovechar de la característica de “contenedor hijo” que tiene Unity. En Unity, a partir de un contenedor padre, podemos crear un contenedor hijo, que obtiene todos los mapeos de tipo del padre y puede añadir los suyos. Cuando se destruye este contenedor hijo, todos los mapeos nuevos que tenga dicho contenedor son destruídos, y se llama a Dispose() de todos los objetos singleton gestionados por este controlador hijo (que sean IDisposable).

A.1 Crear un contenedor hijo en cada inicio de petición y destruirlo al final

Ah… eso es fácil, ¿no? Nos basta usar Application_BeginRequest:

protected void Application_BeginRequest()

{

    var childContainer = _container.CreateChildContainer();

    HttpContext.Current.Items[_unityGuid] = childContainer;

}

Y Application_EndRequest, por supuesto:

protected void Application_EndRequest()

{

    var childContainer = HttpContext.Current.Items[_unityGuid] as IUnityContainer;

    if (childContainer != null)

    {

        childContainer.Dispose();

        HttpContext.Current.Items.Remove(_unityGuid);

    }

}

Por si te lo preguntas _container es el contendor de Unity principal y _unityGuid no es nada más que un objeto para usar como clave en HttpContext. Ambos se inicializan en Application_Start y son estáticos:

private static Guid _unityGuid;

private static IUnityContainer _container;

 

protected void Application_Start()

{

    _container = new UnityContainer();

    _unityGuid = Guid.NewGuid();

A.2 Mapear los tipos en el contenedor de Unity

Eso tampoco reviste especial complicación. La idea es:

  • Los singleton compartidos por todos los usuarios se declaran con el LifetimeManager ContainerControlledLifetimeManager en el contenedor principal
  • Los objetos de crear y destruir (p. ej. un repositorio o un controlador) se declaran con el LifetimeManager TransientLifetimeManager en el contenedor princial
  • Los objetos per-request (como el contexto de EF) se declaran con el LifetimeManager HierarchicalLifetimeManager en el controlador principal. Otra opción sería declararlos como singletons (ContainerControlledLifetimeManager) en el contenedor hijo.

Nota: El lifetime manager HierarchicalLifetimeManager es equivalente al ContainerControlledLifetimeManager (es decir existe una sola instancia en el contenedor) pero con la salvedad de que los contenedors hijos no comparten la instancia del contenedor padre, si no que cada contenedor hijo puede tener la suya propia.

Sería algo así como:

// Objetos "de usar y tirar":

container.RegisterType<IMyRepo, MyRepository>();

// Contexto EF

container.RegisterType<MyDbContext>(new HierarchicalLifetimeManager());

A.3 Crear el “activador de controladores” (IControllerActivator)

Una vez configurado el contenedor debemos decirle a MVC4 que lo use. Una de las formas de hacerlo es crear un IControllerActivator. Esto nos basta para la mayoría de casos (cuando tan solo vamos a inyectar dependencias en los controladores, lo que es ásí casi siempre). ´

La idea es crear los controladores usando el controlador hijo que hemos guardado en HttpContext.Items:

public class UnityControllerActivator : IControllerActivator

{

 

    private Guid _containerGuid;

    public UnityControllerActivator(Guid containerGuid)

    {

        _containerGuid = containerGuid;

    }

    public IController Create(RequestContext requestContext, Type controllerType)

    {

        var container = requestContext.HttpContext.Items[_containerGuid] as IUnityContainer;

        if (container != null)

        {

            return container.Resolve(controllerType) as IController;

        }

        return null;

    }

}

Fijaos que simplemente llamamos a “Resolve” del contenedor que obtenemos de HttpContext.Items.

A.4 Configurar MVC4 para que use nuestro activador de controladores

Para que MVC4 use nuestro activador de controladores, debemos implementar un dependency resolver. A diferencia de la factoría de controladores p.ej, que se puede establecer a través de una clase estática), no hay ningún mecanismo para configurar que activador de controladores usará MVC. Si queremos usar uno específico (como es nuestro caso) debemos establecer un dependency resolver.

Para ello lo suyo es crearse uno que use el propio contenedor de Unity:

public class MvcConfig

{

    public static void Register(IUnityContainer container, Guid containerGuid)

    {

        container.RegisterInstance(typeof (IControllerActivator), new UnityControllerActivator(containerGuid));

        foreach (var type in

            Assembly.GetExecutingAssembly().GetExportedTypes().

                        Where(x => x.GetInterface(typeof(IController).Name) != null))

        {

            container.RegisterType(type);

        }

        DependencyResolver.SetResolver(

            t => container.IsRegistered(t) ? container.Resolve(t) : null,

            t => container.IsRegistered(t) ? container.ResolveAll(t) : Enumerable.Empty<object>());

    }

}

La llamada al método MvcConfig.Register iría en el Application_Start una vez creado el contenedor principal. Este método hace 3 cosas:

  1. Registra el activador de controladores como singleton en el contenedor.
  2. Registra todos los controladores que haya
  3. Finalmente crea el dependency resolver que usa el propio contenedor.

Con esto ya podemos inyectar las dependencias en nuestros controladores y en nuestros repositorios (a los controladores les inyectaríamos los repositorios y a los repositorios el contexto de EF).

B. Solución WebApi

Aunque, por oscuras razones, WebApi se distribuye junto a MVC y parece mucho lo mismo en el fondo hay muchas diferencias entre MVC y WebApi. Y una de esas diferencias es como funciona el tema de inyección de dependencias. Por lo tanto lo que hemos dicho de MVC no funciona para WebApi.

Si queremos obtener lo mismo (es decir que el contexto de EF tenga un tiempo de vida per-request) con WebApi, debemos seguir una estrategia distinta…

Lo que sí es igual es la configuración del contenedor (se siguen las mismas directrices que el punto A.2).

B.1 Crear un DependencyResolver de WebApi

Al igual que en MVC, necesitamos un dependency resolver, pero el de WebApi es distinto que el de MVC. Así si en MVC usábamos el método estático SetResolver de la clase DependencyResolver, en WebApi usaremos la propiedad DependencyResolver del objeto HttpConfiguration global, al que podemos acceder a través de GlobalConfiguration.Configuration.

A diferencia de MVC donde podemos pasar directamente dos delegates para crear el dependency resolver, en WebApi estamos obligados a crear una clase que implemente IDependencyResolver.

Además WebApi, a diferencia de MVC, ya viene “más o menos” preparado para el concepto de objetos per-request. Existe un método dentro de IDependencyResolver, llamado BeginScope. La idea es que cada vez que se necesite crear un “scope hijo” se llame a este método. Lo bonito sería que el framework lo hiciese por nosotros al crear un controlador, pero la realidad es que no lo hace. Nos tocará hacerlo nosotros.

Así para implementar IDependencyResolver vamos a usar dos clases:

public class UnityDependencyScope : IDependencyScope

{

    private IUnityContainer _container;

    protected IUnityContainer Container { get { return _container; } }

    public UnityDependencyScope(IUnityContainer container)

    {

        _container = container;

    }

    public void Dispose()

    {

        _container.Dispose();

    }

    public object GetService(Type serviceType)

    {

        return _container.IsRegistered(serviceType)

                    ? _container.Resolve(serviceType)

                    : null;

    }

    public IEnumerable<object> GetServices(Type serviceType)

    {

        return _container.ResolveAll(serviceType);

    }

}

Esta primera clase implementa IDependencyScope. Esta interfaz es la interfaz base de IDependencyResolver. De hecho IDependencyResolver añade tan solo el método BeginScope. La segunda clase es la que implementa IDependencyResolver:

public class UnityDependencyResolver : UnityDependencyScope, IDependencyResolver

{

    public UnityDependencyResolver(IUnityContainer container) : base (container)

    {

    }

    public IDependencyScope BeginScope()

    {

        return new UnityDependencyScope(Container.CreateChildContainer());

    }

}

Implementamos el método BeginScope retornando un contenedor hijo de nuestro contenedor. De esta manera ya podemos “encadenar” scopes hijos.

B.2 Crear el activador de controladores

Al igual que en MVC debemos crear un activador de controladores propio. Y por supuesto es distinto del de MVC :)

En este caso debemos implementar IHttpControllerActivator:

public class UnityHttpControllerActivator : IHttpControllerActivator

{

    private IUnityContainer _container;

    public UnityHttpControllerActivator(IUnityContainer container)

    {

        _container = container;

    }

    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)

    {

        var scope = request.GetDependencyScope();

        return scope.GetService(controllerType) as IHttpController;

    }

}

La clave está en la llamada a GetDependencyScope del parámetro request. Esto creará un scope (llamando a BeginScope) por lo que el controlador que creemos se creará con el contenedor Unity hijo.

B.3 Configurar WebApi para que use nuestro activador de controladores

Eso si que es realmente sencillo. Simplemente registramos el activador de controladores y el dependency resolver en WebApi. Esto lo hacemos en el Application_Start, p.ej. en el propio método Register de la clase WebApiConfig que crea Visual Studio:

public static void Register(HttpConfiguration config, IUnityContainer container)

{

    config.DependencyResolver = new UnityDependencyResolver(container);

    container.RegisterInstance<IHttpControllerActivator>(new UnityHttpControllerActivator(container));

    foreach (var type in

        Assembly.GetExecutingAssembly().GetExportedTypes().

                    Where(x => x.GetInterface(typeof(IHttpController).Name) != null))

    {

        container.RegisterType(type);

    }

}

También debemos registrar todos los controladores de WebApi (de una forma análoga a la que usábamos al registrar los controladores MVC).

¿Y si tengo WebApi junto a MVC en un mismo proyecto?

Pues te tocará hacer todo lo de MVC y todo lo de WebApi. Lo único que no haces dos veces es la configuración del contenedor (punto A.2) ya que es la misma.

Eso sí, en este caso puedes hacer que si la petición HTTP es una llamada a WebApi, en lugar de una llamada a un controlador MVC, no se cree el contenedor hijo en Application_BeginRequest (ni se destruya en Application_EndRequest). Algo como:

protected void Application_BeginRequest()

{

    if (!Request.IsWebApiRequest())

    {

        // Código para MVC

    }

}

El método IsWebApiRequest es un método de extensión que me he inventado. La verdad, no tenía nada claro como diferenciar una petición de WebApi de una de MVC así que he cortado por lo sano:

public static class HttpRequestExtensions

{

    public static bool IsWebApiRequest(this HttpRequest @this)

    {

        return @this.FilePath.StartsWith("/api/");

    }

}

Exacto… Si la URL empieza por /api/ es que es una llamada a WebApi. Ya… cutre, pero oye tu: funciona :P Por supuesot, dependiendo de la tabla de rutas que tengas eso puede no ser cierto en tu caso ;-)

Y nada más… con eso conseguimos un tiempo de vida per-request tanto en ASP.NET MVC4 como con WebApi :)

Saludos!

Este pasado sábado 26 de febrero, tuvimos el Four Sessions de Techdencias, en el que me lo pasé genial participando junto a Marc Rubiño en un duelo entre Knockout y Backbone para ver que librería era “mejor” para construir aplicaciones web. Al final la gente votó y la verdad és que ganó Marc por KO… :P

Como digo estuvo divertido, aunque comparar Backbone con Knockout es como comparar peras con guisantes ya que, realmente, poco tenen que ver y además son muy complementarias. Backbone se centra más en dotarnos de herramientas para poder arquitecturar nuestra aplicación javascript. Mientras que Knockout tiene un sistema fenomenal de data-binding entre el DOM y el viewmodel pero nada más que eso (que no es poco): sigue siendo tarea nuestra organizar bien nuestra aplicación javascript y de ello Knockout se mantiene al margen.

La consecuencia de estos dos enfoques es que el código javascript que tiene que colocarse en Backone es siempre muy superior al de Knockout. No sólo eso, si no que Backbone por su propia concepción requiere unas habilidades de javascript superiores a las que requiere Knockout para empezar a hacer cosas. Una vez superados estos dos escollos, Backbone ofrece más facilidades que Knockout no sólo para organizar nuestro código de cliente, si no también para validaciones de modelos y creación de aplicaciones SPA.

Dos modelos distintos pero similares

Knockout apuesta por un modelo MVVM, muy semejante al que se encuentra en las tecnologías basadas en XAML: los enlaces se aplican declarativamente en el DOM y en javascript basta con crear un viewmodel que contenga los datos que son enlazados automáticamente (y de forma bidireccional). Knockout ofrece soporte para varios tipos de bindings y la capacidad de definirnos nuestros propios para tareas más avanzadas.

El siguiente código muestra un textbox y una label que se va modificando a medida que vas modificando el contenido del textbox:

<!DOCTYPE html>

<html>

    <head>

        <script src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>

    </head>

    <body>

        <input type="text" data-bind="value: name, valueUpdate: 'afterkeydown'" />

        <br />

        <label data-bind="text: greeting" />

    </body>

 

    <script>

        (function () {

            var MyViewModel = function (aName) {

                var self = this;

                self.name = ko.observable(aName);

                self.greeting = ko.computed(function () {

                    return "Hola " + self.name();

                });

            }

            var vm = new code("eiximenis");

            ko.applyBindings(vm);

        })();

    </script>

</html>

Podemos ver el uso del atributo data-bind para declarar los enlaces declarativamente y el código javascript que se limit a crear el viewmodel con las dos propiedades usadas (name y greeting).

Backbone por su parte apuesta por un modelo más parecido a MVC, con sus modelos y sus vistas claramente diferenciados. Para crear la misma aplicación con Backbone se requiere bastante más código:

<!DOCTYPE html>

<html>

    <head>

        <script src="http://code.jquery.com/jquery.min.js"></script>

        <script src="http://underscorejs.org/underscore-min.js"></script>

        <script src="http://backbonejs.org/backbone-min.js"></script>

        <script type="text/template" id="viewTemplate">

            <input type="text" value="<%- name %>" />

            <br />

            <label><%- greeting %></label>

        </script>

    </head>

    <body>

    </body>

    <script>

        (function () {

            var MyModel = Backbone.Model.extend({

                defaults: { name: 'eiximenis' },

            });

            var MyView = Backbone.View.extend({

                initialize: function () {

                    var self = this;

                    this.model.on('change:name', function () {

                        self.updateLabel();

                    });

                },

                events: { 'keyup input': 'changeName' },

                changeName: function (evt) {

                    this.model.set('name', $(evt.target).val());

                },

                updateLabel: function () {

                    this.$("label").text(this.greeting());

                },

                greeting: function () {

                    return "Hola " + this.model.get('name');

                },

                render: function () {

                    this.$el.empty();

                    var data = { name: this.model.get('name'), greeting: this.greeting() };

                    this.$el.html(_.template($("#viewTemplate").html(), data));

                    return this;

                }

            });

            $(function () {

                var model = new MyModel();

                var view = new MyView({ model: model, el: 'body' });

                view.render();

            });

        })();

    </script>

</html>

Aunque pueda parecer que están a las antípodas, en el fondo los modelos de Knockout y Backbone son similares: ambos tienen una clase javascript que se encarga de mantener los datos y ambos tienen una vista que se encarga de representarlos. La diferencia fundamental es que en Knockout la vista es el propio DOM que contiene los atributos data-bind y en Backbone la vista debe crearse y debe tener todo el código necesario para gestionar la comunicación entre el DOM (DOM que crea la propia vista) y el modelo de datos.

¿Y pueden trabajar juntos?

La verdad es que para pequeñas aplicaciones no tiene sentido usar Backbone, y probablemente con Knockout tirarás perfectamente. Pero, si nos ponemos en la situación de que queremos usar Backbone… no podríamos aprovecharnos del sistema de enlaces de Knockout y ahorrarnos toda esa parte?

Pues sí, y la respuesta se llama Knockback.

Usando Knockback nuestro código quedaría más o menos como:

<!DOCTYPE html>

<html>

    <head>

        <script src="http://code.jquery.com/jquery.min.js"></script>

        <script src="http://underscorejs.org/underscore-min.js"></script>

        <script src="http://backbonejs.org/backbone-min.js"></script>

        <script src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>

        <script src="https://raw.github.com/kmalakoff/knockback/0.16.8/knockback.min.js"> </script>

    </head>

    <body>

        <input type="text" data-bind="value: name, valueUpdate:'afterkeydown'" />

        <br />

        <label data-bind="text: greeting"></label>

        <input type="button" id="ju" value="!!" />

    </body>

    <script>

        (function () {

            var MyModel = Backbone.Model.extend({

                defaults: { name: 'eiximenis' },

            });

            $(function () {

                var model = new MyModel();

                var ViewModel = function (model) {

                    var self = this;

                    this.name = kb.observable(model, 'name');

                    this.greeting = ko.computed(function () {

                        return "Hello " + self.name();

                    })

                }

                ko.applyBindings(vm);

            });

        })();

    </script>

</html>

En el fondo volvemos a un esquema muy parecido al código con knockout, la única diferencia principal es que ahora tenemos un modelo de Backbone y un viewmodel de Knockout. Lo que ya no tenemos (puesto que nos lo proporciona Knockout) es la vista de Backbone.

Evidentemente, si en este ejemplo no tiene mucho sentido aplicar Backbone, tampoco lo tendrá aplicar Knockback, pero vamos a hacer un ejercicio de imaginación y situarnos en un proyecto mucho más complejo.

En este caso, al usar Knockback obtenemos la sencillez de Knockout para los enlaces y además la potencia de Backbone para los modelos. En nuestro ejemplo ViewModel y modelo eran casi idénticos, pero en un ejemplo más grande el ViewModel puede diferir bastante del modelo: Este contiene los datos tal y como van a ser enviados y recibidos desde el servidor y lo gestiona Backbone. Así nos podemos aprovechar de la persistencia automática de Backbone, el enrutamiento en cliente, la ordenación y filtrado de colecciones automática y todo lo que Backbone nos ofrece en los modelos. Por otra parte el Viewmodel contendría los datos preparados para ser enlazados directamente con el DOM.

Imagina una aplicación que muestre los datos generales de una póliza, y luego tenga varias pestañas con información addicional. Pues bien, toda la información puede estar en un modelo de Backbone, que contendrá todos los datos de la póliza, mientras que tendríamos varios viewmodels de Knockout, uno por cada pestaña, que son los que usaríamos para enlazar con el DOM.

Lo que Knockback nos ofrece es que los viewmodels y los modelos estén sincronizados entre ellos (si modifico algo en el viewmodel el modelo se entera de dicha modifcación).

A modo de resumen podríamos decir que un Modelo de Backbone:

  • Es independiente del DOM
  • Ligado fuertemente a los datos que vienen del servidor
  • Representa datos complejos, muchos de los cuales pueden no visualizarse
  • Se modifica a través de la lógica de negocio

Mientras que un ViewModel de Knockout:

  • Está atado a la vista, en el sentido que contiene aquellas propiedades que la vista quiere mostrar
  • No está atado a los datos del servidor, de hecho probablemente tenga tan solo un subconjunto de dichos datos
  • Aplicaciones complejas tenderán a tener varios viewmodels de knockout por cada modelo de Backbone

Por supuesto lo más importante es usar cada librería para lo que ha sido pensada y tener siempre bien claro que ventajas e inconvenientes nos puede tener usar cada una de ellas :)

Un saludo!

pd: Ah sí… y eso de las librerías de javascript está en constante evolución! No todo es Backbone o Knockout! Existen más por ahí afuera y en breve espero poder hablaros de alguna de ellas! ;-)

Muy buenas, en este post vamos a hablar de lo que ocurre si los servicios REST de tu aplicación están en otro servidor distinto al de tu aplicación web… Terminaremos hablando de CORS, pero antes lo haremos de JSONP y empezaremos por el…

… Orígen

No, no me refiero a la onírica película con Di Caprio, aunque muchas veces el desarrollo web se parezca a una pesadilla, si no a lo que en el mundo web entendemos como el origen de una web.

Es un concepto muy sencillo pero que debemos tener siempre presente: protocolo, host y número de puerto conforman un orígen. Así las urls

¿Es simple no? Bueno pues ahora la regla de oro: Los navegadores tienen prohibido procesar una llamada Ajax (con XmlHttpRequest) desde una página que esté en un orígen hacia una URL que sea de otro origen.

Punto.

Vale, que nos impidan hacer peticiones ajax a otro dominio es una muy buena medida de seguridad, pero a veces es una necesidad legítima: Imagina que tienes la API REST de tu aplicación publicada en http://api.foo.com y tu aplicación web en http://foo.com. Pues si desde tu web quieres hacer una petición ajax a alguno de tus servicios: mala suerte.

Para hacer algunas demos voy a crear una solución de vs2012 con dos proyectos web:

image

Uno será mi aplicación web y el otro será mi API. El proyecto WebApi (que yo he llamado CORSDemo) tan solo tiene un controlador, que responde a las peticiones de tipo /Beer/id:

public class BeersController : ApiController

{

    public Beer Get(int id)

    {

        return new Beer {Name = "Cerveza " + id, Id = id};

    }

}

Por otro lado la aplicación web (que he llamado CORSDemo.Web) tiene un solo controlador (Home) que devuelve una vista. Dicha vista intenta hacer una llamada Ajax al servicio REST:

<h2>Index</h2>

 

@section scripts

{

    <script>

    (function() {

            var xhr = new XMLHttpRequest();

            console.log('Invocando servicio REST');

            xhr.open('GET', 'http://localhost:2614/Beers/10', true);

            xhr.setRequestHeader("Accept", "application/json");

            xhr.addEventListener('readystatechange', function (e) {

                console.log('readyState: ' + xhr.readyState)

                console.log('status: ' + xhr.status);

                console.log('response: ' + xhr.responseText);

            });

            xhr.send();

    })();

    </script>

}

Si pongo en marcha el proyecto, efectivamente en mi IIS Express tengo dos aplicaciones web:

image

En mi caso la aplicación WebApi está en localhost:2614 y la aplicación web está en localhost:2628. Y si navego a localhost:2628 veo lo que ya me esperaba:

image

El navegador me muestra el error que no puede realizar la llamada ya que el servicio REST está en otro orígen.

Rompiendo la barrera – jsonp

Por suerte (y por desgracia también, todo tiene las dos caras de la moneda), hay muchas cabezas pensantes por ahí y algunas de ellas se dedicaron a ver si existía alguna posible manera de saltarse esta medida de seguridad. Y dieron con una. Ciertamente no abrieron un boquete en la muralla de seguridad, pero sí una brecha y durante vario tiempo nos hemos estado aprovechando de ella. Esta brecha es la técnica conocida como jsonp. Veamos muy brevemente en que consiste…

El objetivo final es conseguir llamar al servicio REST que tenemos y recuperar los datos. Eso debemos hacerlo de forma asíncrona al igual que hace XMLHttpRequest, que ya hemos visto que no podemos usar.

La técnica de jsonp es muy simple, pero requiere eso sí que los servicios REST devuelvan json (no sirve si devuelven algún otro tipo de datos como XML).

Consiste básicamente en sustuir la llamada AJAX por un tag <script>. El tag <script> permite sin ningún problema incluir scripts de otros orígenes (si no, no podríamos usar CDNs p. ej.). Asi en nuestro caso vamos a añadir un tag <script> como el siguiente:

<script src="http://localhost:2614/Beers/10"></script>

Ahora el navegador realiza la llamada web y obtiene el JSON pero… por supuesto ahora tenemos un error de BLOCKED SCRIPT

image

Eso es debido a que el navegador está intentando interpretar el JSON como si fuese código javascript y por supuesto {"Name":"Cerveza 10","Id":10} no es un código javascript válido. No lo es, pero le falta muy, muy poco para serlo.

Ahora toca que el servicio REST colabore un poco. Que nos devuelva los datos directamente en JSON no nos sirve ya que hemos visto que el navegador no puede interpretarlos. Pero… y si en lugar de devolvernos los datos en JSON el servicio REST nos devuelve algo como:

func_callback({"Name":"Cerveza 10","Id":10});

Ah! Esto sí que es javascript válido. A este código lo llamamos el código jsonp.

Tan solo falta que func_callback esté definida y de eso ya se encargaría la aplicación web.

Veamos como modificar el servicio en WebApi para soportar jsonp. Para ello nos basaremos en la querystring.

Soportando JSONP en WebApi

Que yo sepa WebApi NO tiene soporte directo para jsonp. Por suerte añadirlo es trivial. Basta con usar un MediaTypeFormatter nuevo:

public class JsonpMediaFormatter : JsonMediaTypeFormatter

{

    public JsonpMediaFormatter()

        : base()

    {

        SupportedMediaTypes.Add(DefaultMediaType);

        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));

        MediaTypeMappings.Add(new QueryStringMapping("jsonp", "true",DefaultMediaType));

    }

 

 

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, TransportContext transportContext)

    {

        var callback = GetJsonpCallback();

        if (string.IsNullOrEmpty(callback))

            return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);

 

        Encoding encoding = SelectCharacterEncoding(content.Headers);

        return Task.Factory.StartNew(() =>

            {

                var bytes = encoding.GetBytes(string.Format("{0}(", callback));

                writeStream.Write(bytes, 0, bytes.Length);

            }).

            ContinueWith(task =>

                {

                    base.WriteToStreamAsync(type, value, writeStream, content, transportContext);

                }).

                ContinueWith(task =>

                    {

                        var bytes = encoding.GetBytes(");");

                        writeStream.Write(bytes, 0, bytes.Length);

                    });

    }

 

    protected string GetJsonpCallback()

    {

        if (HttpContext.Current.Request.HttpMethod != "GET") return null;

        if (HttpContext.Current.Request.QueryString["jsonp"] != "true") return null;

        return HttpContext.Current.Request.QueryString["callback"] ?? "func_callback";

    }

}

Para registrar este MediaTypeFormatter debemos añadir la siguiente línea en el Application_Start:

GlobalConfiguration.Configuration.Formatters.Add(new JsonpMediaFormatter());

Ahora nuestro JsonpMediaFormatter actuará si la petición tiene un parámetro querystring llamado jsonp y cuyo valor sea true. Además admite otro parámetro llamado callback con el valor de la función callback. Por lo tanto modificamos ahora el tag <script> para que pase esos dos parámetros y también definimos antes la función show_beer:

<script>

    function show_beer(data) {

        alert(data.Name);

    }

</script>

 

<script src="http://localhost:2614/Beers/10?jsonp=true&callback=show_beer"></script>

¡Y ya hemos terminado! Si ahora ejecutamos la página vemos que efectivamente nos hemos saltado la restricción de orígen:

image

¿Por qué digo que JSONP es una brecha en lugar de un agujero en la seguridad? Muy simple… porque está basado en el tag <script> lo que implica que tan solo funciona para el verbo http GET.

Así, aunque JSONP es un parche que nos puede sacar de muchos apuros, era evidente que necesitábamos una manera segura de poder llamar a servicios REST que estuviesen en otro dominio… y la W3C se puso manos a la obra y definió CORS.

CORS

Las ventajas de CORS sobre JSONP son enormes: CORS funciona para todos los verbos HTTP, permite usar XMLHttpRequest así que no tenemos que andar con trapicheos como en JSONP y además es un estándard y no una técnica salida de una mente calenturienta.

Por supuesto tiene sus inconvenientes: tiene que estar soportado por el servidor y por el navegador. Si os vais a http://caniuse.com/#search=CORS podeis ver como p.ej. IE NO SOPORTA CORS hasta la versión 10 (En la versión 8 y 9 soporta un pseudo-CORS a través del objeto XDomainRequest). Es la historia de siempre… :(

CORS se basa en las cabeceras HTTP. Básicamente la idea es que el navegador envía una petición con la cabecera http “Origin” que contiene el origen de la aplicación web. El servidor recibe esta petición y si admite dicho orígen devuelve en la respuesta la cabecera “Access-Control-Allow-Origin” con el nombre de los orígenes admitidos.

Volvamos de nuevo al código original que teníamos antes de ver jsonp. Si abro una ventana de Chrome y navego a la aplicación web, donde se hace la llamada con XMLHttpRequest y miro las cabeceras enviadas:

image

Fijaos como el navegador envía la cabecera “Origin”. Por lo tanto Chrome ya está intentando iniciar una negociación CORS, pero como el servidor no le responde con la cabecera Access-Control-Allow-Origin Chrome no procesa la petición y no podemos acceder a la respuesta.

En este punto voy a dejar una cosa bien clara: La petición es enviada por el navegador y por lo tanto es RECIBIDA por el servidor. Lo podéis comprobar poniendo un Breakpoint en el controlador de WebApi y veréis que se llega a él. Pero la respuesta NO ES PROCESADA por el navegador. Otra forma de verlo es usando fiddler:

image

Como podemos ver hay una petición (con su cabecera Origin) y una respuesta. Solo que el navegador nos ignora la respuesta debido a que no hay la cabecera CORS Access-Control-Allow-Origin.

Soporte para CORS en WebApi

De nuevo, que yo sepa, no hay soporte out-of-the-box en WebApi para CORS, aunque por suerte añadir uno básico es trivial. Si para JSONP usábamos un MediaTypeFormatter, para CORS usaremos un Message Handler para conseguirlo:

Nota: El Message Handler aquí mostrado implementa una parte muy pequeña de CORS. Está pensado a modo de información y no para poner en producción. Solo por citar una de sus limitaciones no está preparado para lidiar con las peticiones “preflight” de CORS que se dan en según que escenarios y que no tratamos en este post.

public class CorsMessageHandler : DelegatingHandler

{

    protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)

    {

        if (request.Headers.Contains("Origin"))

        {

            var response = await base.SendAsync(request, cancellationToken);   

            response.Headers.Add("Access-Control-Allow-Origin",

                                    request.Headers.GetValues("Origin"));

            return response;

        }

        return await base.SendAsync(request, cancellationToken);

    }

}

Este MessageHandler es muy sencillo: si la petición contiene la cabecera “Origin” añade la cabecera “Access-Control-Allow-Origin” con el mismo valor que la cabecera Origin.

Tenemos que registrar este Message Handler, en el Application_Start:

GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsMessageHandler());

¡Listos! Hemos terminado. Si ahora navegamos de nuevo a la aplicación web vemos que la llamada Ajax se efectúa sin problemas:

image

Y si miramos en la pestaña network veremos la cabecera Access-Control-Allow-Origin que ahora envía el servidor como respuesta:

image

Para más información, aquí tenéis la especificación de CORS del W3C.

Si buscáis un soporte de CORS realmente completo para WebAPi echad un vistazo a este post de brocakllen: http://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/

Saludos!

con 6 comment(s)
Archivado en:

Hace justo casi nada que ha salido la nueva versión de jQuery 1.9 y he aprovechado para actualizar una aplicación web que tenía a medias.

Pues bien, si actualizas una aplicación ASP.NET MVC que use unobtrusive ajax y actualizas a ASP.NET MVC… deja de funcionar. Pero vayamos por partes…

Reproducción del problema

Abre VS2012 y crea un nuevo proyecto ASP.NET MVC4. Usa la plantilla Basic (no la Empty). Podemos ver como por defecto nos ha agregado, entre otros, el fichero jQuery-unobstrusive-ajax. Este fichero es el que da soporte para el unobtrusive ajax de MVC. Ya hablé en este blog hace un tiempecillo sobre el unobstrusive ajax de MVC.

Para ver el problema en acción, añadimos un controlador HomeController con la acción Index por defecto y una acción que he llamado PostData:

public class HomeController : Controller

{

    public ActionResult Index()

    {

        return View();

    }

    [HttpPost]

    public ActionResult PostData(string name)

    {

        ViewBag.DataName = name;

        return PartialView();

    }

}

La vista Index.cshtml es muy sencilla, tiene tan solo un Ajax.BeginForm:

@using (Ajax.BeginForm("PostData", new AjaxOptions() {HttpMethod = "Post", UpdateTargetId = "datadiv"}))

{

 

    <label for="name">Name: </label>

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

    <input type="submit" value="Send" />

}

<hr />

Aquí irá el resultado: <p />

<div id="datadiv">

</div>

Y finalmente nos queda la vista PostData.cshtml que contiene el código HTML que se incrustará en el div “datadiv”:

<h2>Hola @ViewBag.DataName</h2>

Tnemos que hacer una última modificación a la página de _Layout.cshtml que es añadir el bundle que nos añade (entre otros) jQuery-unobstrusive-ajax.js. Para ello añadimos la siguiente línea justo antes del RenderSection:

@Scripts.Render("~/bundles/jqueryval")

Listos, con esto tenemos un formulario que es enviado via Ajax al servidor y el resultado (la vista PostData.cshtml) se incrusta en el formulario.

Ahora simplemente reemplazamos el fichero de jQuery 1.7.1 por el de jQuery 1.9.0 y volvemos a probar la aplicación.

Veréis que el formulario no se envía por Ajax (en su lugar se envía por un POST normal) por lo que el resultado de PostData.cshtml no se incrusta en el div si no que pasa a ser toda la nueva página.

La causa del error y la solución

La causa es que jquery-unobtrusive-ajax.js que es quien se encarga de dar soporte al unobtrusive ajax de ASP.NET MVC usa el método live de jQuery. Pero dicho método fue declarado obsoleto en jQuery 1.7 y se ha eliminado en 1.9. Dicho método permitía asociarse a un evento de cualquier elemento del DOM actual o futuro.

El método que debe usarse actualmente en lugar de live es el método on. No obstante la sintaxis es un poco distinta, ya que el método on tiene más usos en jQuery.

$("form[data-ajax=true]").live("submit", function (evt) {

Vamos a modificar la llamada a “live” a una llamada a “on”. Para que “on” actue como live debemos pasarle a “on” 3 parámetros:

  • El evento (igual que live, será “submit”)
  • Un selector (elementos “hijos”) del selector base que es el que debe existir siempre
  • La función gestora (igual que live).

En este caso nuestra línea queda como:

 $("body").on("submit", "form[data-ajax=true]",function (evt) {

Fijaos: he movido el selector al segundo parámetro de “on” y he puesto como selector base “body” (no es el más óptimo, pero así estoy seguro de que existe siempre). La idea es que la función pasada se asocia a todos los elementos presentes y futuros de tipo “form[data-ajax=true]” que estén dentro del selector base (body).

Para el resto de llamadas a live (hay 3 más) hacemos la misma sustitución que nos quedarán así:

$("body").on("click", "form[data-ajax=true] :submit", function (evt) {

$("body").on("click", "a[data-ajax=true]", function (evt) {

$("body").on("click", "form[data-ajax=true] input[type=image]", function (evt) {

Y listos! Con esto ya hemos recuperado la funcionalidad del unobtrusive ajax de MVC y nuestra aplicación debería volver a funcionar correctamente!

Saludos!

PD: Actualmente el fichero jquery-unobstrusive-ajax.js se distribuye a través de un paquete de NuGet llamado Microsoft jQuery Unobtrusive Ajax. Es de esperar que en breve lo actualizarán ;-)

con 3 comment(s)
Archivado en:

Hoy, para una prueba de concepto que estoy realizando, me he encontrado con la necesidad de establecer una cookie desde un controlador de ASP.NET WebApi (un ApiController). Por supuesto podríamos discutir largo y tendido sobre la conveniencia de hacer esto o no (si suponemos que dichos controladores van a ser accedidos por clientes que no sean navegadores y por lo tanto pueden no entender las cookies). Pero obviando esta discusión el problema está en que desde el controlador no podemos acceder al objeto Response (a diferencia de un controlador MVC tradicional). Por supuesto siempre existe la opción de usar HttpContext.Current pero eso no es deseable ya que ata mi controlador a un pipeline de ASP.NET por lo que pierdo la facilidad de pruebas.

La solución que he encontrado pasa por devolver un HttpResponseMessage desde el controlador. En mi caso la firma original del método era:

public AuthResultDTO PostLoginByUserPass(LoginDTO login)

{

}

El primer paso es modificarlo para que devuelva un HttpResponseMessage y usar el HttpResponseMessage para establecer las cookies.

Primero me he creado un método para que me crease la cookie (nota para curiosos: lo que quería hacer era usar un ticket de autenticación forms personalizado):

private static CookieHeaderValue CreateCustomCookie(string name, int id)

{

    var userData = id.ToString();

    var ticket = new FormsAuthenticationTicket(1, name, DateTime.Now, DateTime.Now.AddMinutes(30), false, userData);

    var encTicket = FormsAuthentication.Encrypt(ticket);

    var cookieValue = new CookieHeaderValue(FormsAuthentication.FormsCookieName, encTicket)

        {

            Path = "/",

            HttpOnly =  true,

        };

    return cookieValue;

}

Este método crea un ticket con un campo addicional (el id del usuario) y crea el objeto CookieHeaderValue que es el que contiene los datos de las cookies a añadir.

Ahora toca modificar el método del controlador. Lo primero es hacer que devuelva un HttpResponseMessage en lugar del AuthResultDTO que devolvía antes:

public HttpResponseMessage PostLoginByUserPass(LoginDTO login)

{

    var response = new HttpResponseMessage();

    return response;

}

El primer paso es añadir la cookie generada a nuestro mensaje:

var cookieValue = CreateCustomCookie(login.Name, id);

response.Headers.AddCookies(new CookieHeaderValue[] {cookieValue});

Pero ahora nos falta incrustar el AuthResultDTO dentro de la respuesta. Para ello debemos usar la propiedad Content del HttpResponseMessage (authResult es una variable de tipo AuthResultDTO):

response.Content = new ObjectContent(typeof(AuthResultDTO), authResult, new JsonMediaTypeFormatter());

Como vemos hemos de usar la clase ObjectContent ya que en mi caso quiero mandar el objeto serializado. El tercer parámetro indica que serializador se usa. En mi caso uso el serializador de JSON (JsonMediaTypeFormatter). Notad que aquí estoy fijando que la respuesta será siempre en JSON, con independencia de la cabecera Accept que me mande el cliente (contraviniendo lo que hace WebApi por defecto).

¡Y listos! Con eso ya hemos conseguido establecer nuestra cookie desde un controlador de WebApi.

Bonus track: ¿Cómo seleccionar el MediaTypeFormatter correcto?

Como he dicho antes el usar JsonMediaTypeFormatter directamente estoy “rompiendo” la capacidad de WebApi de seleccionar el tipo de respuesta según la cabecera Accept que envíe el cliente. Por suerte no es muy dificil restablecer dicha funcionalidad. Basta con mirar en la configuración global de WebApi todos los formatters que haya y encontrar el primero que pueda procesar el tipo especificado. Una posible implementación sería:

private MediaTypeFormatter ChooseMediaTypeFormatter(HttpHeaderValueCollection<MediaTypeWithQualityHeaderValue> accept, Type typeToWrite)

{

    var cfg = GlobalConfiguration.Configuration;

    foreach (var formatter in cfg.Formatters)

    {

        if (formatter.SupportedMediaTypes.Any(x => accept.Select(a => a.MediaType).Contains(x.MediaType)) &&

            formatter.CanWriteType(typeToWrite)) return formatter;

    }

 

    return new JsonMediaTypeFormatter();

}

Y llamaríamos a dicho método de la forma:

var mediaFormatter = ChooseMediaTypeFormatter(Request.Headers.Accept, typeof(AuthResultDTO));

Nota: Realmente esta implementación de ChooseMediaTypeFormatter no es del todo correcta ya que no tiene en cuenta la calidad especificada en la cabecera accept… Pero bueno, creo que la idea de como se podría hacer ya queda clara, no?

Saludos!

Actualización: Nicolás Herrera (@nicolocodev) me ha hecho dar cuenta de algo en lo que no he caído cuando he escrito el post. En un tweet me propone usar Request.CreateResponse en lugar de buscar manualmente el MediaTypeFormatter como estoy haciendo yo. Y tiene toda la razón del mundo :) Anotado queda!

Información del Método (de extensión) CreateRequest.

Gracias! ;-)

con no comments
Archivado en:

Buenas un post rapidito!! ;-)

El otro día en un curso sobre patrones Javascript que estaba impartiendo surgió una de las dos(*) eternas dudas sobre Javascript. Uno de los alumnos (probablemente el único que estaba despierto) me preguntó “¿y cual es la diferencia exacta entre null y undefined?”.

Es curioso la desinformación que existe sobre este tema, cuando en realidad es muy simple. Por supuesto buscando por internet aparecen multitud de páginas sobre el tema, ¡pero es que he visto varias que están mal! P. ej. en una página donde se hablaba precisamente de esto, se decía:

    1. Una función siempre tiene que retornar un valor.
    2. Si algo es undefined no tiene valor por tanto una función nunca puede retornar undefined (me refiero a funciones propias de JavaScript)

Esas dos afirmaciones son falsas (a no ser que con el paréntesis final intente aclarar algo que no llego a entender) y además tienen que ver con el llamado “doble error de null” o el hecho de que un error anula a otro y todo parece correcto cuando en realidad… ¡hay dos errores!

Para empezar podemos ver que una función javascript puede perfectamente devolver undefined. Para una demostración rápida me voy a basar en el REPL que tiene Node.js:

image

Lo cierto es que es práctica común y errónea usar código como el siguiente para comprobar p.ej. si una variable (o propiedad) ha sido definida o un parámetro ha sido pasado:

if (login == null) { }

Esta simple línea contiene dos errores que se anulan uno al otro:

  1. El primer es usar null en lugar de undefined.
  2. El segundo es usar la comparación coercitiva (==) en lugar de la comparación estricta (===).

El código correcto debería ser:

if (login === undefined) { }

En javascript se usa undefined para indicar que algo no existe. Que no exista no significa que no tenga valor. Para indicar que algo existe pero no tiene valor se usa null. Piensa p. ej. que en javascript los argumentos de las funciones son opcionales: si quien llama a la función omite el argumento, este toma el valor de undefined. Pero a lo mejor quien nos llama a la función nos ha pasado el argumento pero sin valor: entonces será null. Lo mismo ocurre con las propiedades de los objetos, que en javascript como buen lenguaje dinámico pueden existir o no sobre la marcha.

El problema es que undefined !== null pero undefined==null y esa es la causa de toda la confusión. Cuando hacemos if (x == null) la comparación coercitiva intenta hacer conversiones de tipos y puede convertir entre el tipo undefined a null.

Realmente la comparación coercitiva (==) es una herramienta poderosísima y debería usarse con mucho cuidado. Por norma general deberías usar siempre === (y su contrario !==) para comparar. De esta manera te evitarías muchos errores y comportamientos extraños. Y echar mano del doble igual solo cuando es estrictamente necesario.

Saludos!

(*): Por cierto que la otra eterna duda (que también salió, como no) tuvo que ver sobre el volátil significado de this en javascript y también escribiré sobre ello.

con 7 comment(s)
Archivado en:

Bueno… se ha terminado el 2012. Un año que ha traído dos cambios importantes en mi vida. Por un lado en Febrero nació mi hija Noa, lo que hizo que realmente todo fuese distinto y también, para que negarlo, que la cantidad de tiempo libre disminuyese exponencialmente. Y por otro en diciembre dejé raona para entrar a formar parte de pasiona y techdencias. No hay ninguna razón especial para dejar raona (un sitio donde he estado muy a gusto y donde se trabaja muy bien), solo que ya hacía demasiado tiempo que estaba allí y necesitaba un cambio de aires. Y una vez empecé a sondear el mercado el proyecto de pasiona me pareció muy interesante… Veremos :)

A nivel profesional ha sido un buen año: por un lado fuí nominado por primera vez como MVP y por otro hice algo que siempre había querido: impartir un curso on-line y fue la gente de desarrolloweb quienes me dieron la oportunidad de crear un curso de ASP.NET MVC para ellos. La experiencia fue muy enriquidora… ¡y en breve espero poder anunciar más noticias relacionadas con cursos on-line!

También a nivel personal con unos amigos nos juntamos y iniciamos una aventura que llamamos Epnuke. No sabemos como va a terminar, sólo que este 2013 le vamos a dedicar más ganas aún (si es que es posible).

Pero como he dicho el 2012 se ha ido y tras 12 campanadas ha llegado este 2013 que según los Mayas no debería haber existido jamás.

Y una de las tareas que realizo al principio de cada año es marcarme un conjunto de objetivos, o tareas que me gustaría hacer a lo largo del año. Luego intento planificarlas y así tener una especie de “calendario” a cumplir. Son siempre tareas al margen de mi vida laboral (desarrollo web y .NET) y muy genéricas (y relacionadas con el desarrollo por supuesto). P.ej. en el 2012 me planteé, las siguientes:

  1. Aprender un lenguaje funcional
  2. Aprender Unity3D
  3. Publicar un juego en la store de WP7
  4. Aprender desarrollo nativo en iOS (usando Objective C)

La primera la cumplí a medias (el lenguaje elegido fue scala aunqué he de reconocer que todavía me queda para poder decir que “sé” el lenguaje). La segunda sí la considero cumplida, y de hecho esta pasada BcnDevCon me animé a dar una charla sobre Unity3D. La tercera no la he cumplido, aunque he de decir que he aprendido a desarrollar para WP7 usando Silverlight primero y XNA después, pero no he llegado a publicar nada en la store. Y la cuarta la he cumplido a medias: he aprendido los fundamentos de Objective C y lo básico del desarrollo para iOS, pero a un nivel muy fundamental.

¿Y para ese 2013? Pues los objetivos que me he marcado son los siguientes:

  1. Seguir con Unity3D y publicar un juego hecho con él.
  2. Publicar un juego sencillo en la appstore, realizado con Objective C
  3. Publicar un juego sencillo en la store de Win8 / WP8. Estoy evaluando la tecnología y me gustaría mirarme SharpDX, aunque es más probable que al final termine tirando más por Monogame y así reaproveche mi conocimiento de XNA.

La verdad es que si cumplo dos de los tres me sentiré más que satisfecho. Y si cumplo los tres será para darme con un canto entre los dientes. ¿Lo conseguiré? Veremos… ;-)

¿Y vosotros? ¿Qué objetivos tenéis planteados para el 2013?

Saludos!

con no comments
Archivado en:

Sigamos con la serie de posts sobre las APIs de HTML5. Ahora le toca al canvas, uno de los elementos más revolucionarios de HTML5. Yo siempre digo que si <video /> hirío a Flash, entonces <canvas /> lo mata definitivamente.

Que es el canvas? Pues dicho rápido y mal: Un nuevo elemento de HTML, que nos permite tener una superficie de dibujo. El canvas por si mismo no tiene una API asociada, en su lugar se obtiene un contexto de dibiujo sobre el canvas. Es dicho contexto el que nos proporciona una API para interaccionar con el canvas.

En la actualidad hay dos especificaciones de contextos distintas:

  • 2d: Para operaciones de 2D. Contiene una API muy sencilla para por un lado dibujar en el canvas (líneas, círculos, cuadrados, etc) y por otra acceder directamente al contenido binario del canvas.
  • webgl: Para operaciones 3D. Ofrece una API basada en OpenGL ES 2.0.

Actualmente todos los navegadores soportan el contexto 2d, y la mayoría soportan webgl. La excepción es IE y la razón principal (al margen de ciertas vulnerabilidades descubiertas en webgl) es que no es un estándard W3C.

No voy a explicar en este post como dibujar en el canvas, hay multitud de tutoriales para ello. En su lugar vamos a ver como combinar el canvas, File Api, un poco de drag and drop y acceso al contenido binario del canvas para crearnos nuestro propio Instagram :)

Paso 1 – Declaración del <canvas />

Bueno… eso no tiene apenas ningún secreto, basta con añadir un tag <canvas> y darle un tamaño:

<div>

    <canvas height="400" width="600" id="mc" style="border: 1px solid black"></canvas> 

</div>

Paso 2 – Habilitar drag and drop

Ahora lo que queremos es que el usuario pueda hacer drop de un fichero local sobre el canvas y que nosotros lo podamos procesar. Todavía no hemos visto la API nativa de drag and drop que incorpora HTML5 pero vamos a ver lo básico ahora para habilitarla. Debemos seguir dos pasos para que un elemento sea una zona donde se pueda hacer drop:

  1. Asociarnos a su evento dragOver y allí hacer un preventDefault. Eso es porque por defecto todos los objetos del DOM heredan una implementación de dicho evento que significa “no se puede hacer drop aquí”. Al rededinir dicho evento quitamos este comportamiento por defecto y nuestro elemento del DOM pasa a ser un zona donde se puede hacer drop. El evento dragOver se dispara cuando estamos arrastrando algo y pasamos por encima del elemento.
  2. Asociarnos a su evento drop y allí recoger los datos que se hayan arrastrado. En el caso que nos ocupa (arrastre de ficheros locales hacia un elemento del DOM), el evento de drop tiene una propiedad dataTransfer que tiene una propiedad files que es una FileList (de File API), con la información de los ficheros que se hayan arrastrado.

Por lo tanto añadamos el mínimo código script para soportar el drag and drop:

<script type="text/javascript">

    var canvas = document.getElementById("mc");

    canvas.addEventListener("dragover", handleDragOver, false);

    canvas.addEventListener("drop", handleDrop, false);

    function handleDragOver(e) {

        e.preventDefault();

    }

 

    function handleDrop(e) {

        var files = e.dataTransfer.files;

        if (files.length > 0) {

            var file = files[0];

            procesarFichero(file);

        }

 

        e.preventDefault();

    }

 

    function procesarFichero(file) {

        alert("has arrastrado " + file.name + "->" + file.type);

    }

</script>

Con esto ya tenemos el drag and drop habilitado para el canvas y podemos arrastrar y soltar en él, ficheros locales!

Paso 3 – Leer la imagen y colocarla en el canvas

De todas las funciones que nos ofrece el contexto 2d para el canvas, hay una que nos interesa ahora mismo que es drawImage. Esta función nos permite recoger el contenido de una imagen ya existente y colocarlo en el canvas. Por lo tanto antes de usar drawImage necesitamos tener una imagen cargada. Ya vimos en el post dedicado a File API como leer un objeto File y asignarlo a una imagen: con el método ReadAsDataURL. Bien, pues lo hacemos:

function procesarFichero(file) {

    var fr = new FileReader();

    fr.addEventListener("loadend", function(e) {

        var datauri = e.target.result;

        loadCanvas(datauri);

    }, false);

 

    fr.readAsDataURL(file);

}

function loadCanvas(datauri) {

    alert(datauri);

}

Ahora tan solo nos falta colocar la imagen en el canvas. Para ello, primero la tenemos que colocar en un objeto Imagen y luego sí, ya en el canvas:

function loadCanvas(datauri) {

    var img = new Image();

    img.addEventListener("load", function(e) {

        var ctx = canvas.getContext("2d");

        ctx.drawImage(img, 0, 0);

    }, false);

    img.src = datauri;

}

A destacar de este código anterior:

  1. El uso de getContext("2d") para obtener el contexto 2d. Ojo, que la cadena que se pasa a getContext es case-sensitive!
  2. Creamos una imagen y le asignamos como src el resultado de readAsDataURL.
  3. Usamos drawImage para dibujar la imagen en el canvas. Importante que esto lo tenemos que hacer dentro del evento load de la imagen, para asegurarnos que esta estará cargada.

Paso 4: Crear un filtro

Vamos a crear un filtro para manipular nuestra imagen. En este ejemplo será muy sencillo: un botón que invertirá los colores de la imagen.

Para implementar este filtro debemos acceder al contenido binario del canvas. Dicho contenido es muy sencillo: cada pixel del canvas ocupa 4 bytes (rojo, verde, azul y canal alfa), y está guardado por filas (es decir, primero todos los pixels de la primera fila de izquierda a derecha y así sucesivamente).

El método getImageData del contexto, nos devuelve dicho array rellenado con el contenido actual del canvas. A dicho método se le pasa el rectángulo que queremos obtener (punto top-left, ancho y alto) y nos devolverá el array correspondiente. Cada píxel ocupará, insisto, 4 posiciones de dicho array (el array es de bytes).

Así me creo mi función de filtro, que simplemente invertirá los valores de r,g,b dejando el canal alfa tal cual estaba:

function applyFilter() {

    var ctx = canvas.getContext("2d");

    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

    for (var idx = 0; idx < imageData.data.length; idx+=4) {

        var r = imageData.data[idx];

        var g = imageData.data[idx + 1];

        var b = imageData.data[idx + 2];

 

        imageData.data[idx] = 255 - r;

        imageData.data[idx + 1] = 255 - g;

        imageData.data[idx + 2] = 255 - b;

    }

    ctx.putImageData(imageData, 0, 0);

}

Ahora tan solo me falta añadir un botón a la página y enlazar el click del botón con el método applyFilter.

Paso 5: Enviar los datos del canvas al servidor

Una vez hemos modificado el canvas nos puede interesar mandar la imagen que hay en el canvas hacia el servidor. Para ello no hay ninguna función específica, así que veremos como podemos hacerlo.

Hay un método toBlob en el propio canvas que nos devuelve un Blob (es casi equivalente al File de File API que conocemos, de hecho File deriva de Blob), pero es muy nuevo y no está apenas implementado en ningún navegador, así que lo descartamos. Dicho método sería la manera más directa, ya que luego con un FormData podemos mandar directamente este Blob (como vimos en el post de uploads asíncronos). Pero como digo, queda descartado.

Pero bueno, tampoco es ningún drama. La solución pasa por crearnos el Blob a mano y rellenarlo con los datos del canvas… Veamos:

function uploadCanvas() {

    var ctx = canvas.getContext("2d");

    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

    var xhr = new XMLHttpRequest();

 

    var arrayBuffer = new ArrayBuffer(imageData.data.length);

    var view = new Uint8Array(arrayBuffer);

    for (var idx = 0; idx < imageData.data.length; idx++) {

        view[idx] = imageData.data[idx];

    }

    var blob = new Blob();

    var formData = new FormData();

    formData.append("canvas", blob);

    formData.append("w", canvas.width.toString());

    formData.append("h", canvas.height.toString());

    xhr.open("POST", "@Url.Action("Index", "Instagram")", true);

    xhr.send(formData);

}

Para crear un Blob debemos crear antes un ArrayBuffer con los datos de dicho Blob, y para acceder (y rellenar) un ArrayBuffer debemos hacerlo a través de un Uint8Array. De ahí que el código es un poco liadillo.

Luego, además de los datos binarios del canvas, mandamos el ancho y el alto, para tenerlos en servidor.

Ahora nos falta la parte de servidor:

[HttpPost]

public ActionResult Index(HttpPostedFileBase canvas, int w, int h)

{

    var stream = canvas.InputStream;

    var bmp = new Bitmap(w, h);

    int r, g, b, a;

    for (var row = 0; row < h; row++)

    {

        for (var col = 0; col < w; col++)

        {

            r = stream.ReadByte();

            g = stream.ReadByte();

            b = stream.ReadByte();

            a = stream.ReadByte();

            bmp.SetPixel(col, row, Color.FromArgb(a, r, g, b));

        }

    }

    bmp.Save("d:\\filename.png", ImageFormat.Png);

    return new HttpStatusCodeResult(200);

}

¿Sencillo no? Creamos un bitmap, decodificamos los datos binarios del canvas y “pintamos” el Bitmap. Luego lo guardamos y listo ;-)

Paso 6: Guardar en el cliente

Para finalizar esta maravilla que estamos creando vamos a añadir un botón para guardar la imagen modificada en el cliente, sin necesidad de subirla al servidor.

Para ello nos vamos a aprovechar de un método existente en el canvas llamado toDataURL, que como supondrás nos convierte el contenido del canvas a una data URL:

function saveCanvas() {

    var datauri = canvas.toDataURL("image/png");

    window.open(datauri, "preview", "width=" + (canvas.width + 20) +",height=" + (canvas.height + 20));

}

Esta función la tengo enlazada al evento click de un botón y lo que hace es simplemente mostrar la imagen en un popup y así el usuario puede hacer "guardar como" y listos.

Existe una API definida (FileWriter) para escribir ficheros desde javascript pero su soporte es hoy en día tan minoritario que no tiene sentido plantearse el usarla. Otra alternativa, pero también minoritaria (sólo funciona con Chrome) sería usar un tag <a /> dinámico con el atributo download y forzar el click con javascript.

Saludos!

con 1 comment(s)
Archivado en:

¡Buenas! En el post anterior vimos el funcionamiento de File Api y como leer ficheros locales en servidor. En este post vamos a seguir usando File Api pero lo vamos a combinar con XMLHttpRequest y progress Api para ver como podemos hacer uploads de ficheros al servidor de forma fácil y asíncrona.

Para empezar vamos a montar la página:

<!DOCTYPE html>

<html>

<head>

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

    <title>Index</title>

</head>

<body>

    <div>

        Selecciona fichero.

        <input type="file" id="file" value="enviar" /> <br />

        <progress id="progress"></progress>

    </div>

</body>

</html>

Un simple input file y un progress para poder mostrar el progreso.

Ahora el siguiente punto es usar XMLHttpRequest para hacer la petición y enviar el fichero de forma asíncrona. Para ello nos vamos a aprovechar de la característica de que el XMLHttpRequest puede enviar un objeto File. Para ello usamos FormData que nos simplifica el código. Usando formData nos basta con una llamada a append para añadir el fichero. No solo esto, al usar FormData XMLHttpRequest usará el content-type correcto (“multipart/form-data”) al realizar la petición, sin que lo tengamos que establecer nosotros. Fijaos como després de abrir la conexión (xhr.open) nos limitamos a mandar el FormData que es quien contiene los datos :)

<script type="text/javascript">       

    function ficheroSeleccionado(e) {

        if (e.target.files.length > 0) {

            subirFichero(e.target.files[0]);

        }

    }

 

    function subirFichero(file) {

        var xhr = new XMLHttpRequest();

        var formData = new FormData();

        formData.append("file", file);

 

        xhr.addEventListener("error", function(e) {

            alert("Error subiendo el archivo.");

            var progress = document.getElementById("progress");

            progress.value = 0;

        }, false);

 

        xhr.addEventListener("load", function(e) {

            alert("fichero subido: " + e.target.status + "->" + e.target.statusText);

        });

 

        xhr.open('POST', '@Url.Action("Index")', true);

        xhr.send(formData);

    }

    document.getElementById("file").addEventListener("change", ficheroSeleccionado, false);

</script>

Nota: El objeto File se añade con el nombre “file” al FormData porque ese es el nombre del parámetro en la acción de destino del controlador (he usado ASP.NET MVC).

Con esto ya tenemos un file uploader con Ajax… ¿Qué sencillo, no? Pues, tal y como diría cierto conejo pesado, ¡no se vayan todavía, aún hay más!

Progress API

El objeto XMLHttpRequest define una propiedad llamada upload que devuelve el XMLHttpRequestUpload asociado. Pues bien dicho objeto define un evento, llamado “progress” que se va lanzando cada cierto tiempo donde nos informa del estado de la petición. Es decir, que ahora podemos saber cada cierto tiempo el total de bytes que se tienen que enviar y el total ya enviados. Y con ello podemos mostrar una progress bar (en mi caso he usado directamente un <progress>):

xhr.upload.addEventListener("progress", function(e) {

    if (e.lengthComputable) {

        var progress = document.getElementById("progress");

        progress.max = e.total;

        progress.value = e.loaded;

    }

}, false);

Y lisos, es así de sencillo! Con ello ya tenemos un file uploader, que funciona por Ajax y que además con una progress nos va mostrando como va la subida de ficheros al servidor.

¿Fácil no? :)

PD: Si vas a probar el código en local recuerda que .NET limita el tamaño de la petición por defecto (sobre unos 4MB creo). Si vas a probar con archivos grandes usa <httpRuntime maxRequestLength=”xxx” /> para establecer el tamaño máximo de petición.

PD2: Y recuerda que aún cuando uses httpRuntime, hay otro límite que aplica IIS (maxAllowedContentLength): http://msdn.microsoft.com/en-us/library/ms689462%28VS.90%29.aspx

con 1 comment(s)
Archivado en:
Más artículos Página siguiente >