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!

TIP: DebuggerDisplay

Muy buenas! Acabo de leer el post de Luis Ruiz Pavon sobre sobre el sobreescribir .ToString() para mejorar la información del modo de depuración de VS.

Y ya puestos, para complementar su post, quería comentar un truco que no se si conoce mucha gente y que permite algo parecido sin necesidad de sobreescribir ToString (que se usa para otras cosas además de para mostrar la información en la ventana de depuración) y que es el uso del atributo DebuggerDisplay.

En este caso un ejemplo vale más que mil palabras:

  1.    [DebuggerDisplay ("Product {Name} ({Code}): {Description}")]
  2.     public class Product
  3.     {
  4.         public string Code { get; set; }
  5.         public string Name { get; set; }
  6.         public string Description { get; set; }
  7.         public Product(string code, string name, string description)
  8.         {
  9.             Code = code;
  10.             Name = name;
  11.             Description = description;
  12.         }
  13.         public override string ToString()
  14.         {
  15.             return String.Format("Code – {0}", Code);
  16.         }
  17.     }

Y el resultado en la ventana de watches es:

image

Podemos conseguir cosas más interesantes utilizando las propiedades Name y TargetTypeName que modifican los valores de las columnas “Name” y “Type”. Así si uso el siguiente [DebuggerDisplay]:

  1. [DebuggerDisplay ("({Code}): {Description}", Name = "Product {Name}", Type ="Producto")]

Veo lo siguiente en la ventana de watches:

image

Y en la ventana de Locals veo, ahora, lo siguiente:

image

El uso de DebuggerDisplay tiene sus limitaciones si se compara con redefinir ToString() (este último es un método y puede contener código como el que mostraba Luis en su post para convertir a JSON), mientras que DebuggerDisplay tan solo puede generar una pequeña cadena a partir de las propiedades.

Si una clase tiene tanto ToString() redefinido como DebuggerDispay este último tiene precedencia para el depurador.

¡Y… eso es todo! 🙂

Saludos!

Sirviendo ASP.NET vNext con Nowin

Nota: Este post está realizado sobre la CTP de VS2014 y la alfa de ASP.NET vNext. Todo lo dicho puede cambiar cuando salgan las versiones RTM…

¿Conoces Nowin? Es un servidor web OWIN, es decir si construimos nuestra aplicación web basándonos en middleware OWIN (p. ej. WebApi 2 o NancyFx) podemos usar este servidor web para servirla a los clientes. Ya he hablado de OWIN varias veces antes, ya que OWIN ha sido la penúltima revolución en desarrollo de aplicaciones web en tecnología Microsot (la última es vNext, claro). Microsoft hizo un esfuerzo implementando middleware OWIN y permitiendo su integración con ASP.NET (el proyecto Katana), pero ahora sale ASP.NET vNext y es lógico preguntarse… ¿en qué queda todo?

Básicamente la pregunta es si ASP.NET vNext es compatible con OWIN y si lo es, en que terminos: ¿podemos añadir componentes OWIN al pipeline de vNext y podemos usar componentes vNext como si fuesen componentes OWIN?

La respuesta rápida a las tres preguntas es sí. ASP.NET vNext es compatible con OWIN y podemos tanto añadir componentes OWIN a nuestro pipeline de ASP.NET vNext como usar un componente vNext como si fuese middleware OWIN. De hecho cualquier otra respuesta sería totalmente incomprensible.

La idea de modularización de la aplicación en componentes que tiene ASP.NET vNext está sacada directamente de OWIN (en Katana se usa IAppBuilder lo que generó un interesantísimo debate en la comunidad).

Bueno, vayamos al tajo… intentemos ver como podemos servir una aplicación ASP.NET vNext (MVC6) pero usando un servidor OWIN como Nowin para ello. Lo primero es crear una “ASP.NET vNext Console Application” usando VS2014 CTP… Eso nos permitirá ver como hospedar ASP.NET vNext en una aplicación de consola 😉

