martes, 15 de octubre de 2013
Continuando la serie sobre esta simpática pareja, en esta ocasión vamos crear nuestro primer módulo middleware personalizado. Utilizando la infraestructura creada por Katana, veremos lo fácil que resulta crear un componente de este tipo e introducirlo en el pipeline para que procese peticiones.
Pero antes, como siempre, permitidme que recuerde los otros posts de la serie a los que acabáis de llegar:
La petición es normalmente procesada por el primer módulo introducido en el pipeline; éste realizaría su trabajo y, si procede, cedería el proceso al siguiente módulo, donde volvería a ocurrir exactamente lo mismo, y así hasta llegar al último módulo del pipeline, o bien a un módulo que decida no delegar la petición al siguiente de la lista.
Y en la respuesta ocurriría lo mismo. El último middleware ejecutado del pipeline retornaría el control a quien lo inició, y éste tendría posibilidad de alterar el resultado, proceso que iría repitiéndose hasta llegar de nuevo al primer módulo del pipeline, quien finalmente tendría la última palabra en cuanto a la respuesta retornada al servidor.
En el diagrama anterior se puede observar un pipeline con cuatro módulos middleware, y cómo al entrar la petición van pasándosela de unos a otros hasta que el tercero decide procesarla por completo y dar por finalizada la secuencia. El cuarto módulo ni siquiera se enterará de que entró una petición.
A raíz de ahí, se puede intuir que un middleware necesita dos cosas para ejecutarse:
Una de las sobrecargas del extensor
Por tanto, podríamos crear e insertar nuestro primer módulo en el pipeline de la siguiente forma:
El parámetro
El parámetro que hemos llamado
Y en ejecución quedaría algo así:
Si insertamos este pequeño módulo al principio del pipeline en el epígrafe anterior, podremos tener resultados como este:
El siguiente módulo introduce en encabezado en las respuestas cuando la petición lo atraviesa:
Bueno, pues creo que con lo visto hasta el momento ya tenemos una idea general de qué es y cómo funciona Katana y Owin, al menos a alto nivel. En siguiente artículo continuaremos avanzando en nuestro camino hacia el cinturón negro, y hablaremos de otras variantes que podemos utilizar para añadir módulos al pipeline: los extensores
Publicado en Variable not found.
Pero antes, como siempre, permitidme que recuerde los otros posts de la serie a los que acabáis de llegar:
- OWIN (I): Introducción fue un primer acercamiento a la conceptual especificación OWIN, sus objetivos y componentes.
- En OWIN (II)- Katana, cinturón blanco vimos qué era Katana, sus principales piezas, y su funcionamiento a alto nivel.
- Seguidamente, en OWIN y Katana (III)- Primeros combates creamos ya nuestras primeras aplicaciones basadas en Katana, una en entorno web y otra usando self-hosting sobre una aplicación de consola.
- Por último, OWIN y Katana (IV)- Startup y Configuration muestra el proceso de arranque de las aplicaciones OWIN, y dónde podemos implementar su código de inicialización.
1. Middlewares: vamos a llevarnos bien
Como sabemos, cuando una petición llega a una aplicación OWIN, es procesada por los middlewares introducidos en el pipeline durante la inicialización mediante lo que podríamos denominar una cadena colaborativa de ejecución o proceso de peticiones.La petición es normalmente procesada por el primer módulo introducido en el pipeline; éste realizaría su trabajo y, si procede, cedería el proceso al siguiente módulo, donde volvería a ocurrir exactamente lo mismo, y así hasta llegar al último módulo del pipeline, o bien a un módulo que decida no delegar la petición al siguiente de la lista.
Y en la respuesta ocurriría lo mismo. El último middleware ejecutado del pipeline retornaría el control a quien lo inició, y éste tendría posibilidad de alterar el resultado, proceso que iría repitiéndose hasta llegar de nuevo al primer módulo del pipeline, quien finalmente tendría la última palabra en cuanto a la respuesta retornada al servidor.
En el diagrama anterior se puede observar un pipeline con cuatro módulos middleware, y cómo al entrar la petición van pasándosela de unos a otros hasta que el tercero decide procesarla por completo y dar por finalizada la secuencia. El cuarto módulo ni siquiera se enterará de que entró una petición.
A raíz de ahí, se puede intuir que un middleware necesita dos cosas para ejecutarse:
- Primero, información sobre la petición, que determinará la forma en que el módulo la procesará, así como un canal para escribir la correspondiente respuesta, por si lo necesita. Tened en cuenta que no todos los módulos necesitan escribir respuesta para el cliente: imaginad un módulo de logging.
- Segundo, información sobre el siguiente módulo, para que pueda invocarlo cuando interese y continuar así la cadena de ejecución.
2. ¡Mira mamá, me he hecho un middleware!
Aunque sólo sea para lograr el orgullo de una madre, vamos a construir e insertar en el pipeline nuestro primer módulo middleware. Y como no hay que ser más ambicioso de la cuenta en estas primeras fases, crearemos uno sencillito cuyo objetivo es simplemente saludar al usuario cuando se realice una petición a una URL determinada; por ejemplo, si accedemos a /hello/name=john, mostrará como resultado “Hello, john”.Una de las sobrecargas del extensor
IAppBuilder.Use()
permite indicar un delegado o lambda en el que se puede implementar directamente el middleware. Como parámetros, recibirá el contexto de la petición (IOwinContext
), y un delegado mediante el cual podemos ejecutar el siguiente módulo en el pipeline; y de retorno, siempre un objeto Task
, para permitir el proceso asíncrono de la petición.Por tanto, podríamos crear e insertar nuestro primer módulo en el pipeline de la siguiente forma:
public class Startup { public void Configuration(IAppBuilder app) { app.Use((context, nextModule) => { if (context.Request.Path.StartsWith("/hello") && !string.IsNullOrWhiteSpace(context.Request.Query.Get("name"))) { return context.Response.WriteAsync("Hello, " + context.Request.Query.Get("name") ); } return nextModule(); }); // Other modules } }Observad que si se cumple la condición de entrada se trata de un módulo final, como el tercero del diagrama anterior: se procesará la petición y no dará oportunidad de ejecutarse a otros módulos que ocupen una posición posterior en el pipeline. En caso contrario, cederá el turno al siguiente middleware.
El parámetro
context
que declaramos en la lambda es un objeto que implementa IOwinContext
, que es simplemente un wrapper sobre el diccionario de entorno del que ya hemos hablado muchas veces. Sus propiedades y métodos proporcionan acceso al contenido de dicho diccionario de forma tipada y mucho más cómoda que haciéndolo a mano. Por ejemplo, desde ahí tendremos acceso al Request
(de tipo IOwinRequest
), Response
(IOwinResponse
), o información de autenticación del usuario, entre otros.El parámetro que hemos llamado
nextModule
es un delegado que podemos utilizar para invocar de forma directa al siguiente middleware en el pipeline. Su tipo de retorno es un Task
, lo que permite utilizarlo como resultado de la función para que se ejecute de forma asíncrona.Y en ejecución quedaría algo así:
3. ¡Mira, mamá, ahora sin manos!
Como estamos ya lanzados, vamos a crear otro middleware que insertaremos en primer lugar en el pipeline, y que añadirá al resultado del resto de módulos un texto indicando el tiempo que ha tardado en procesarse la petición:app.Use(async (context, next) => { var watch = Stopwatch.StartNew(); await next(); watch.Stop(); await context.Response.WriteAsync( "-- Elapsed: " + watch.Elapsed ); }); ... // Other modulesEn este caso, utilizamos una lambda asíncrona (observad el
async
en su definición) para poder utilizar await
cuando queremos esperar a que finalice el proceso del resto de módulos del pipeline. Podríamos haberlo hecho también sin esto, pero así mola más ;-)Si insertamos este pequeño módulo al principio del pipeline en el epígrafe anterior, podremos tener resultados como este:
4. Pero mejor tener una Katana a mano…
Obviamente, esta no es la mejor forma de crear middlewares. Katana ofrece infraestructura para hacerlo más cómoda y fácilmente a través de su abstractaOwinMiddleware
, definida de la siguiente forma:public abstract class OwinMiddleware { protected OwinMiddleware Next { get; set; } protected OwinMiddleware(OwinMiddleware next) { this.Next = next; } public abstract Task Invoke(IOwinContext context); }Para crear middlewares, simplemente tenemos que heredar de
OwinMiddleware
, implementar su constructor, que recibe una referencia al siguiente módulo del pipeline en forma de objeto OwinMiddleware
, e introducir la lógica deseada en el método Invoke()
.El siguiente módulo introduce en encabezado en las respuestas cuando la petición lo atraviesa:
public class PoweredByMiddleware : OwinMiddleware { private readonly string _by; public PoweredByMiddleware(OwinMiddleware next, string by) : base(next) { _by = by; } public override Task Invoke(IOwinContext context) { context.Response.Headers.Append("Powered-by", _by); return Next.Invoke(context); } }Y en este caso, podríamos insertar el módulo al principio del middleware usando la sobrecargas genérica de
Use()
, y pasándole como argumento los valores que deseamos enviar al constructor del componente:public class Startup { public void Configuration(IAppBuilder app) { app.Use<PoweredByMiddleware>("Owin"); ... // Other modules } }En tiempo de ejecución quedaría algo así:
Bueno, pues creo que con lo visto hasta el momento ya tenemos una idea general de qué es y cómo funciona Katana y Owin, al menos a alto nivel. En siguiente artículo continuaremos avanzando en nuestro camino hacia el cinturón negro, y hablaremos de otras variantes que podemos utilizar para añadir módulos al pipeline: los extensores
Map()
y Run()
.Publicado en Variable not found.
Publicado por José M. Aguilar a las 9:00 a. m.
Etiquetas: katana, owin, tutorial, tutorial-owin-katana
2 Comentarios:
Hola Jose Manuel
Como siempre tus explicaciones son geniales.
He intentado reproducir el ejemplo que vas comentando en el código, pero nunca consigo que pase por el método Configuration de la clase Startup.
He seguido los pasos del post anterior y poniendo un punto de interrupción no consigo que pare dentro de la función Configuration.
He probado con un proyecto aplicacion web o website, pero en ninguna de las ocasiones consigo que funcione.
¿Por que puede ser?
Muchas gracias
Hola!
Pues tiene pinta de deberse a que te falta por instalar el paquete Nuget que "engancha" OWIN con tu aplicación web.
Mira este post: http://www.variablenotfound.com/2013/10/owin-y-katana-iv-startup-y-configuration.html
Un saludo!
Enviar un nuevo comentario