Código sin nulls

Hace algunos días Juan Quijano escribió un post en GenBetaDev con este mismo título donde comentaba lo poco que le gustaba que la funciones devolviesen null y lo que hacía para evitar errores en ese caso.

Este post es mi respuesta a su post, ya que personalmente no me gusta la solución que presenta. En general termina con una solución como la siguiente:

  1. public class Modelo
  2. {
  3.     public Persona GetPersonaByName(string nombre)
  4.     {
  5.         Persona persona = new Persona();
  6.         if (nombre == "pepe")
  7.         { persona = new Persona { Nombre = nombre, Edad = 14 }; }
  8.         return persona;
  9.     }
  10. }
  11. public class Persona
  12. {
  13.     public string Nombre { get; set; }
  14.     public int Edad { get; set; }
  15.     public Persona()
  16.     {
  17.         Nombre = string.Empty;
  18.         Edad = 0;
  19.     }
  20. }

No me gusta por varias razones, la principal es que “oculta” la causa, devolviendo una Persona “sin datos” cuando no se encuentra la persona. Está estableciendo una convención que nadie más sabe: que una persona con el nombre vacío “no existe” en realidad. El código va a terminar llenándose de ifs para validar si el nombre es o no vacío para hacer algo o no. A diferencia de un null, donde olvidarte del if genera un error en ejecución (y por lo tanto es visible), dejarte un if en este caso hará que tu código se comporte mal… y a veces esto puede ser mucho, pero que mucho, más difícil que detectar el null reference, que al menos viene con stack trace. Otro problema es que no siempre existe en todo el rango de valores posibles un valor que pueda ser usado como “indicador de que no hay datos”.

Hay varios patrones para tratar esos caso, el más conocido el NullObject. De hecho un NullObject “mal hecho” es lo que propone Juan en su post. No soy muy amante del NullObject, aunque lo he usado a veces (la última hace poco en un refactoring, donde se tuvo que “desactivar” toda una funcionalidad. En este caso lo hicimos creando un NullObject del objeto que se estaba usando, de forma que el impacto en el resto del código (unos 90 proyectos de VS) fue nulo).

No quiero hablar del NullObject, si no presentar otra alternativa. En este caso una clase que contenga el valor más un indicador de si el valor existe o no. Vamos lo equivalente a Nullable<T> pero para cualquier tipo (sí… incluso los que pueden ser null). A priori parece que no ganamos nada pero dejadme un rato y veréis las ventajas que aporta.

La versión inicial de nuestra clase sería:

  1. public struct Maybe<T>
  2. {
  3.  
  4.     private readonly T _value;
  5.     private readonly bool _isEmpty;
  6.     private readonly bool _initialized;
  7.  
  8.     public T Value
  9.     {
  10.         get { return _value; }
  11.     }
  12.  
  13.     public bool IsEmpty
  14.     {
  15.         get { return (!_initialized) || _isEmpty; }
  16.     }
  17.  
  18.     public Maybe(T value)
  19.     {
  20.         _value = value;
  21.         _isEmpty = ((object)value) == null;
  22.         _initialized = true;
  23.     }
  24.  
  25.     public static Maybe<T> Empty()
  26.     {
  27.         return new Maybe<T>();
  28.     }
  29. }

Un punto importante es que no es una clase, es una estructura. Eso es para evitar que alguien que declare que devuelve un Maybe<T> termine devolviendo un null (recordad que queremos evitar los null).

Vale, esta estrcutura, tal cual está no nos aporta casi nada útil. El método del Modelo que presentaba Juan quedaría ahora como:

  1. public Maybe<Persona> GetPersonaByName(string nombre)
  2. {
  3.     Persona persona = new Persona();
  4.     if (nombre == "pepe")
  5.     {
  6.         persona = new Persona { Nombre = nombre, Edad = 14 };
  7.         return new Maybe<Persona>(persona);
  8.     }
  9.     return Maybe<Persona>.Empty();
  10. }

O devolvemos un Maybe relleno con la persona o devolvemos un Maybe vacío.  El test que usaba Juan quedaría como sigue:

  1. [TestMethod]
  2. public void GetPersonaByName_con_null_devuelve_string_empty()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName(null);
  6.     Assert.AreEqual(string.Empty, persona.Value.Nombre);
  7. }

Este test falla y la razón es obvia: persona.Value es null por lo que persona.Value.Nombre da un NullReferenceException. Podría añadir un if en el código para validar si person.IsEmpty es true, y en este caso no hacer nada. Personalmente prefiero mil veces un if (person.IsEmpty) que un if (person.Nombre ==””) ya que el primer if deja mucho claro que se pretende. Pero está claro, que no hemos ganado mucho. Como digo, dicha estructura apenas aporta nada.

Lo bueno es preparar dicha estructura para que pueda ser usada como un monad. Lo siento, soy incapaz de encontrar palabras sencillas para definir que es un monad porque el concepto es muy profundo, así que os dejo con el enlace de la wikipedia: http://en.wikipedia.org/wiki/Monad_(functional_programming)

Ahora vamos a preparar nuestra estructura para que pueda ser usada como un monad:

  1. public struct Maybe<T>
  2. {
  3.  
  4.     private readonly T _value;
  5.     private readonly bool _isEmpty;
  6.     private readonly bool _initialized;
  7.  
  8.     public T Value
  9.     {
  10.         get { return _value; }
  11.     }
  12.  
  13.     public bool IsEmpty
  14.     {
  15.         get { return (!_initialized) || _isEmpty; }
  16.     }
  17.  
  18.     public Maybe(T value)
  19.     {
  20.         _value = value;
  21.         _isEmpty = ((object)value) == null;
  22.         _initialized = true;
  23.     }
  24.  
  25.     public static Maybe<T> Empty()
  26.     {
  27.         return new Maybe<T>();
  28.     }
  29.  
  30.     public void Do(Action<T> action)
  31.     {
  32.         if (!IsEmpty) action(Value);
  33.     }
  34.  
  35.     public void Do(Action<T> action, Action elseAction)
  36.     {
  37.         if (IsEmpty)
  38.         {
  39.             action(Value);
  40.         }
  41.         else
  42.         {
  43.             elseAction();
  44.         }
  45.     }
  46.  
  47.     public TR Do<TR>(Func<T, TR> action)
  48.     {
  49.         return Do(action, default(TR));
  50.     }
  51.  
  52.     public TR Do<TR>(Func<T, TR> action, TR defaultValue)
  53.     {
  54.         return IsEmpty ? defaultValue : action(Value);
  55.     }
  56.  
  57.  
  58.     public Maybe<TR> Apply<TR>(Func<T, TR> action)
  59.     {
  60.         return IsEmpty ? Maybe<TR>.Empty() : new Maybe<TR>(action(Value));
  61.     }
  62. }

He añadido dos familias de métodos:

  1. Método Do para hacer algo solo si Maybe tiene valor
  2. Método Apply para encadenar Maybes. Este es el más potente y lo veremos luego.

Empecemos por los métodos Do. Dichos métodos básicamente nos permiten evitar el if(). Son poco más que una pequeña ayuda que nos proporciona la estructura. Mi test quedaría de la siguiente manera:

  1. [TestMethod]
  2. public void GetPersonaByName_con_null_devuelve_string_empty()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName(null);
  6.     var name = string.Empty;
  7.     persona.Do(p => name = p.Nombre);
  8.     Assert.AreEqual(string.Empty, name);
  9. }

El código del Do se ejecuta solo si hay valor, es decir si se ha devuelto una persona.

Podríamos reescribir el test usando otra de las variantes de Do:

  1. [TestMethod]
  2. public void GetPersonaByName_con_null_devuelve_string_empty()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName(null);
  6.     var name = persona.Do(p => p.Nombre, "no_name");
  7.     Assert.AreEqual("no_name", name );
  8. }

No hay mucho más que decir sobre los métodos Do… porque el método más interesante es Apply 😉

El método Apply me permite encadenar Maybes. Para ver su potencial, cambiaré el método del Modelo:

  1. public Maybe<Persona> GetPersonaByName(string nombre)
  2. {
  3.     Persona persona = new Persona();
  4.     if (nombre == "pepe")
  5.     {
  6.         persona = new Persona {Nombre = null, Edad = 42};
  7.         return new Maybe<Persona>(persona);
  8.     }
  9.     return Maybe<Persona>.Empty();
  10. }

Ahora si le paso “pepe” me da a devolver una Persona pero con el Nombre a null. Tratar esos casos con ifs se vuelve muy complejo y costoso. Apply viene en nuestra ayuda:

  1. [TestMethod]
  2. public void Acceder_a_nombre_null_no_da_probleamas()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName("pepe");
  6.     // En este punto tenemos un Maybe relleno pero value.Nombre es null
  7.     var nombreToUpper = string.Empty;
  8.     nombreToUpper = persona.Apply(p => p.Nombre).Do(s =>s.ToUpper(), "NO_NAME");
  9.     Assert.AreEqual("NO_NAME", nombreToUpper);
  10. }

La variable persona es un Maybe<Persona> con un valor. El método Apply lo que hace es básicamente ejecutar una transformación sobre el valor (el objeto Persona) y devolver un Maybe con el resultado. En este caso transformamos el objeto persona a p.Nombre, por lo que el valor devuelto por Apply es un Maybe<string>. Y como el valor de p.Nombre era null, el Maybe está vacío.

La combinación de Apply y Do permite tratar con valores nulos de forma muy sencilla y elegante.

Si os pregunto que capacidades funcionales tiene C# seguro que muchos responderéis LINQ… Porque no hacemos que nuestra clase Maybe<T> pueda participar del juego de LINQ? Por suerte eso es muy sencillo. Para ello basta con que Maybe<T> implemente IEnumerable<T> añadiendo esas dos funciones:

  1. public IEnumerator<T> GetEnumerator()
  2. {
  3.     if (IsEmpty) yield break;
  4.     yield return _value;
  5. }
  6.  
  7. IEnumerator IEnumerable.GetEnumerator()
  8. {
  9.     return GetEnumerator();
  10. }

Básicamente un Maybe<T> lleno se comporta como una colección de un elemento de tipo T, mientras que un Maybe<T> vacío se comporta como una colección vacía. A partir de aquí… tenemos todo el poder de LINQ para realizar transformaciones, consultas, uniones, etc… con nuestros Maybe<T> con otros Maybe<T> o cualquier otra colección. P. ej. podríamos tener el siguiente código:

  1. [TestMethod]
  2. public void Comprobar_Que_Nombre_es_Null()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName("pepe");
  6.     var tiene_nombre= persona.Apply(p => p.Nombre).Any();
  7.     Assert.IsFalse(tiene_nombre);
  8. }