El siguiente paso será instalar MVC6 en nuestra aplicacion. No usaremos NuGet para ello, en su lugar editaremos el fichero project.json que trae VS2104 y añadiremos las siguientes líneas en la sección dependencies:

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

Por suerte VS2014 nos ofrece intellisense al editar project.json (aunque parecen no salir todas las referencias de paquetes NuGet):

image

Ahora añade un controlador de MVC a tu aplicación (HomeController) con una acción Index que simplemente retorne la vista asociada:

  1. using Microsoft.AspNet.Mvc;
  2.  
  3. namespace NowinDemo.Controllers
  4. {
  5.     public class HomeController : Controller
  6.     {
  7.         public IActionResult Index()
  8.         {
  9.             return View();
  10.         }
  11.     }
  12. }

Una vez hecho esto añade (en la carpeta /Views/Home) la vista Index.chstml:

  1. <html>
  2. <head><title>Nowin Test</title></head>
  3. <body>
  4.     <p>Running on Nowin… @DateTime.Now.ToShortDateString()</p>
  5. </body>
  6. </html>

Ya tenemos el controlador y la vista de MVC. Tan solo nos falta “configurar” nuestra aplicación para que use ASP.NET MVC. Para ello nos creamos una clase llamada Startup:

  1. public class Startup
  2. {
  3.     public void Configure(IBuilder app)
  4.     {
  5.  
  6.         app.UseServices(services =>
  7.         {
  8.             services.AddMvc();
  9.         });
  10.  
  11.         app.UseMvc();
  12.  
  13.     }
  14. }

El siguiente paso es añadir las referencias necesarias para hospedar nuestra aplicación web y a Nowin. Para ello editamos el archivo project.json para añadir en la sección “dependencies”:

  1. "Nowin" :  "0.11.0",
  2. "Microsoft.AspNet.Owin": "0.1-alpha-build-*",
  3. "Microsoft.AspNet.Hosting": "0.1-alpha-build-*"

(Fíjate que al guardar el fichero project.json y al compilar te saldrán ya las referencias en VS2014)

Ya tenemos Nowin agregado, ahora toca configurarlo todo. Lo primero es cargar nuestra clase Startup. Eso es necesario porque es una aplicación de consola. En este caso debemos añadir el siguiente código a Program.cs:

  1. public class Program
  2. {
  3.     private readonly IServiceProvider _hostServiceProvider;
  4.  
  5.     public Program(IServiceProvider hostServiceProvider)
  6.     {
  7.         _hostServiceProvider = hostServiceProvider;
  8.     }
  9.  
  10.     public Task<int> Main(string[] args)
  11.     {
  12.         return Task.FromResult(0);
  13.     }
  14. }

Sí: es un código un poco raro para ser un programa de consola, verdad? Por un lado Program tiene un constructor que recibe un IServiceProvider y por otro el método Main no es estático si no que devuelve una Task<int>. Y esto? Esto es porque la aplicación de consola se pone en marcha a través de KRE (el runtime de ASP.NET vNext).

Bien, ahora vamos a colocar código en Main para inicializar nuestr aplicación ASP.NET vNext:

  1. var serviceCollection = new ServiceCollection();
  2. serviceCollection.Add(HostingServices.GetDefaultServices());
  3. var services = serviceCollection.BuildServiceProvider(_hostServiceProvider);

Bien, ahora que ya lo tenemos todo configurado, nos queda poner en marcha el host. Antes que nada, un pequeño recordatorio, sobre como establecíamos el servidor web usando OWIN y Katana en una aplicación de consola. En Katana usábamos el paquete Microsoft.Owin.Hosting, que define una clase llamada WebApp<T> y usábamos un código parecido a:

var options = new StartOptions
{
    ServerFactory = "Nowin",
    Port = 8080
};
using (WebApp.Start<Startup>(options))
{
    Console.WriteLine("Running a http server on port 8080");
    Console.ReadKey();
}

En este contexto la clase Startup (parámetro genérico de WebApp) era la clase de configuración de la aplicación (lo equivalente a nuestra clase Startup). Lo interesante es la la StartOptions que permitía establecer el ensamblado que contiene el servidor web (en este caso Nowin).

