Inicio de una aplicación ASP.NET Core
Introducción
ASP.NET Core es un framework para desarrollar aplicaciones web multiplatarforma, de código abierto y se encuentra disponible en GitHub. El hecho de que sea multiplataforma significa que nuestra aplicación no puede estar acoplada a un servidor concreto. Aparte de que nuestra aplicación sea multiplataforma tendremos otros beneficios adicionales:- No necesitamos instalar un Framework .NET para que se ejecute. Todos las bibliotecas necesarias forman parte de nuestra entrega.
- Puede usarse Visual Studio Code para desarrollar las aplicaciones el cual es libre, ligero y está disponible para todas las platarformas.
- Es muy ligero en comparación con ASP.NET. Las comparaciones entre ambos frameworks son bastante significativas, tanto en términos de memoria consumida, como de espacio en disco y lo más importante, las peticiones que es capaz de servir por segundo.
Desacople servidor-aplicación
En un primer paso, ASP.NET introdujo una evolución implementando la especificación OWIN con su proyecto Katana. De esta forma podemos crear una aplicación web sin usar la biblioteca System.Web y así eliminar la dependencia de IIS de nuestra aplicación. Inicialmente bautizado como ASP.NET vNext, ASP.NET Core da continuidad a esta separación servidor-aplicación convirtiéndose en el sucesor de Katana, pero aún va mucho más allá, nos proporciona un nuevo CLR, cambia el sistema de proyectos, integra un contenedor para la inyección de dependencias, nos facilita la personalización de la canalización (pipeline) mediante middleware y un largo etcétera. Estos aspectos introducen diferencias significativas en el ciclo de vida de las aplicaciones, empezando por el punto de entrada.Punto de entrada
En una aplicación ASP.NET el punto de entrada es el archivo Global.asax. Aquí es donde se controlan tareas como la configuración de rutas, el registro de filtros y el registro de las áreas. La primera diferencia que nos encontramos al crear nuestra primera aplicación ASP.NET Core es que se trata de una aplicación de consola. Una aplicación con una claseProgram
y con su propio punto de entrada mediante un método Main
. WebHost
Lo primero que debemos hacer en una aplicación ASP.NET Core es configurar e iniciar un objetoWebHost
que hará tanto de aplicación como de Host HTTP. Nuestra implementación de WebHost
escuchará peticiones HTTP y las introducirá en nuestra aplicación en forma de un objeto HttpContext
con toda la información de la petición. La inicialización de este Host la haremos en el punto de entrada: método Main
de la clase Program
. ASP.NET Core incluye dos implementaciones de servidor: Kestrel y HTTP.sys. La primera de ellas es un servidor HTTP multiplataforma y la segunda es sólo para Windows. Kestrel es el servidor que está incluido en las plantillas para proyectos ASP.NET Core. De la versión 1.0 a la versión 2.0 de ASP.NET Core se ha simplificado la configuración del servidor Kestrel introduciendo el método CreateDefaultBuilder
que encapsula la configuración básica de este servidor. Invocando este método se configura un Host Kestrel con un proxy inverso IIS de frontal. Si queremos utilizar otro tipo de Host o precisamos de otras características personalizadas deberemos configurarlo de forma manual. En el ejemplo vemos como se llama al método BuildWebHost
para solicitar una implementación de WebHost
. Una vez obtenida esta implementación se inciará el Host llamando al método Run
. Antes, el método BuildWebHost
habrá llamado al método CreateDefaultBuilder
que devuelve un objeto builder IWebHostBuilder
, el cual se utiliza como ayudante para configurar el Host haciendo uso de una interfaz fluida donde podemos encadenar métodos de configuración sobre nuestro objeto builder. Usamos esta interfaz fluida para decirle a nuestro Host que clase queremos que utilice como Startup. La clase Startup configura la canalización (pipeline) y las dependencias de nuestra aplicación. Para ello llamamos al método de extensión UseStartup<T>
donde T
es el tipo que queremos usar como Startup. Este tipo no es necesario que implemente ninguna interfaz ni que herede de ningún otro tipo. El WebHost
se encargará de instanciarla e invocar sus métodos de configuración al inicio de la aplicación. Para finalizar llamamos al método Build
de nuestro builder para obtener el objeto WebHost
que se solicitó desde el método Main
. Es aquí donde se inicia el Host mediante el método Run
. El objeto WebHostBuilder, mediante reflexión, se encarga de convertir nuestra clase Startup a una implementación de IStartup usando una convención de nombres en lugar de forzar a que se implemente una interfaz o se herede de una superclase. Este enfoque ha sido arrastrado de su predecesor OWIN/Katana. No obstante es posible implementar esta interfaz e incluso heredar de la clase abstracta StartupBase. Puede ser interesante usar la interfaz en lugar de la convención con el fin de "moquear" nuestro Startup para diseñar nuestros tests.
Startup
Aquí es donde realmente empieza la lógica de nuestra aplicación. Hasta ahora nos habíamos limitado a configurar y poner en marcha nuestro Host. Esta lógica será iniciada precisamente por nuestro Host antes de comenzar a despachar peticiones HTTP. Primero instanciará un objeto Startup y posteriormente llamará a sus métodos de configuración por este orden:ConfigServices
y Config
, siendo el primero de ellos opcional. Una vez llamados ambos métodos se entiende que la aplicación está lista para empezar a servir. Instanciación de Startup
El Host registra algunos servicios en el contenedor de dependencias que pueden ser utilizados mediante inyección en el constructor de nuestro Startup. Basta con añadir estos tipos como parámetros a nuestro constructor y el Host se encargará de resolver estas instancias y de inyectarlas. Uno de estos servicios esIHostingEnvironment
, que proporciona información sobre el entorno de hospedaje web de la aplicación. El método CreateDefaultBuilder
que vimos en un apartado anterior, además de configurar un Host Kestrel básico, también registra en el contenedor una instancia IConfiguration
con la configuración establecida en los archivos appSettings.json o appsettings.{Environment}.json. Si no usamos el método CreateDefaultBuilder
deberemos registrar en el contendor nuestra propia instancia de configuración de forma manual en el Startup: Además, también podemos añadir nuestras propias dependencias cuando configuramos el Host en la clase Program
, de esta manera pueden ser inyectadas en el constructor de la clase Startup
: En el ejemplo hemos añadido una dependencia de tipo IMyClass
al contenedor para que el WebHost
sea capaz de resolverla y proporcionarnos una instancia en el constructor: Método ConfigureServices
Este método nos permite registrar servicios para que estén disponibles en el métodoConfigure
y en el resto de la aplicación. Para ello contamos con el parámetro IServiceCollection
. Este parámetro es un contenedor de dependencias en el que podremos registrar dependencias de forma tradicional usando los métodos simples AddScoped
, AddTransient
y AddSingleton
, dependiendo del ciclo de vida deseado, o también, mediante métodos de extensión del tipo Add[servicio]
si tenemos algún registro más complejo que requiera de un método aparte. Aunque muchas veces los métodos de extensión del tipo IServiceCollection
devuelven el mismo objeto IServiceCollection
para permitir encadenar metodos del tipo Add[Servicio]
, a veces también pueden devolver objetos builder que permiten encadenar configuraciones más complejas usando una interfaz fluida. Otras veces estos métodos aceptan parámetros de opciones del tipo Action<MyServiceOptions>
donde el objeto MyServiceOptions
expone propiedades para poder ser utilizadas en expresiones lambda enriqueciendo la interfaz fluida disponible: Método Configure
Middleware
Los componentes que forman la canalización (pipeline) por la que viaja cada petición HTTP se denominan middleware. Cada uno de estos componentes recibe la solicitud del anterior y es responsable de continuar el flujo hacia el siguiente componente o de cortocircuitar el flujo terminando la canalización e iniciando el camino de vuelta. Tiene la posibilidad de ejecutar su propia lógica tanto antes de invocar al siguiente componente como después cuando regrese de vuelta: En el métodoConfigure
especificaremos el middleware que queramos usar en nuestra aplicación web. Para ello se utilizará una interfaz fluida sobre el objeto IApplicationBuilder
muy similar a la utilizada en el punto anterior, solo que en este caso los métodos se denominan Use[Middleware]
. El orden en el que se añaden los componentes es muy importante puesto que será el mismo orden en el que se invocarán en cada petición. También existe una convención de nombres Run[Middleware]
para indicar que un componente es el último por lo que se inicia el camino de vuelta inmediatamente después. Existe una última convención no muy utilizida Map[Middleware]
que sirve para trabajar con distintas ramas en la canalización. Además del propio objeto IApplicationBuilder
podemos especificar como parámetro del método cualquier servicio que hayamos añadido previamente al contenedor de dependencias.
Written on May 13, 2018