Y por supuesto podemos iterar con foreach sobre los elementos de un Maybe<T> 🙂

Ya para finalizar vamos a añadir un poco más de infrastructura a la clase Maybe<T>. En concreto soporte para la comparación:

  1. public static bool operator ==(Maybe<T> one, Maybe<T> two)
  2. {
  3.     if (one.IsEmpty && two.IsEmpty) return true;
  4.     return typeof(T).IsValueType ?
  5.         EqualityComparer<T>.Default.Equals(one._value, two._value) :
  6.     object.ReferenceEquals(one.Value, two.Value);
  7. }
  8.  
  9. public bool Equals(Maybe<T> other)
  10. {
  11.     return _isEmpty.Equals(other._isEmpty) && EqualityComparer<T>.Default.Equals(_value, other._value);
  12. }
  13.  
  14. public override bool Equals(object obj)
  15. {
  16.     if (ReferenceEquals(null, obj)) return false;
  17.     return obj is Maybe<T> && Equals((Maybe<T>)obj);
  18. }
  19.  
  20. public static bool operator !=(Maybe<T> one, Maybe<T> two)
  21. {
  22.     return !(one == two);
  23. }
  24.  
  25. public override int GetHashCode()
  26. {
  27.     unchecked
  28.     {
  29.         return (_isEmpty.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(_value);
  30.     }
  31. }

Maybe<T> intenta replicar el comportamiento de comparación de T. Es decir:

  • Dos Maybe<T> son “Equals” si los dos Ts de cada Maybe son “Equals”
  • Un Maybe<T> == otro Maybe<T> si:
    • Ambos Ts son el mismo objeto (en el caso de tipo por referencia)
    • Ambos Ts son “Equals” en el caso de tipos por valor

P. ej. el siguiente test valida el comportamiento de ==:

  1. var i = 10;
  2. var i2 = 10;
  3. var p = new Persona();
  4. var p2 = new Persona();
  5. Assert.IsTrue(new Maybe<int>(i) == new Maybe<int>(i2));
  6. Assert.IsFalse(new Maybe<Persona>(p) == new Maybe<Persona>(p2));

Y finalmente añadimos soporte para la conversión implícita de Maybe<T> a T:

  1. public static implicit operator Maybe<T>(T from)
  2. {
  3.     return new Maybe<T>(from);
  4. }

Dicha conversión nos permite simplificar las funciones que deben devolver un Maybe<T>. Así ahora la función del modelo puede ser:

  1. public Maybe<Persona> GetPersonaByName(string nombre)
  2. {
  3.     return nombre == "pepe" ?
  4.         new Persona {Nombre = null, Edad = 42} :
  5.         null;
  6. }

Fíjate que la función GetPersonaByName sigue devolviendo un Maybe<Persona> pero para el código es como si devolviese un Persona. El return null se traduce a devolver un Maybe<Persona> vacío.

Bueno… con eso termino el post. Espero que os haya resultado interesante y que hayáis visto otras posibles maneras de lidiar con las dichosas referencias null.

Saludos!

ASP.NET vNext–Crea tus propios “comandos K”

Una de las características de ASP.NET vNext son los “comandos K”, es decir aquellos comandos que se invocan desde línea de comandos a través del fichero K.cmd.

Dichos comandos están definidos en el project.json y la idea es que ofrezcan tareas necesarias durante el ciclo de vida de compilación y pruebas. Así podemos tener un comando (p. ej. K run) que nos ejecute el proyecto y otro (K test) que nos lance los tests unitarios. Recordad siempre que ASP.NET vNext se crea con el objetivo de que sea multiplataforma total: no solo que sea ejecutable en Linux o MacOSX a través de Mono, si no que sea posible desarrollar en esos sistemas operativos. Y eso implica “desligarse” de Visual Studio. Por supuesto, eso no quita que VS añada e implemente su propio soporte para el ciclo de vida de compilación y pruebas.

De hecho, incluso actualmente en VS no es nuevo usar comandos para gestionar algunas de las tareas necesarias para el ciclo de vida. Así muchos de vosotros conoceréis el comando Install-Package para instalar un paquete NuGet. Así, a dia de hoy, usamos la “Package Manager Console” que no es más que un wrapper sobre PowerShell. Para algunos comandos (como el citado Install-Package) el propio VS ofrece una alternativa gráfica pero hay otros comandos que solo se pueden lanzar desde dicha consola. El ejemplo más claro son todos los comandos de EF Migrations (p. ej. Update-Database).

La razón de que ASP.NET vNext se “olvide” de los comandos Powershell es que Powershell solo funciona en Windows y recuerda… ASP.NET vNext es multiplataforma de verdad.

Por supuesto nosotros podemos crear nuestros propios “comandos K” para añadir tareas que nos sean necesarias o útiles para el ciclo de vida de compilación y pruebas de la aplicación. Porque básicamente un comando definido en el project.json lo único que hace es invocar a un ensamblado.

Vamos a ver como podemos hacerlo. 🙂

Los comandos residen en un assembly que debe ser compilado con vNext. En nuestro caso vamos a crear una “ASP.NET vNext Console Application” usando VS14 CTP3. En mi caso la he llamado TestCommands.

image

La verdad es que el template que viene con VS14 CTP3 para dicho tipo de aplicaciones es casi inútil, pero bueno… menos da una piedra. Dicho template genera la clásica clase “Program” con su método Main, útil para ejecutables, pero en nuestro caso nuestra aplicación estará lanzada a través de un comando K, así que será el propio framework de ASP.NET vNext quien invocará nuestra aplicación.

Lo primero es agregar una dependencia a Microsoft.Framework.Runtime.Common en nuestro project.json:

  1. "dependencies": {
  2.     "Microsoft.Framework.Runtime.Common": "1.0.0-alpha3"
  3. },

Luego podemos modificar la clase Program para que quede de la siguiente así:

  1. public class Program
  2. {
  3.     public Program(IApplicationEnvironment env)
  4.     {
  5.         Console.WriteLine("In Progrm.ctor " + env.ApplicationBasePath);
  6.     }
  7.     public void Main(string[] args)
  8.     {
  9.         Console.WriteLine("In main");
  10.         Console.ReadLine();
  11.     }
  12. }

Si lo ejecutas con VS directamente verás algo parecido:

image

Por supuesto puedes ejecutarlo también usando KRE. Para ello asegúrate de tener en el path la carpeta donde está KRE instalado. Puedes tener varios KREs instalados side by side y por defecto se instalan en %HOME%.krepackages. Así p. ej. en mi maquina tengo:

image

Debes agregar al path la carpeta bin del KRE que quieras usar. Así p. ej. yo tengo agregado al path la carpeta C:Usersetomas.krepackagesKRE-svr50-x86.1.0.0-alpha3bin

Así si navegamos a la carpeta donde está el fichero project.json y ejecutamos “k run”:

image

En el caso de que os de un error de que no puede encontrar algún assembly lanzando un mensaje de error parecido al siguiente:

System.InvalidOperationException: Failed to resolve the following dependencies:
   Microsoft.Framework.Runtime.Common 1.0.0-alpha3

Debes ejecutar el comando “kpm restore”. Eso es necesario cuando la maquina en la que estás ejecutando no tiene alguno de los paquetes marcados como dependencias en el project.json. Si es la propia maquina en la que tienes VS14 eso no te ocurrirá (VS14 se descarga los paquetes) pero, p. ej. yo he copiado el proyecto a otra máquina donde no tenía VS14, pero sí el runtime de vNext y he necesitado ejecutar dicho comando.

Fijate en cuatro detalles:

  1. Se pasa primero por el constructor de la clase Program
  2. El framework nos inyecta automáticamente el objeto IApplicationEnvironment
  3. Luego se llama automáticamente al método Main
  4. No es necesario que el método Main sea estático.

Si te preguntas como pasar parámetros al método main, pues simplemente añadiéndolos al comando “k run”. Así, si p. ej. tecleas “k run remove /s” el método Main recibirá dos parámetros (“remove” y “/s”). Para pasar parámetros con VS debes usar las propiedades del proyecto (sección Debugging).

Podríamos implementar un comando “list” que listase todos los ficheros del proyecto:

  1. public class Program
  2. {
  3.     private readonly string _folder;
  4.     public Program(IApplicationEnvironment env)
  5.     {
  6.         _folder = env.ApplicationBasePath;
  7.     }
  8.     public void Main(string[] args)
  9.     {
  10.         if (args.Length == 1 && args[0] == "list")
  11.         {
  12.             ListFiles();
  13.         }
  14.         Console.ReadLine();
  15.     }
  16.  
  17.     private void ListFiles()
  18.     {
  19.         var files = Directory.EnumerateFiles(_folder, "*.*", SearchOption.AllDirectories);
  20.         foreach (var file in files)
  21.         {
  22.             Console.WriteLine(file);
  23.         }
  24.     }
  25. }

Para poder usar la clase Directory debes añadir una dependencia en el project.json al paquete System.IO.FileSystem (recuerda que en vNext todo el framework está fragmentado y dividido en paquetes NuGet).

Bien, ahora ya sabemos que podemos ejecutar este programa con “k run list”, pero a nosotros lo que nos interesa es tener un “comando k” adicional para usar con otro programa vNext.

Para ver como lo podemos montar vamos a agregar otro proyecto de consola de ASP.NET vNext a la solución. Yo lo he llamado DemoLauncher. No es necesario que toques nada del código.

Ahora debes agregar una dependencia desde DemoLauncher al otro proyecto (que en mi caso se llamaba TestCommands):

  1. "dependencies": {
  2.     "TestCommands": ""
  3. },

Puedes agregar esta dependencia porque, a pesar de que TestCommands no

está en NuGet está en la misma solución.

Ahora damos de alta el comando. Los comandos se dan de alta en el project.json. Así editamos el project.json de DemoLauncher para añadir una sección de commands:

  1. "commands": {
  2.     "tc" : "TestCommands"
  3. }

Con esto le indicamos que cuando se lance el comando “tc” se invoque al ensamblado “TestCommands”.

Ahora puedo ir a la carpeta donde está el project.json de DemoLauncher y si tecleo “k run” se ejecutará DemoLauncher (eso era de esperar):

image

Pero lo bueno viene ahora. Si desde esa misma carpeta tecleas “k tc list” se te listarán todos los ficheros que haya en la carpeta (y subcarpetas) de DemoLauncher:

image

Al lanzar “k tc” como en el project.json hay definido el comando “tc” se invoca al ensamblado “TestCommands” y se le pasan los parámetros que se hayan pasado después de tc. Así pues TestCommands recibe el parámetro “list” y lista todos los ficheros. Lo interesante es que TestCommands se ejecuta bajo el contexto de ejecución de DemoLauncher (la propiedad ApplicationBasePath del IApplicationEnvironment apunta al directorio donde está DemoLauncher).

En un escenario final real, tendríamos “TestCommands” publicado a NuGet, pero el resto vendría a ser lo mismo que hemos visto.

La gente de EF ya ha empezado a usar esa táctica para los comandos de Migrations (y así posibilitar el uso de Migrations en entornos no windows al no depender más de Powershell). Y personalmente creo que vamos a ver bastantes de esos futuros comandos.

Un saludo!

ASP.NET MVC6 (vNext)–ViewComponents

En ASP.NET vNext se unifican MVC y WebApi en una nueva API llamada MVC6. Aunque MVC6 se parece a MVC5 no es compatible con ella, del mismo modo que WebApi se parece a MVC pero por debajo son muy distintas.

Ya hemos viso algunas de las novedades o cambios que trae MVC6 (temas de model binding, controladores POCO, …) y en este post vamos a explorar uno más: los ViewComponents.

Resumiendo: los ViewComponents sustituyen a las vistas parciales. Ya no existe este concepto en MVC6. De hecho, tampoco nos engañemos, desde razor la diferencia entre vistas parciales y vistas normales (a nivel del archivo .cshtml) es muy pequeña: se puede usar una vista parcial como vista normal tan solo cambiando el “return PartialView()” por un “return View()” (o viceversa). En el motor de vistas de ASPX eso no era así, ya que las vistas eran archivos .aspx y las vistas parciales eran archivos .ascx.

En Razor la única diferencia actual entre una vista parcial y una normal es que en la segunda se procesa el archivo de Layout (usualmente _Layout.cshtml) y en la primera no. Pero no es el archivo .cshtml quien determina si es vista normal o parcial. Es el ActionResult devuelto. Si devuelves un ViewResult el archivo .cshtml se procesará como vista normal. Si devuelves un PartialViewResult el archivo .cshtml se procesará como vista parcial.

El código “clásico” en MVC5 para tener una vista parcial era algo como:

  1. public ActionResult Child()
  2. {
  3.     return PartialView();
  4. }

El problema con este enfoque es que esta acción es enrutable, por lo que cualquiera puede ir a la URL que enrute esa acción (p. ej. Home/Child si suponemos HomeController) y recibirá el contenido HTML de la vista parcial. Eso, generalmente, no se desea (para algo la vista es parcial).

Para solventar esto, en MVC4 se añadió el atributo [ChildActionOnly] que evitaba que una acción se enrutase. Así si decoramos la acción con dicho atributo cuando el usuario navega a la URL que debería enrutar dicha acción recibirá un error:

image

La acción se puede invocar a través del helper Html.RenderAction:

  1. @{ Html.RenderAction("Child", "Home"); }

Nota: Se puede usar Html.Partial o Html.RenderPartial para renderizar una vista parcial directamente (sin pasar por un controlador). Eso es útil en el caso de que no haya lógica asociada a dicha vista parcial (si la hay, lo suyo es colocarla en la acción y usar Html.RenderAction).

Bueno… así tenemos las cosas hoy en día: básicamente colocamos las acciones “hijas” en un controlador (porque es donde podemos colocar lógica) pero luego las quitamos del sistema de enrutamiento (con [ChildActionOnly]) y las llamamos indicando directamente que acción y que controlador es.

Realmente ¿tiene sentido que las acciones hijas estén en un controlador? No. Porque la responsabilidad del controlador es, básicamente, responder a peticiones del navegador y eso no es una petición del navegador.

Así en MVC6 se elimina el concepto de vista parcial y el PartialViewResult, y se sustituye por el concepto de ViewComponent. Ahora lo que antes eran acciones hijas son clases propias que derivan de ViewComponent:

  1. [ViewComponent(Name = "Child")]
  2. public class ChildComponent : ViewComponent
  3. {
  4.     public async Task<IViewComponentResult> InvokeAsync()
  5.     {
  6.         return View();
  7.     }
  8. }

El atributo [ViewComponent] nos permite especificar el nombre que damos al componente. El siguiente paso es definir el método InvokeAsync que devuelve una Task<IViewComponentResult> con el resultado. La clase ViewComponent nos define el método View() que devuelve la vista asociada a dicho componente (de forma análoga al método View() de un controlador).

La ubicación por defecto de la vista asociada a un componente es /Views/Shared/Components/[NombreComponente]/Default.cshtml. Es decir en mi caso tengo el fichero Default.cshtml en /Views/Shared/Components/Child:

image

Por supuesto ahora tengo un sitio donde colocar la lógica (si la hubiera) de dicho componente: la propia clase ChildComponent.

Finalmente nos queda ver como renderizamos el componente. Ya no tenemos Html.RenderAction, si no que en su lugar usamos la propiedad Component que tienen las vistas de MVC6:

  1. @await Component.InvokeAsync("Child")

Simplemente le pasamos el nombre del componente (el mismo definido en el atributo [ViewComponent].

Y listos 🙂

Creando tu librería JavaScript con requirejs y gulp

Si desarrollas aplicaciones web con alta carga de código en cliente, es posible que tengas que desarrollarte tus propias funciones JavaScript. Incluso tu propia librería o framework si es el caso.

Una opción es abrir tu editor de texto favorito, crear un archivo .js y empezar a teclear. Total, jQuery viene en un solo archivo .js, ¿verdad? Antes de hacer esto, párate a pensar un poco: ¿a qué nunca colocarías todo el código c# de tu proyecto en un solo fichero .cs? Tenerlo en varios ficheros permite localizar el código más rápidamente, tenerlo mejor organizado y evitar conflictos cuando se trabaja en equipo. Pues bien, eso mismo aplica a JavaScript.

Por supuesto los desarrolladores de jQuery (y de cualquier otra librería decente) no trabajan en un solo fichero. Tienen su código distribuido en ficheros separados que se combinan al final para crear la librería. Vamos a ver como podemos hacer esto de forma sencilla.

Lo primero que tenemos que hacer es precisamente definir los módulos de nuestra librería. Básicamente un módulo es un fichero .js, que define un conjunto de funciones que son accesibles a todos los clientes que usen el módulo. El código de dichos ficheros se organiza siguiendo un patrón que se conoce precisamente como patrón de módulo (revealing module pattern o alguna de sus variantes).

El problema está que en JavaScript no hay (de momento) ninguna manera de establecer las dependencias entre módulos: es decir, de indicar que el módulo A, depende del módulo B (lo que quiere decir que el módulo B debe estar cargad antes que el módulo A). Es ahí donde entra una librería llamada requirejs. Dicha librería permite especificar las dependencias entre módulos. Veamos un ejemplo de módulo con soporte para require:

  1. define([], function () {
  2.     var rnd = function () {
  3.         return 42;
  4.     };
  5.  
  6.     var isOdd = function (i) {
  7.         return (i % 2) != 0;
  8.     };
  9.  
  10.     return {
  11.         rnd: rnd,
  12.         isOdd: isOdd
  13.     };
  14.  
  15. });

Lo importante ahí es la funcion define. Dicha función está definida en requirejs y suele tomar dos parámetros:

  1. Un array con las dependencias del módulo.
  2. Una función con el código del módulo. Dicha función puede recibir como parámetro los módulos especificados en las dependencias.

El módulo define dos funciones (rnd y isOdd) y luego las exporta. Exportarlas significa que las hace visibles al resto de módulos que dependan de este. La forma de exportar es devolviendo un resultado: todo lo que se devuelve, es exportado.

Ahora vamos a declarar otro módulo (llamémosle main.js) que hará uso de este módulo que hemos definido:

  1. define([‘math/rnd’], function (math) {
  2.     var addOnlyIfOdd = function (a, b) {
  3.         var result = 0;
  4.         if (math.isOdd(a)) result += a;
  5.         if (math.isOdd(b)) result += b
  6.         return result;
  7.     };
  8.  
  9.     return {
  10.         addOnlyIfOdd: addOnlyIfOdd
  11.     };
  12. });

Es lo mismo que antes con la diferencia de que ahora el array de dependencias contiene el módulo (math/rnd). Este es nuestro módulo anterior. El nombre de un módulo es el nombre del fichero js tal cual (incluyendo la ruta) (existen los llamados módulos AMD donde esto no tiene porque ser así, pero tampoco es relevante ahora). Por lo tanto, en este código, el módulo anterior lo habíamos guardado en math/rnd.js Dentro de este módulo podemos usar todo lo que el módulo anterior exportaba a través del parámetro math.

¿Como usaríamos eso desde una página web?

Lo primero es cargar requirejs desde la página web y usar data-main para indicarle a require el módulo “inicial”:

  1. <!DOCTYPE html>
  2. <head>
  3.     <title>Demo</title>
  4.     <script data-main=«./main.js» src=«http://requirejs.org/docs/release/2.1.14/minified/require.js»></script>
  5. </head>
  6. <body>
  7. </body>

Si ejecutamos este fichero html y miramos la pestaña Network veremos como no solo se carga el fichero requirejs si no que tanto main.js como también math/rnd.js se cargan: require ha cargado tanto el módulo inicial como todas sus dependencias.

Esto nos permite dividir nuestro código JavaScript con la tranquilidad de que luego los scripts se cargarán en el orden correcto, gracias a requirejs.

Vale, pero estarás diciendo que jQuery viene en un solo js único y para cargar jQuery no debes usar require para nada. Y tienes razón. La realidad es que la gente de jQuery (y tantos otros frameworks) tienen su código dividido en módulos pero luego los unen todos en el orden correcto en un solo js. Y eso lo hacen cuando “construyen” jQuery. Vamos a ver un ejemplo usando gulp (en jQuery se usa grunt que es similar).

Gulp es un sistema de build basado en JavaScript. Se basa en node (así que es necesario tener node instalado). Pero teniendo node instalado, instalar gulp es seguir dos pasos:

  1. Ejecutar npm install –g gulp. Eso instala gulp a nivel “global”. Debe ejecutarse una sola vez.
  2. Ejecutar npm install –save-dev gulp. Eso debe ejecutarse desde el directorio donde tenemos el código fuente de nuestra librería. Eso instala gulp a nivel local y debe hacerse una vez por cada librería instalada.

El tercer paso es crear un fichero javascript, llamado gulpfile.js que será el que tenga las distintas tareas para generar nuestro proyecto JavaScript.

Para el ejemplo vamos a suponer que tenemos nuestro fichero main.js y el fichero math/rnd.js dentro de una subcarpeta src. El fichero gulpfile.js está en la carpeta que contiene a /src. Es decir tenemos una estructura tal que así:

image

Ahora vamos a crear una tarea en gulp para combinar todos los módulos de nuestro proyecto en un .js final. Para ello tenemos que instalar requirejs como módulo de node mediante npm install –save-dev requirejs (desde el directorio de nuestro proyecto).

Finalmente podemos crear una tarea, llamada “make” en nuestro gulpfile:

  1. var gulp = require(‘gulp’);
  2. var requirejs = require(‘requirejs’);
  3. gulp.task(‘make’, function () {
  4.     var config = {
  5.         baseUrl: ‘src’,
  6.         name: ‘main’,
  7.         out: ‘dist/demo.js’,
  8.         findNestedDependencies: true,
  9.         skipSemiColonInsertion: true,
  10.         optimize: ‘none’
  11.     };
  12.     requirejs.optimize(config, function (buildResponse) {
  13.         console.log(buildResponse);
  14.     });
  15. });

Cargamos los módulos de node que vamos a usar (gulp y require) y luego usamos requirejs.optimize para cargar el módulo incial, junto con todas sus dependencias y generar un solo .js. Esto es precisamente lo que hace la llamada a requirejs.optimize. El primer parámetro es la configuración. Existen muchas opciones de configuración, siendo las más importantes:

  1. baseUrl: Directorio base donde están los módulos
  2. name: Módulo inicial
  3. out: Fichero de salida
  4. optimize: Si debe minimificar el archivo de salida o no.

Una vez hemos modificado el fichero gulpfile.js ya podemos ejecutarlo, mediante gulp make. Esto invocará la tarea ‘make’ del fichero gulpfile:

image

Una vez ejecutado en el directorio dist tendremos un fichero (demo.js) con todos nuestros módulos combinados en el orden correcto. Este sería el fichero que distribuiríamos y que se usaría desde el navegador.

Eso nos permite tener nuestro código javascript bien separado y organizado, con la tranquilidad de que siempre podemos generar la versión unificada “final” usando gulp.

Por supuesto gulp sirve para mucho más que combinar los módulos. Puede minimificar salidas, pasar tests unitarios, realizar optimizaciones adicionales, tareas propias… en fin, cualquier cosa que uno esperaría de un sistema de builds.

¡Un saludo!

ASP.NET vNext–Model Binding

Bien, en el post anterior comentamos cuatro cosillas sobre el model binding en ASP.NET MVC y WebApi, sus semejanzas y sus diferencias. En ASP.NET vNext ambos frameworks se unifican así que es de esperar que el model binding también lo haga… Veamos como funciona el model binding de vNext.

Nota: Este post está realizado con la versión de ASP.NET vNext que viene con el VS14 CTP2. La mejor manera de probar dicha CTP es usando una VM en Azure creada a partir de una plantilla que ya la contiene instalada. Por supuesto todo lo dicho aquí puede contener cambios en la versión final 🙂

Pruebas de caja negra

Antes que nada he intentado hacer unas pruebas de “caja negra” para ver si el comportamiento era más parecido al de WebApi o al de MVC. He empezado con un proyecto web vNext vacío, y en el project.json he agregado la referencia a Microsoft.AspNet.Mvc. Luego me he creado un controlador como el siguiente:

  1. public class HomeController : Controller
  2. {
  3.     public IActionResult Index(Product product, Customer customer)
  4.     {
  5.         return View();
  6.     }
  7.  
  8.     public bool Post(Product product, Customer customer)
  9.     {
  10.         return true;
  11.     }
  12. }

Finalmente en el Startup.cs he configurado una tabla de rutas que combine MVC y WebApi:

  1. public class Startup
  2. {
  3.     public void Configure(IBuilder app)
  4.     {
  5.         app.UseServices(s => s.AddMvc());
  6.  
  7.         app.UseMvc(r =>
  8.         {
  9.             r.MapRoute(
  10.                 name: "default",
  11.                 template: "{controller}/{action}/{id?}",
  12.                 defaults: new { controller = "Home", action = "Index" });
  13.             r.MapRoute(
  14.                 name: "second",
  15.                 template: "api/{Controller}/{id?}"
  16.                 );
  17.         });
  18.     }
  19. }

Con esa tabla de rutas un POST a /Home/Index debe enrutarme por la primera acción del controlador (al igual que un GET). Mientras que un POST a /api/Home debe enrutarme por la segunda acción del controlador (mientras que un GET a /api/Home debe devolverme un 404). Para más información echa un vistazo a mi post sobre el routing en vNext.

Las clases Customer y Product contienen simplemente propiedades:

  1. public class Customer
  2. {
  3.     public int Id { get; set; }
  4.     public string Name { get; set; }
  5.     public string Gender { get; set; }
  6. }

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

Luego he usado cURL para realizar unos posts y ver que es lo que tenía:

curl –data "Id=1&Name=eiximenis&Gender=Male" http://localhost:49228/  –header "Content-type:application/x-www-form-urlencoded"

Con esto simulo un post a que contenga los datos Id, Name y Gender y eso es lo que recibo en el controlador (en el método Index):

image

Este comportamiento es el mismo que en ASP.NET MVC. Ahora cambio la petición de cURL para enviar la misma petición pero a /api/Home para que se me enrute al método Post (estilo WebApi). Mi idea era ver si para enrutamiento tipo MVC se usaba un binding parecido a MVC y para enrutamiento tipo WebApi (sin acción y basado en verbo HTTP) se usaba un binding parecido al de WebApi:

curl –data "Id=1&Name=eiximenis&Gender=Male" http://localhost:49228/api/Home  –header "Content-type:application/x-www-form-urlencoded"

El resultado es que se me llama al método Post del controlador pero recibo exactamente los mismos valores que antes. Recordad que en WebApi eso NO era así. Así a simple vista parece que se ha elegido el modelo de model binding de ASP.NET MVC antes que el de web api.

Otra prueba ha sido realizar un POST contra /api/Home/10 (el parámetro 10 se corresponde al route value id) y dado que estamos pasando el id por URL quitarlo del cuerpo de la petición:

curl –data "Name=eiximenis&Gender=Male" http://localhost:49228/api/Home/10  –header "Content-type:application/x-www-form-urlencoded"

El resultado es el mismo que en el caso anterior (y coincide con ASP.NET MVC donde el model binder ni se preocupa de donde vienen los datos).

Por lo tanto estas pruebas parecen sugerir que en vNext el model binding que se sigue es el de ASP.NET MVC.

Claro que cuando uno pruebas de caja negra debe tener presente el máximo número de opciones… Porque resulta que si hago algo parecido a:

curl –data "{‘Name’:’eiximenis’,’Gender’:’Male’}"http://localhost:49228/api/Home  –header "Content-type:application/json"

Entonces resulta que ambos parámetros son null. Parece ser que vNext no enlaza por defecto datos en JSON, solo en www-form-urlencoded. Además mandar datos en JSON hace que los parámetros no se enlacen. Aunque mande datos a través de la URL (p. ej. como route values) esos no se usan.

Por supuesto vNext soporta JSON, pero es que nos falta probar una cosilla…

Atributo [FromBody]

De momento en vNext existe el atributo [FromBody] (pero no existe el [FromUri]). Ni corto ni perezoso he aplicado el FromBody a uno de los parámetros del controlador:

  1. public bool Post(Product product, [FromBody] Customer customer)
  2. {
  3.     return true;
  4. }

Y he repetido la última petición (el POST a /api/Home/10). Y el resultado ha sido… un error:

System.InvalidOperationException: 415: Unsupported content type Microsoft.AspNet.Mvc.ModelBinding.ContentTypeHeaderValue

He modificado la petición cURL para usar JSON en lugar de form-urlencoded:

curl –data "{‘Name’:’eiximenis’,’Gender’:’Male’}" http://localhost:49228/api/Home/10  –header "Content-type:application/json"

Y el resultado ha sido muy interesante:

image

El parámetro customer se ha enlazado a partir de los datos en JSON del cuerpo (el Id está a 0 porque es un route value y no está en el cuerpo de la petición) pero el parámetro product está a null. Por lo tanto el uso de [FromBody] modifica el model binding a un modelo más parecido al de WebApi.

WebApi solo permite un solo parámetro enlazado desde el cuerpo de la petición. Mi duda ahora era si vNext tiene la misma restricción. Mirando el código fuente de la clase JsonInputFormatter intuía que sí… y efectivamente. Aunque a diferencia de WebApi no da error si no que tan solo enlaza el primer parámetro. Así si tengo el método:

  1. public bool Post([FromBody] Product product, [FromBody] Customer customer)

Y repito la llamada cURL anterior, los datos recibidos son:

image

El parámetro product (el primero) se ha enlazado a partir del cuerpo de la petición y el segundo vale null.

¿Y como funciona todo (más o menos)?

Recordad que ASP.NET vNext es open source y que nos podemos bajar libremente el código de su repositorio de GitHub. Con este vistazo al código he visto algunas cosillas.

El método interesante es el método GetActionArguments de la clase ReflectedActionInvoker. Dicho método es el encargado de obtener los argumentos de la acción (por tanto de todo el proceso de model binding). Dicho método hace lo siguiente:

  • Obtiene el BindingContext. El BindingContext es un objeto que tiene varias propiedades, entre ellas 3 que nos interesan:
    1. El InputFormatterProvider a usar
    2. El ModelBinder a usar
    3. Los Value providers a usar
  • Obtiene los parámetros de la acción. Cada parametro viene representado por un objeto ParameterDescriptor. Si el controlador acepta dos parámtetros (customer y product) existen dos objetos ParameterDescriptor, uno representando a cada parámetro de la acción. Dicha clase tiene una propiedad llamada BodyParameterInfo. Si el valor de dicha propiedad es null se usa un binding más tipo MVC (basado en value providers y model binders). Si el valor no es null se usa un binding más tipo WebApi (basado en InputFormatters).

Por defecto vNext viene con los siguientes Value Providers:

  1. Uno para query string (se crea siempre)
  2. Uno para form data (se crea solo si el content type es application/x-www-form-urlencoded
  3. Otro para route values (se crea siempre)

La clave está en el uso del atributo [FromBody] cuando tenemos un parámetro enlazado mediante este atributo entonces no se usan los value providers si no los InputFormatters. Pueden haber dado de alta varios InputFormatters pero solo se aplicará uno (basado en el content-type). Por defecto vNext incluye un solo InputFormatter para application/json.

Ahora bien… qué pasa si tengo un controlador como el siguiente:

  1. public IActionResult Index([FromBody] Customer customer, Product product)
  2. {
  3.     return View();
  4. }

Y hago la siguiente petición?

C:UsersetomasDesktopcurl>curl –data "{‘Name’:’eiximenis’,’Gender’:’Male’}" http://localhost:38820/Home/Index/100?Name=pepe –header "Content-type:application/json"

Pues el valor de los parámetros será como sigue:

image

Se puede ver como el parámetro enlazado con el [FromBody] se enlaza con los parámetros del cuerpo (en JSON) mientras que el parámetro enlazado sin [FromBody] se enlaza con el resto de parámetros (de la URL, routevalues y querystring). En vNext el [FromUri] no es necesario: si hay un [FromBody] el resto de elementos deben ser enlazados desde la URL. Si no hay [FromBody] los elementos serán enlazados desde cualquier parte de la request.

Bueno… en este post hemos visto un poco el funcionamiento de ASP.NET vNext en cuanto a model binding. El resumen es que estamos ante un modelo mixto del de ASP.NET MVC y WebApi.

En futuros posts veremos como podemos añadir InputFormatters y ValueProviders para configurar el sistema de model binding de vNext.

Saludos!

Model binding en ASP.NET MVC y WebApi

Una de las confusiones más habituales con la gente que viene de MVC y pasa a WebApi es que el funcionamiento del model binding (es decir rellenar los parámetros de los controladores a partir de los datos de la request) es distinto entre ambos frameworks. La confusión viene porque a primera vista todo parece que funcione igual pero realmente hay diferencias muy profundas entre ambos frameworks.

Veamos, pues, algunas de las diferencias que hay entre ambos frameworks

ASP.NET MVC Value Providers primero y Model Binders después

En ASP.NET MVC el proceso de model binding está compuesto de dos pasos:

  1. Primero existe una serie de clases, llamadas value providers que se encargan de leer la petición HTTP y colocar los datos en un sitio común (como un diccionario). Así no importa si un dato (p. ej. Id) está en querystring o en formdata). Simplemente, se obtendrá ese Id y se colocará en el ese sitio común. Hay varios value providers porque cada value provider puede analizar una parte de la petición. Así hay un value provider que analiza la querystring, un par que analizan formdata (uno si el content-type es application/x-www-form-urlencoded y otro si es application/json) y así sucesivamente.
  2. Una vez los datos de la petición están en este lugar común entra en acción el model binder. El model binder recoge los datos de ese sitio común y los utiliza para crear los objetos que son pasados como parámetros en la acción del controlador. Cada parámetro es enlazado por un model binder distinto dependiendo del tipo del parámetro (aunque por defecto casi todos los tipos se enlazan usando el DefaultModelBinder, nosotros podemos crear model binders propios y vincularlos a tipos de parámetros).

Supongamos dos clases como las siguientes:

  1. public class Customer
  2. {
  3.    public int Id { get; set; }
  4.    public string Name {  get; set; }
  5.    public string Address{ get; set; }
  6. }
  7.  
  8. public class Product
  9. {
  10.    public int Id { get; set; }
  11.    public string Name { get; set; }
  12. }

Y una vista que envíe los datos:

  1. <form>
  2.     <input type="text" name="Id" /><br />
  3.     <input type="text" name="Name" /><br />
  4.     <input type="text" name="Address" /><br />
  5.     <input type="submit" value="send"/>
  6. </form>

Ahora si tenemos un controlador que tiene una acción con dos parámetros (Customer y Product) ¿qué es lo que recibimos? Pues lo siguiente:

image

Es decir el valor de las propiedades Id y Name se ha enlazado a ambos parámetros. Es lógico: los value providers han recogido 3 valores de la petición resultado de enviar el formulario (Id, Name y Address) y los han colocado en el “sitio común”. Luego cuando el model binder del parámetro customer debe crear el objeto usa los valores de dicho sitio común para enlazar las propiedades… lo mismo que el model binder del parámetro product. De ahí que las propiedades que se llamen igual tengan el mismo valor.

Por supuesto esta posibilidad está contemplada en ASP.NET MVC, de forma que el model binder entiende de “prefijos”. Así si modifico uno de los campos de texto para que sea:

  1. <input type="text" name="customer.Name" />

Ahora el valor del campo de la petición llamado “customer.Name” se enlazará solo a la propiedad Name del parámetro customer (product.Name será null).

El modelo de value providers + model binders es muy versátil y potente.

WebApi – Básicamente MediaTypeFormatters (básicamente)

En WebApi la aproximación es radicalmente distinta. Primero hay una distinción clara, clarísima, sobre si el parámetro de la acción se enlaza desde el cuerpo de la petición o desde cualquier otro sitio (p. ej. querystring).

En MVC el model binder no sabe de donde vienen los datos que usa para enlazar los parámetros del controlador, ya que los saca siempre del “sitio común” (donde lo dejan los value providers). WebApi usa una orientación distinta pero la regla de oro fundamental es: El cuerpo de la petición puede ser leído una sola vez.

En efecto en WebApi el cuerpo de la petición HTTP es un stream forward-only, lo que significa que puede ser leído una única vez (en ASP.NET MVC el cuerpo está cacheado ya que distintos value providers pueden leerlo). Eso está hecho por temas de rendimiento.

Así en WebApi se usa un paradigma basado básicamente en el content-type. Aparecen unos entes nuevos (los media type formatters) encargados de leer el cuerpo de la petición. Pero SOLO UN media type formatter puede leer el cuerpo (recuerda: puede ser leído una sola vez). Y como sabe WebApi qué media type formatter procesa el cuerpo de la petición? Pues basándose en el content-type.

El media type formatter lee pues el cuerpo de la petición y rellena un objeto de .NET (del tipo indicado) en base a dichos datos. Efectivamente: el media type formatter lee de la petición y enlaza las propiedades. Eso implica una restricción importante: tan solo un parámetro de la acción puede ser obtenido a partir de los datos del cuerpo de una petición.

Veamos la diferencia: he creado un controlador WebApi con un método análogo al anterior que recibe (via POST) un Customer y un Product:

  1. public class DataController : ApiController
  2. {
  3.     public bool Post(Product product, Customer customer)
  4.     {
  5.         return true;
  6.     }
  7. }

Y he modificado la vista para que haga post del formulario a dicho controlador. Existe un media type formatter que se encarga de leer los datos cuando el content-type es application/x-www-form-urlencoded (de hecho, realmente hay dos pero tampoco es necesario entrar en más detalles). ¿Y cual es el resultado? Pues un error. Concretamente una System.InvalidOperationException con el mensaje: Can’t bind multiple parameters (‘product’ and ‘customer’) to the request’s content.

La razón es que tenemos dos parámetros a rellenar basándonos en el cuerpo de la petición. Pero dicho cuerpo puede ser leído una sola vez por un media type formatter. Y un media type formatter tan solo puede enlazar un objeto.

Es ahí donde entra el atributo FromUri. Dicho atributo se aplica a parámetros de la accion para indicar que esos parámetros se deben enlazar a partir de datos encontrados en la URL (querystring):

  1. public bool Post(Product product, [FromUri] Customer customer)
  2. {
  3.     return true;
  4. }

Si ahora ejecutamos de nuevo podemos ver que recibe el controlador son:

image

Podemos ver como customer no tiene datos (a pesar de haber un campo Address en el cuerpo de la petición) porque customer se enlaza a partir de los datos de la URL (querystring).

Por defecto WebApi usa siempre un media type formatter cuando el parámetro es un tipo complejo (una clase) a no ser que haya el atributo FromUri aplicado. Si el parámetro es un tipo simple (p. ej. int o un string) intenta enlazarlo a partir de la URL a no ser que haya aplicado el atributo contrario [FromBody]. Eso sí recuerda que solo un parámetro puede ser enlazado desde el cuerpo de la petición.

WebApi – ModelBinders y Value Providers

Cuando se enlaza un parámetro que no viene del cuerpo de la petición (es decir que viene de la URL) WebApi usa entonces el mismo modelo que MVC. Es decir primero los value providers recogen los datos de la petición (excepto el cuerpo) y los colocan en un sitio común y luego los model binders usan los datos de este sitio cómún para enlazar los parámetros).

P. ej. modifico el controlador de WebApi para que acepte GET y enlace ambos parámetros desde la URL:

  1. public bool Get([FromUri]Product product, [FromUri] Customer customer)
  2. {
  3.     return true;
  4. }

Ahora si realizo una llamada GET a /api/Data/10?name=test lo que obtengo es:

image

Los value providers recogen los datos de la URL (hay dos, uno para los route values (como /10) y otro para la querystring) y los dejan en el sitio común. Luego los model binders usan esos datos para enlazar tanto product como customer y es por eso que obtengo los datos duplicados (es el caso análogo al caso inicial de ASP.NET MVC).

En resumen: hemos visto cuatro pinceladas de como ASP.NET MVC y WebApi enlazan los parámetros a los controladores haciendo especial énfasis en las diferencias. Dejamos para un post posterior el ver como funciona este tema en vNext pues recuerda que en vNext WebApi y MVC son un solo framework 🙂

Saludos!

Cifrado en WebApi

Muy buenas! El otro día recibí el siguiente correo (a través del formulario de contacto del blog):

Estoy desarrollando una serie de web apis para transacciones con tarjetas de crédito. Mi problema es que no encuentro la forma de cifrar los datos sensibles como el numero de tarjeta de crédito. con un web service esta claro como hacerlos pero no encuentro la forma en una web api. que me recomendas?

Es un tema interesante y que da para mucho pero a ver si podemos dar cuatro pinceladas

Antes de nada lo dicho en este post trata de evitar que alguien que use un sniffer para analizar el tráfico de red pueda ver los datos confidenciales que enviamos. Este post no pretende ni puede ser una solución completa al problema, ya que hay muchas casuísticas que se deben tratar y muchos tipos de ataque a los que debemos responder. Honestamente la solución más sencilla pasa por usar HTTPS. Si usas HTTPS delegas toda la seguridad en el canal. Sin duda es lo más sencillo. No hay que hacer nada especial para poder usar HTTPS en WebApi. Si usas IIS Express para desarrollar (lo habitual con VS) habilitar el soporte para HTTPS está a un click de distancia:

SNAGHTML5d216779

Al seleccionar SSL Enabled VS te indica tanto la URL http como la https (con otro puerto claro). Para que todo funcione IIS Express genera un certificado SSL de desarrollo y ya tienes HTTPS habilitado. Por supuesto cuando despliegues en producción deberás desplegar el certificado SSL de producción en IIS.

Conceptos básicos de cifrado

Si no quieres usar IIS entonces debes encriptar los datos en el cliente y desencriptarlos en el servidor. Eso indica que debes especificar que método de encriptación se usa… hay muchos pero a grandes rasgos se dividen en dos grandes grupos:

  1. De clave simétrica
  2. De clave asimétrica

En los métodos de clave simétrica, cliente y servidor comparten una clave. Dicha clave es usada para generar texto cifrado a partir de texto plano… y viceversa. Es decir alguien que conozca la clave puede tanto enviar mensajes cifrados y descifrarlos.

En los métodos de clave asimétrica cada uno de los actores tiene un par de claves. Ambas claves sirven tanto para cifrar como para descifrar, pero con una particularidad: lo cifrado con una clave debe ser descifrado con la otra y viceversa.

Los métodos asimétricos tienen a priori una seguridad mayor que el método simétrico. Imagina a alguien llamado Alice que quiera enviar un mensaje a Bob. Con un método simétrico:

  1. Alice cifraría el mensaje con la clave K de encriptación
  2. Bob usaría la misma clave K para descifrarlo
  3. Si Bob quiere enviar una respuesta cifrada a Alice la cifraría usando la misma clave K y Alice lo descifraría usando K.

El punto débil aquí es el intercambio de claves. Si queremos que un mensaje de Alice a Bob solo pueda ser leído por Bob debemos asegurarnos que la clave K solo sea conocida por Alice y por Bob. Porque si dicha clave K llega a un tercero este podrá leer el mensaje.

Para evitar este punto entran los sistemas asimétricos:

  1. Bob tiene su par de claves Kpu y Kpr. Bob publica la clave Kpu en su web pero se guarda bajo llave la clave Kpr.
  2. Alice quiere mandarle un mensaje cifrado a Bob. Para ello lo cifra con la clave Kpu de Bob (que está en su web).
  3. Bob recibe el mensaje y lo descifra usando su propia clave Kpr (que tiene guardada bajo llave).
  4. Si Bob quiere responder a Alice puede cifrar su respuesta usando la clave Kpu de Alice (que también está en la web de Alice). Alice puede descifrar el texto usando su propia clave Kpr.

No hay intercambio de claves. Par enviar algo a Bob se cifra con su clave pública (la Kpu de Bob) y tan solo Bob lo puede descifrar con su Kpr (su clave privada). Los métodos asimétricos son más lentos (tanto para cifrar como para descifrar) que los asimétricos y tienen sus propios problemas: existen ataques sobre la Kpu para intentar averiguar la Kpr asociada, ya que es obvio que tienen alguna relación. Algunos algoritmos asimétricos han sido rotos gracias a que se han encontrado relaciones más o menos obvias entre ambas claves que permiten deducir (o acotar suficientemente el ámbito de búsqueda para permitir un ataque por fuerza bruta) la clave privada al saber la pública.

Cifrando y descifrando datos

Bueno… veamos como podemos implementar un cifrado asimétrico usando WebApi. Nos centramos solo en el cifrado de datos (no entraremos en temas de firma digital aunque los principios sean muy parecidos). El algoritmo que usaremos será RSA (pero vamos, hay otros) a través de la clase RSACryptoServerProvider.

La propia clase se encarga de crear el par de claves necesario y luego pueden usarse los métodos ToXmlString() y FromXmlString() para guardar dichas claves o bien incorporar dichas claves en un RSACryptoServerProvider:

  1. static void Main(string[] args)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     var both = rsa.ToXmlString(true);
  5.     var pub = rsa.ToXmlString(false);
  6.     File.WriteAllText("both.xml", both);
  7.     File.WriteAllText("pub.xml", pub);
  8. }