En vNext la idea es muy similar (cambian clases y propiedades) pero es la misma idea:

  1. var context = new HostingContext()
  2. {
  3.     Services = services,
  4.     ServerName= "Nowin",
  5.     ApplicationName = "NowinDemo",
  6. };
  7. var engine = services.GetService<IHostingEngine>();
  8. using (engine.Start(context))
  9. {
  10.     Console.WriteLine("Server waiting… ");
  11.     Console.ReadLine();
  12. }

Nota: NowinDemo es el nombre de mi ensamblado (el que contiene la clase Startup) y Nowin es obviamente el nombre del ensamblado de Nowin.

Directamente establezco que el servidor está en el ensamblado Nowin. No obstante esto no funciona:

image

La razón es que, efectivamente ASP.NET vNext intenta ir a este ensamblado para obtener un servidor, pero ahora espera un servidor vNext y no uno OWIN como Nowin. En concreto espera que haya una clase que implemente IServerFactory (interfaz de vNext).

Bueno, por suerte, no es excesivamente complejo crear dicha clase. Así pues añadimos una clase que implemente IServerFactory:

  1.     public class NowinServerFactory : IServerFactory
  2.     {
  3.         public IServerInformation Initialize(IConfiguration configuration)
  4.         {
  5.         }       
  6.         public IDisposable Start(IServerInformation serverInformation, Func<object, Task> application)
  7.         {
  8.   
  9.         }
  10.     }

El método Initialize se llama una vez al principio de la aplicación y debemos devolver una clase que implemente IServerBuilder. Dicha interfaz tan solo define una propiedad llamada Name. Vamos a crearnos una clase que nos implemente dicha interfaz:

  1. class NowinServerInformation : IServerInformation
  2. {
  3.     public string Name { get { return "Nowin"; } }
  4. }

En el método Initialize debemos instanciar el servidor Nowin y devolver un objeto IServerInformation:

  1. public IServerInformation Initialize(IConfiguration configuration)
  2. {
  3.     var builder = ServerBuilder.New()
  4.         .SetAddress(IPAddress.Any)
  5.         .SetPort(8080)
  6.         .SetOwinApp(HandleRequest);
  7.     return new NowinServerInformation();
  8. }