Dicho código crea dos archivos xml (both.xml y pub.xml). El primero contiene el par de claves (pública y privada) mientras que el segundo contiene tan solo la clase pública. Por supuesto hay mejores maneras de guardar las claves pero para este post eso será suficiente.

Ahora vamos a crear una aplicación ASP.NET WebApi.

He generado un par de claves y las he guardado en una clase (he copiado el contenido entero de both.xml en una propiedad CryptoKeys.Both y lo mismo para CryptoKeys.Pub):

  1. class CryptoKeys
  2. {
  3.     internal const string Both = "[CONTENIDO ENTERO DEL FICHERO BOTH.XML]";
  4.     internal const string Pub = "[CONTENIDO ENTERO DEL FICHERO PUB.XML]";
  5. }

Vale… en un mundo real esto NO estaría hardcodeado pero… ¡estamos en el mundo ideal de los blogs!

Ahora creamos un controlador WebApi para que nos devuelva la clave pública:

  1. public class KeyController : ApiController
  2. {
  3.     // GET api/values
  4.     public string Get()
  5.     {
  6.         return CryptoKeys.Pub;
  7.     }
  8. }

Una llamada GET a /api/Key nos permite obtener la clave pública del servidor:

SNAGHTML5d500782

Vale… vamos a ver el código del cliente.

  1. static void Main(string[] args)
  2. {
  3.     DoEverythingAsync().Wait();
  4. }
  5.  
  6. private static async Task DoEverythingAsync()
  7. {
  8.     string kpu = null;
  9.     // Obtenemos clave del servidor
  10.     using (var client = new HttpClient())
  11.     {
  12.         client.DefaultRequestHeaders.Accept.ParseAdd("application/json");
  13.         var data = await client.GetAsync("http://localhost:25986/api/key");
  14.         kpu = await data.Content.ReadAsStringAsync();
  15.         kpu = JsonConvert.DeserializeObject<string>(kpu);
  16.     }
  17.  
  18.     var encrypted = EncryptData(kpu, "PRIVATE DATA TO SEND");
  19.     await SendEncryptedDataAsync(encrypted);
  20. }

Básicamente obtenemos la clave pública (llamando a /api/Key del servidor), encriptamos unos datos y los enviamos. El código de EncryptData es:

  1. private static byte[] EncryptData(string kpu, string data)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     rsa.FromXmlString(kpu);
  5.     var bytes = new byte[data.Length * sizeof(char)];
  6.     Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
  7.     var encrypted = rsa.Encrypt(bytes, true);
  8.     return encrypted;
  9. }

Simplemente usamos el método FromXmlString para inicializar el RSACryptoServiceProvider con la clave pública obtenida previamente. Luego llamamos a Encrypt para encriptar los datos.

Finalmente el código de SendEncryptedDataAsync:

  1. private static async Task SendEncryptedDataAsync(byte[] encrypted)
  2. {
  3.     using (var client = new HttpClient())
  4.     {
  5.         var content = new FormUrlEncodedContent(new[]
  6.         {
  7.             new KeyValuePair<string, string>("CC", Convert.ToBase64String(encrypted)),
  8.             new KeyValuePair<string, string>("Name", "edu"),
  9.         });
  10.         var result = await client.PostAsync("http://localhost:25986/api/Test", content);
  11.         Console.WriteLine("Status Code: " + result.StatusCode);
  12.     }
  13. }

Enviamos dos campos en el POST, uno llamado CC (el encriptado) y otro llamado Name que NO está encriptado.