Nos falta el método HandleRequest (un Func<IDictionary<string, object>, Task> que espera SetOwinApp:

  1. private Task HandleRequest(IDictionary<string, object> env)
  2. {
  3.     return _callback(new OwinFeatureCollection(env));
  4. }

La variable _callback es un Func<object, Task>. Lo interesante es el uso de la clase OwinFeatureCollection (definida en Microsoft.AspNet.Owin) que ofrece un “envoltorio” tipado al diccionario de entorno de Owin.

Ahora en el método Start del objeto IServerFactory debemos devolver el componente que es el servidor. Este método recibe el IServerInformation y la definición de aplicación (es decir el conjunto de componentes del pipeline vNext) como una Func<object, Task>.  El problema ahora lo tenemos en el hecho de que debemos obtener el servidor Nowin (INowinServer) a partir del objeto IServerInformation… pero allí no lo hemos guardado. Así pues debemos redefinir dicha clase, para que nos guarde la información necesaria para ello. La idea es que la clase que implementa IServerFactory debe ser, como su nombre indica, la factoría para permitir crear el servidor a cada petición. Así pues el código final queda como sigue:

  1. public class NowinServerFactory : IServerFactory
  2. {
  3.     private Func<object, Task> _callback;
  4.  
  5.     private Task HandleRequest(IDictionary<string, object> env)
  6.     {
  7.         return _callback(new OwinFeatureCollection(env));
  8.     }
  9.  
  10.     public IServerInformation Initialize(IConfiguration configuration)
  11.     {
  12.         var builder = ServerBuilder.New()
  13.             .SetAddress(IPAddress.Any)
  14.             .SetPort(8080)
  15.             .SetOwinApp(HandleRequest);
  16.         return new NowinServerInformation(builder);
  17.     }
  18.             
  19.     public IDisposable Start(IServerInformation serverInformation, Func<object, Task> application)
  20.     {
  21.         var information = (NowinServerInformation)serverInformation;
  22.         _callback = application;
  23.         INowinServer server = information.Builder.Build();
  24.         server.Start();
  25.         return server;
  26.     }
  27.  
  28.  
  29.     private class NowinServerInformation : IServerInformation
  30.     {
  31.         public NowinServerInformation(ServerBuilder builder)
  32.         {
  33.             Builder = builder;
  34.         }
  35.  
  36.         public ServerBuilder Builder { get; private set; }
  37.  
  38.         public string Name
  39.         {
  40.             get
  41.             {
  42.                 return "Nowin";
  43.             }
  44.         }
  45.     }
  46. }

Nota: El código de esta clase es mérito de David Fowler y lo tenéis en este Gist.

Ahora ya tan solo nos queda modificar el HostContext puesto que el ensamblado que contiene el IServerFactory ya no es Nowin sino nuestra propia aplicación (NowinDemo en mi caso):

  1. var context = new HostingContext()
  2. {
  3.     ServerName= "NowinDemo",
  4.     ApplicationName = "NowinDemo",
  5.     Services = services,
  6. };

¡Y con esto ya hemos terminado! Tenemos una aplicación ASP.NET vNext MVC6 servida desde línea de comandos y usando un servidor web OWIN como Nowin para servirla (en lugar del clásico Microsoft.AspNet.Server.WebListener de ASP.NET vNext).

¿Donde está el código compilado?

Si compilas el proyecto en VS2014 CTP verás que en la carpeta Bin/Debug… no hay nada! No hay un NowinDemo.exe ni nada parecido. Las aplicaciones ASP.NET vNext, incluso si son de consola, se ponen en marcha a través del runtime de ASP.NET. Para ello se usa el comando K run “path_de_la_aplicación”.

El fichero K.cmd está donde tengas instalado el KRE. Por defecto está en %HOME%.krepackagesKRE-svrc50-x86.0.1-alpha-build-0446bin

Si no lo tienes debes añadir al path este directorio, irte donde tienes el proyecto de VS2014 (en el path raíz del proyecto donde tienes el project.json) y teclear:

K run

¡Y listos! Tu aplicación de consola ASP.NET vNext ya está en marcha!

ASP.NET vNext – IBuilder

Muy buenas! Esos días he estado jugando con VS2014 CTP. Esta versión no puede instalarse side by side con cualquier versión anterior de VS, así que la he instalado en una máquina virtual en Azure… La verdad es que es el mecanismo más rápido para probarlo, puesto que ha hay una plantilla de MV en Azure que contien VS2014 CTP. Vamos, que en cinco minutos pasas de no tener nada a estar ya trasteando con el VS2014. ¡Genial!

Una de las grandes novedades de este VS2014 es el tooling para ASP.NET vNext. Si bien ya hace algunos días que se podía descargar ASP.NET vNext y juguetear con ella, no era posible usar VS para ello, teniendo que recurrir a las herramientas de líneas de comandos.

Para ver de que se compone una aplicación ASP.NET vNext he abierto el VS2014 y he creado una nueva ASP.NET vNext web Application.

La diferencia más importante respecto a una web clásica es que ya no existen ni global.asax ni web.config. Ahora tanto los elementos de configuración que se incluían dentro de web.config como la inicialización de la aplicación que se realizaba en global.asax se unifican en un mismo archivo (que por defecto se llama Startup.cs).

Dicho archivo contiene una clase (llamada Startup) que es invocada automáticamente por el runtime de ASP.NET vNext para realizar la inicialización de la aplicación. Nos encontramos ante un nuevo ejemplo de convention over configuration. No es necesario configurar nada: crea una clase llamada Startup y esa pasará a ser la clase que inicialice toda tu aplicación. Eso no es nuevo de vNext, ya en Katana ocurría algo muy similar.

Dicha clase Startup debe contener un método llamado Configure que espera un parámetro de tipo IBuilder. Dicha interfaz está definida de la siguiente manera:

    public interface IBuilder
    {
        IServiceProvider ApplicationServices { get; set; }
        IServerInformation Server { get; set; }

        RequestDelegate Build();
        IBuilder New();
        IBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
    }

El método importante es el método Use que permite "enchufar" un componente al pipeline de tu aplicación. Ten presente que ASP.NET vNext es totalmente modular de forma que el primer paso al configurar una aplicación es enchufar todos los componentes. Así ahora, ASP.NET MVC6 es un componente, al igual que lo es WebApi 3 y al igual que lo es un sistema para autenticarnos con cookies. Si conoces Katana es exactamente la misma idea.

El método Use es muy genérico pero raras veces lo usarás directamente: lo normal es quc cada componente proporcione un método de extensión sobre IBuilder para enchufar dicho componente. Así para enchufar ASP.NET MVC usarás el método UseMvc. Pero al final todos esos métodos de extensión terminan llamando al genérico Use.

Creación de un componente (middleware) ASP.NET vNext

Vamos a ver como crear un componente (también se les llama middleware) ASP.NET vNext. Para ello agrega una clase cualquiera a tu proyecto y añádele el siguiente código:

public class UfoMiddleware
{
    private readonly RequestDelegate _next;

    public UfoMiddleware(RequestDelegate next)
    {
        this._next = next;
    }

    public async Task Invoke(HttpContext ctx)
    {
        Debug.WriteLine("UfoMiddleware::Entrada");
        await _next(ctx);
        Debug.WriteLine("UfoMiddleware::Salida");
    }
}

Eso es todo lo que necesitas para crear un middleware (componente) de ASP.NET vNext. El parámetro next del constructor te lo mandará ASP.NET vNext (y es el componente siguiente a invocar). En el método Invoke realizas las acciones que quieras y luego llamas de forma asíncrona al middleware siguiente. Por supuesto puedes colocar código cuando el middleware siguiente ha terminado su ejecución (pero ten presente que el middleware siguiente llamará al siguiente y así sucesivamente). Así, si tienes 3 componentes (A, B y C) encadenados en este orden se ejecutará:

  1. El código de A de antes del await
  2. El código de B de antes del await
  3. El código de C de antes del await
  4. El código de C de después del await
  5. El código de B de después del await
  6. El código de A de después del await

Si te preguntas en qué orden se ejecutan los componentes, pues es en el orden el que estén registrados en IBuilder. Es decir el orden en que se llamen los métodos Use.

Para añadir nuestro componente en el pipeline de ASP.NET vNext podemos llamar al método Use de IBuilder:

 app.Use(next => new UfoMiddleware(next).Invoke);

Aunque lo más habitual sería que UfoMiddleware proporcionase un método de extensión sobre IBuilder:

public static IBuilder UseUfo(this IBuilder builder)
{
    return builder.Use(n => new UfoMiddleware(n).Invoke);
}

Y así podríamos llamar a app.UseUfo() en el Startup. La otra gran ventaja de los métodos de extensión es que permiten pasar parámetros de configuración a nuestro middleware. Así que bueno, lo normal es que cada componente venga con su método de extensión.

Ahora podemos modificar el método Invoke de nuestro UfoMiddleware para que haga "visible":

public async Task Invoke(HttpContext ctx)
{
    var path = ctx.Request.Path;
    if (ctx.Request.Path.Value == "/Help.ufo")
    {
        using (StreamWriter outfile = new StreamWriter(ctx.Response.Body))
        {
            outfile.Write("Generated by Ufo Middleware");
        }
    }
    await _next(ctx);
}

Si ahora ejecutamos de nuevo nuestra aplicación y vamos a la URL /Help.ufo (ojo, que esa url es case-sensitive) veremos que por el navegador nos sale "Generated by Ufo Middleware". Perfecto! Ya hemos hecho un middleware de ASP.NET vNext funcional (vale, no es muy útil, pero funcional es).

En siguientes posts iré desgranando distintos aspectos de ASP.NET vNext porque la verdad… ¡es apasionante!