Hecho esto, en el servidor nos creamos un controlador de prueba (Test):

  1. public class TestController : ApiController
  2. {
  3.     public void Post(CreditCardInfo card)
  4.     {
  5.         
  6.     }
  7. }
  8.  
  9. public class CreditCardInfo
  10. {
  11.     public string Name { get; set; }
  12.     public string CC { get; set; }
  13. }

Si ahora levantamos la aplicación WebApi y ejecutamos nuestra aplicación de consola cliente vemos que los datos llegan al servidor:

image

Por supuesto el servidor tiene que descifrarlos… Veamos como:

  1. public void Post(CreditCardInfo card)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     rsa.FromXmlString(CryptoKeys.Both);
  5.     var bytes = Convert.FromBase64String(card.CC);
  6.     var decrypted = rsa.Decrypt(bytes, true);
  7.     var chars = new char[decrypted.Length / sizeof(char)];
  8.     Buffer.BlockCopy(decrypted, 0, chars, 0, decrypted.Length);
  9.     var decryptedString = new string(chars);
  10. }

¡Al final en la variable decryptedString estarán los datos descifrados!

Un poco más de integración con WebApi

Tener que colocar el código de descifrado continuamente no es muy agradecido. Por suerte podemos integrarnos dentro de WebApi de forma relativamente simple.

WebApi usa media type formatters para pasar los datos que vienen en el cuerpo de la petición http a un parámetro del controlador. En nuestro caso mandamos dos datos (Name y CC). Para saber que media type formatter usar WebApi debe saber en qué formato están los datos en la petición y para ello usa la cabecera content-type. El valor normal de content-type cuando enviamos datos vía POST es application/x-www-form-urlencoded (si enviasemos un JSON usaríamos application/json). WebApi viene con soporte nativo para application/x-www-form-urlencoded a través de dos media type formatters:

  • FormUrlEncodedMediaTypeFormatter: Se usa si el controlador recibe un JToken o un FormCollection
  • JQueryMvcFormUrlEncodedFormatter: Se usa si el controlador recibe un objeto como parámetro. Deriva del anterior.

En nuestro caso el controlador recibe un CreditCardInfo por lo que será JQueryMvcFormUrlEncodedFormatter el que procese los datos de la petición, y cree el CreditCardInfo que recibe el controlador en el método POST.

Así la solución pasa por derivar de JQueryMvcFormUrlEncodedFormatter y añadir en la clase derivada el código de descifrado. Veamos como hacerlo de forma sencilla. Primero creamos la clase CypheredFomUrlEncodedMediaTypeFormatter:

  1. public class CypheredFormUrlEncodedMediaTypeFormatter : JQueryMvcFormUrlEncodedFormatter
  2. {
  3.     public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content,
  4.         IFormatterLogger formatterLogger)
  5.     {
  6.         return this.ReadFromStreamAsyncCore(type, readStream, content, formatterLogger);
  7.     }
  8. }

Sobreescribimos el método ReadFromStreamAsync. Este método debe leer los datos del cuerpo de la petición, crear el objeto del tipo type indicado y pasar los datos de la petición al objeto creado.

Todo el código lo tendremos en el método ReadFromStreamAsyncCore de la propia clase:

  1. public async Task<object> ReadFromStreamAsyncCore(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
  2.     {
  3.         var obj =
  4.             await base.ReadFromStreamAsync(typeof(FormDataCollection), readStream, content, formatterLogger) as
  5.                 FormDataCollection;
  6.         var binded = obj.ReadAs(type);
  7.         foreach (var property in type.GetProperties().Where(p => p.PropertyType == typeof(string)))
  8.         {
  9.             var attr = property.GetCustomAttribute<CypheredDataAttribute>();
  10.             if (attr != null)
  11.             {
  12.                 property.SetValue(binded, Decrypt(property.GetValue(binded) as string));
  13.             }
  14.         }
  15.  
  16.         return binded;
  17.     }

El método ReadFromStreamAsyncCore hace lo siguiente:

  1. Usamos el método (de la clase base) ReadFromStreamAsync pasándole como parámetro un FormDataCollection. Con eso obtenemos un FormDataCollection (básicamente un diccionario clave, valor) con los datos del cuerpo de la petición. Eso nos funciona porque derivamos de FormUrlEncodedMediaTypeFormatter que tiene soporte para FormDataCollection.
  2. Usamos el método (de extensión) ReadAs de FormDataCollection que crea un objeto del tipo indicado a partir de los datos de un FormDataCollection. Es decir, hace el binding de propiedades.
  3. Ahora iteramos sobre todas las propiedades de tipo cadena y miramos si alguna tiene el atributo CypheredDataAttribute aplicado.
    1. Si lo tiene desciframos el valor de dicha propiedad mediante una llamada a Decrypt.

El código del método Decrypt es básicamente el mismo que teníamos antes en el controlador:

  1. private string Decrypt(string encryptedBase64)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     rsa.FromXmlString(CryptoKeys.Both);
  5.     var bytes = Convert.FromBase64String(encryptedBase64);
  6.     var decrypted = rsa.Decrypt(bytes, true);
  7.     var chars = new char[decrypted.Length / sizeof(char)];
  8.     Buffer.BlockCopy(decrypted, 0, chars, 0, decrypted.Length);
  9.     var decryptedString = new string(chars);
  10.     return decryptedString;
  11. }

Con esto ya tenemos un media type formatter que descifrará automáticamente todas aquellas propiedades string decoradas con [CypheredData]. El código de CypheredDataAttribute es trivial:

  1. [AttributeUsage(AttributeTargets.Property)]
  2. public class CypheredDataAttribute : Attribute
  3. {
  4. }

Ahora nuestra clase CreditCardInfo nos queda de la siguiente manera:

  1. public class CreditCardInfo
  2. {
  3.     public string Name { get; set; }
  4.     [CypheredData]
  5.     public string CC { get; set; }
  6. }

¡Un último detalle! Tenemos que agregar nuestro MediaTypeFormatter en la configuración de webapi:

  1. config.Formatters.Remove(
  2.     config.Formatters.Single(f => typeof (JQueryMvcFormUrlEncodedFormatter) == f.GetType()));
  3. config.Formatters.Add(new CypheredFormUrlEncodedMediaTypeFormatter());

Y lo mejor: ¡en el controlador no tenemos que hacer nada para tener los datos descifrados!

image

¡Listos! Hemos visto como podemos enviar datos cifrados y como podemos integrarnos un poco dentro de webapi para que el descifrado sea sencillo. No hemos cubierto todos los casos posibles, pero espero que os haya dado algunas ideas de como implementarlo!

Saludos!

ASP.NET vNext–Configuración

Otra de las cosas que cambia, radicalmente, en ASP.NET vNext es el tema de la configuración. Hasta ahora teníamos, generalmente, mezcladas en un mismo archivo (web.config) tanto la configuración propia de nuestra aplicación (app settings, connection strings y módulos de configuración propios) como la del framework (p. ej. la configuración de forms authentication o de los HttpModules).

En vNext eso cambia radicalmente. Para empezar desaparece web.config. La configuración de los distintos módulos del framework se realiza por código, en el método Configure de la clase Startup. La configuración de nuestra aplicación se mantiene en ficheros de configuración pero desaparecen las nociones de appsettings o cadenas de conexión. Ýa no hay una API más o menos “tipada” como la del ConfigurationManager (donde tenemos propiedades como ConnectionStrings o AppSettings). La configuración de vNext es básicamente un diccionario de claves, valores. Así pues los ficheros de configuración que usemos ya no tienen una estructura predefinida. Desaparece toda noción de esquema en los ficheros de configuración. De hecho se soportan varios formatos: XML, JSON y INI (si, puede ser sorprendente el soporte para INI pero debemos tener presente la aspiración “multiplataforma” de vNext y ficheros INI son muy usados en entornos Unix).

Vamos a ver un ejemplo sencillo del uso del sistema de configuración nuevo de vNext. Para ello partiremos de una aplicación vNext vacía (ASP.NET vNext Empty Web Application).

Una diferencia importante respecto a ASP.NET clásico es que la configuración ahora es un objeto. Ya no hay clases estáticas (como ConfigurationManager). Así que el primer punto es crear dicho objeto en el método Configure de la clase Startup:

  1. public void Configure(IBuilder app)
  2. {
  3.     var config = new Configuration();    
  4. }

El siguiente paso será agregar un archivo json (p. ej. data.json) con el siguiente formato:

  1. {
  2.     "environments": {
  3.         "dev": {
  4.             "background": "blue"
  5.         },
  6.           "pre":{
  7.               "background":"yellow"
  8.         },
  9.           "prod":{
  10.               "background":"red"
  11.         }
  12.     }
  13. }

El “esquema” de los datos JSON es totalmente inventado.

Bien. La clase Configuration tiene un método, llamado Add, al cual debe pasársele un IConfigurationSource. No tiene soporte para cargar distintos tipos de archivo, pero dicho soporte se obtiene a través de métodos de extensión. Uno de ellos es AddJsonConfig que está definido en el paquete Microsoft.Framework.ConfigurationModel.Json, así que debemos agregar una referencia a dicho paquete en project.json (en la sección “dependencies”):

  1. "Microsoft.Framework.ConfigurationModel.Json": "0.1-alpha-build-0233"

Ahora ya podemos usar el método “AddJsonFile”:

  1. config.AddJsonFile("data.json");

Con eso cargamos los valores de dicho fichero de configuración dentro del objeto Configuration. El objeto Configuration es un diccionario plano, pero nuestro JSON tiene profundidad (p. ej. el objeto environments tiene tres propiedades (dev, pre, prod), cada una de las cuales tiene otra propiedad llamada background. Para convertir esa estructura jerárquica a una plana, se añaden las claves usando el carácter dos puntos (:) como separador. Así, después de procesar este JSON el objeto Configuration tendrá tres claves:

  1. environments:dev:background (con el valor blue)
  2. environments:pre:background (con el valor yellow)
  3. environments:prod:background (con el valor red)

Se usa el método Get para acceder a un valor de la configuración. El método Get acepta un solo parámetro: la clave y devuelve el valor (otra cadena). Al ejecutar el siguiente código value valdría “yellow”:

  1. var value = config.Get("environments:pre:background");

Recordad: ¡no hay una sección predeterminada para guardar valores de cadenas de conexión ni appsettings! Nosotros decidimos donde guardar cada cosa que necesitemos.

Vamos a ver ahora como hacer uso de dicha configuración desde un módulo de vNext. P. ej. agreguemos ASP.NET MVC a nuestro proyecto, agregando la referencia a Microsoft.AspNet.Mvc a project.json:

  1. "Microsoft.AspNet.Mvc" :"0.1-alpha-build-1268"

Ahora añadamos el código al método Configure de la clase Startup para inicializar correctamente ASP.NET MVC. Además añadimos el objeto config dentro del sistema de inyección de dependencias de vNext:

  1. public void Configure(IBuilder app)
  2. {
  3.     var config = new Configuration();
  4.     config.AddJsonFile("data.json");
  5.     app.UseServices(
  6.         s =>
  7.         {
  8.             s.AddInstance<IConfiguration>(config);
  9.             s.AddMvc();
  10.         });
  11.     app.UseMvc();
  12. }

Ahora agrega un controlador Home y haz que tenga en su constructor un parámetro de tipo IConfiguration:

  1. public class HomeController : Controller
  2. {
  3.     private readonly string _bg;
  4.  
  5.     public HomeController(IConfiguration cfg)
  6.     {
  7.         _bg = cfg.Get("environments:dev:background");
  8.     }
  9.  
  10.     public IActionResult Index()
  11.     {
  12.         ViewBag.bg = _bg;
  13.         return View();
  14.     }
  15. }

Dado que hemos agregado la configuración dentro del mecanismo de inyección de dependencias de vNext, ahora podemos inyectar dicha configuración por el constructor. Fíjate que en el controlador no nos guardamos toda la configuración si no solo lo que necestiamos. Finalmente si te creas una vista Index.cshtml de prueba (en Views/Home):

  1. <html>
  2. <head><title>Demo Config</title></head>
  3. <body style="background: @ViewBag.bg">
  4.     <h1>Demo config</h1>
  5. </body>
  6. </html>

Ahora al ejecutar la demo deberías ver la página con el fondo azul, pues este es el valor de la entrada environments:dev:background de la configuración:

image

Y eso viene a ser todo… ¡Espero que os haya resultado interesante!

ASP.NET MVC vNext – Controladores “POCO”

Una de las novedades que presenta ASP.NET MVC6 (integrada dentro de vNext) es la posibilidad de que los controladores ya no deban heredar de ninguna clase base.

De hecho la clase Controller en MVC clásico (MVC5 y anteriores) proporcionaba básicamente dos cosas:

  1. Un conjunto de métodos de para devolver action results (p. ej. el método View() para devolver un ViewResult o el método Json para devolver un JsonResult).
  2. Acceso a algunas propiedades para contexto (ControllerContext, ModelState y ViewBag básicamente).

Los métdos para devolver action results no son estríctamente necesarios (aunque ayudan) pero pueden encapsularse en alguna clase aparte y los objetos de contexto pueden añadirse por inyección de dependencias (ASP.NET vNext está montando desde la base usando inyección de dependencias).

Así en MVC6 podemos crear un controlador como el siguiente:

  1. public class HomeController
  2. {
  3.     // GET: /<controller>/
  4.     public IActionResult Index()
  5.     {
  6.         return null;
  7.     }
  8. }

Si (asumiendo la tabla de rutas tradicional) navegamos hacia /Home/Index veremos como se nos invoca dicho método. Por supuesto ahora hemos de ver como crear el action result necesario. P. ej. supongamos que queremos devolver la vista (lo que sería un return View() en un controlador tradicional). Vemos que el constructor del ViewResult nos pide dos parámetros:

image

Como he dicho antes ASP.NET vNext está montado basado en inyección de dependencias así que… deja que el propio framework te inyecte estos parámetros:

  1. public class HomeController
  2. {
  3.     private readonly IServiceProvider _serviceProvider;
  4.     private readonly IViewEngine _viewEngine;
  5.     public HomeController(IServiceProvider serviceProvider, IViewEngine viewEngine)
  6.     {
  7.         _serviceProvider = serviceProvider;
  8.         _viewEngine = viewEngine;
  9.     }
  10.     public IActionResult Index()
  11.     {
  12.         return new ViewResult(_serviceProvider, _viewEngine);
  13.     }
  14. }

Si ahora ejecutas y colocas un breakpoint en el constructor verás que ambos parámetros han sido inicializados por el framework de ASP.NET vNext:

image

Verás como efectivamente esto devuelve la vista Index.cshtml localizada en Views/Home (exactamente lo mismo que hace return View()).

Pasar un modelo a la vista tampoco es excesivamente complicado:

  1. public class HomeController
  2. {
  3.     private readonly IServiceProvider _serviceProvider;
  4.     private readonly IViewEngine _viewEngine;
  5.     private readonly IModelMetadataProvider _modelMetadataProvider;
  6.     public HomeController(IServiceProvider serviceProvider, IViewEngine viewEngine, IModelMetadataProvider modelMetadataProvider)
  7.     {
  8.         _serviceProvider = serviceProvider;
  9.         _viewEngine = viewEngine;
  10.         _modelMetadataProvider = modelMetadataProvider;
  11.     }
  12.     public IActionResult Index()
  13.     {
  14.         var viewdata = new ViewDataDictionary<FooModel>(_modelMetadataProvider);
  15.         viewdata.Model = new FooModel();
  16.         return new ViewResult(_serviceProvider, _viewEngine) { ViewData = viewdata };
  17.     }

Necesitamos un IModelMetadataProvider (que recibimos también por inyección de dependencias) ya que lo necesitamos para la construcción del ViewDataDictionary que pasamos a la vista.

Para evitar que nuestro controlador POCO deba tomar demasiadas depenencias en el constructor (dependencias que son requeridas básicamente para construir los action results), el equipo de ASP.NET ha creado la interfaz IActionResultHelper. Dicha interfaz contiene métodos para ayudarnos a crear más fácilmente los action results. Por supuesto, en el controlador recibimos un IActionResultHelper por inyección de dependencias. Así podemos modificar nuestro controlador para que quede de la siguiente forma:

  1. public class HomeController
  2. {
  3.     private readonly IActionResultHelper _actionHelper;
  4.     private readonly IModelMetadataProvider _modelMetadataProvider;
  5.     public HomeController(IActionResultHelper actionHelper, IModelMetadataProvider modelMetadataProvider)
  6.     {
  7.         _actionHelper = actionHelper;
  8.         _modelMetadataProvider = modelMetadataProvider;
  9.     }
  10.     public IActionResult Index()
  11.     {
  12.         var viewdata = new ViewDataDictionary<FooModel>(_modelMetadataProvider);
  13.         viewdata.Model = new FooModel();
  14.         return _actionHelper.View("Index", viewdata);
  15.     }
  16. }

Ahora el controlador solo toma una dependencia contra el IActionResultHelper y el IModelMetadataProvider. Las dependencias contra el IServiceProvider y el IViewEngine (que eran solo para crear el ViewResult) son gestionadas ahora por el IActionResultHelper.

¡Y listos! Hemos visto como podemos crear controladores POCO (que no hereden de Controller) y como a través de la inyección de dependencias ¡recibimos las dependencias necesarias de forma automática!

¡Saludos!

Explorando ASP.NET vNext: Routing

Lentamente se van desgranando las novedades que incorporará ASP.NET vNext. Recordad que podéis ya jugar un poco con él, descargandoos la CTP de VS14 🙂

Vamos a ver en este post cuatro cosillas sobre el routing que incorpora ASP.NET vNext. Vamos a partir de una aplicación web vNext vacía y agregaremos tan solo lo justo para tener MVC y una tabla de rutas. Lo primero será editar el project.json y añadir la referencia a asp.net MVC:

  1. "dependencies": {
  2.     "Helios" : "0.1-alpha-build-0585",
  3.     "Microsoft.AspNet.Mvc": "0.1-alpha-build-1268"
  4. },

Una vez hecho esto ya podemos editar el método Configure de la clase Startup para agregar MVC y configurar una tabla de rutas:

  1.     app.UseServices(s => s.AddMvc());
  2.     app.UseMvc(r =>
  3.     {
  4.         r.MapRoute(
  5.             name: "default",
  6.             template: "{controller}/{action}/{id?}",
  7.             defaults: new { controller = "Home", action = "Index" });
  8.     });
  9. }

Con esto tenemos la “clásica” tabla de rutas por defecto. Una diferencia respecto a MVC clásico (MVC5 y anteriores) es el uso de id? para indicar un parámetro opcional (en lugar de colocar id = UrlParameter.Optional en el defaults). Esta nomenclatura es la misma que usábamos en el routing attribute de webapi 2 y es mucho más concisa.

Si añadimos un controlador HomeController para que nos devuelva una vista (Index.cshtml) veremos que todo funciona correctamente… Hasta ahí nada nuevo bajo el sol.

Pero, recordad: WebApi y MVC están ahora unificados y a nivel de routing funcionaban de forma relativamente distinta. Pero… ¿como funcionará el routing de vNext? Hagamos un experimento:

  1. public IActionResult Index()
  2.  {
  3.      return View();
  4.  }
  5.  
  6.  public IActionResult Index(int id)
  7.  {
  8.      return View();
  9.  }

En MVC clásico este código no funciona (MVC se queja de una ambigüedad) ya que dos métodos de un controlador no pueden implementar la misma acción (para el mismo verbo HTTP). Pero en WebApi esto funciona. Pues en vNext también.

Si invocamos a la acción sin pasar el parámetro id (p. ej. con / o con /Home/Index) se ejecutará el primer método. Si invocamos la acción pasando el parámetro id (p. ej. con /Home/Index/2) se ejecutará el segundo método.

Prosigamos nuestro experimento… Modifiquemos el método Startup.Configure para tener la siguiente tabla de rutas:

  1. app.UseMvc(r =>
  2. {
  3.     r.MapRoute(
  4.         name: "default",
  5.         template: "{controller}/{action}/{id?}",
  6.         defaults: new { controller = "Home", action = "Index" });
  7.  
  8.     r.MapRoute(
  9.         name: "second",
  10.         template: "Details/{id?}",
  11.         defaults: new { controller = "Product", action="Display"});
  12. });

Creamos el controlador ProductController con una acción Dispay:

  1. public IActionResult Display(int? id)
  2. {
  3.     if (id == null) ViewBag.Pid = "None";
  4.     else ViewBag.Pid = id;
  5.     return View();
  6. }

Si ahora vamos a /Details/100.. ¿qué es lo que obtenemos? En ASP.NET MVC clásico obtendremos un 404 porque las rutas se evalúan en orden y la primera que es posible aplicar se aplica. Así la url /Details/100 encaja dentro de la primera ruta con:

  • controller = Details
  • action = 100
  • id = sin valor

Y como no existe el DetailsController de ahí el 404 que obtendríamos con MVC clásico. De ahí que la tabla de rutas tenga que configurarse con las rutas en orden desde la más específica a la más general.

Pero en vNext eso ya no es así. Las rutas siguen evaluándose en orden pero si una ruta devuelve un 404 el control pasa a la siguiente ruta. Así, en este caso se intenta evaluar la primera ruta con los parámetros indicados anteriormente (al igual que en MVC clásico). Dicha ruta devuelve un 404 así que el control pasa a la siguiente ruta. La URL /Details/10 encaja con el patrón de la siguiente ruta así que se intenta aplicar dicha ruta con los parámetros:

  • controller = Product (sacado de defaults)
  • action = Display (scado de defaults)
  • id = 100

Ahora como el controlador y la acción existen se invocan y vemos la página de detalles del producto 100.

Hagamos una tercera modificación a la tabla de rutas para que quede así:

  1. app.UseMvc(r =>
  2. {
  3.     r.MapRoute(
  4.         name: "default",
  5.         template: "{controller}/{action}/{id?}",
  6.         defaults: new { controller = "Home", action = "Index" });
  7.  
  8.     r.MapRoute(
  9.         name: "second",
  10.         template: "Details/{id?}",
  11.         defaults: new { controller = "Product"});
  12. });

Es la misma tabla de rutas que teníamos con una excepción importante: la ruta “second” no define acción.

Si ahora navegamos de nuevo a /Details/100 obtenemos un 404. Era de esperar, puesto que no hay acción alguna… Pero el routing de vNext nos tiene reservada una última sorpresa: si no hay acción definida se enrutará por el verbo HTTP.

Para hacer la prueba modifica el controlador Product:

  1. public IActionResult Get(int? id)
  2. {
  3.     if (id == null) ViewBag.Pid = "None";
  4.     else ViewBag.Pid = id;
  5.     return View("Display");
  6. }

Si ahora navegas a /Details/100 deberías ver, de nuevo, los detalles del producto con id 100. Al no haber acción vNext ha enrutado por el verbo HTTP y nuestro controlador se ha enrutado como si fuese un controlador de webapi.

Bueno… hemos visto brevemente algunas de las diferencias entre el routing de vNext y el de MVC tradicional. Para más detalles os recomiendo este post del equipo de ASP.NET.

Saludos!