martes, 19 de abril de 2016
Al representar gráficamente el pipeline de ASP.NET Core, lo habitual es hacerlo como una tubería única en las que colocamos estratégicamente los middlewares que procesarán las peticiones que entrarán por ella.
Incluso a nivel de código, la inserción de middlewares se realiza normalmente invocando métodos del tipo
UseXXX()
sobre la instancia de IApplicationBuilder
que recibimos en el método Configure()
de la clase Startup
de nuestras aplicaciones ASP.NET Core.Esto parece dar a entender que el pipeline es lineal, y que todas las peticiones serán procesadas de la misma forma y por los mismos middlewares, salvo que alguno de ellos decida cortocircuitar la petición.
Sin embargo, aunque eso será lo más frecuente, no es obligatoriamente así. Ya desde los tiempos de Katana tenemos la posibilidad de dividir el pipeline en branches o ramas en función de determinados criterios, permitiéndonos crear configuraciones mucho más complejas y potentes que las que habitualmente suelen mostrarse.
Como ejemplo, en el diagrama lateral podemos observar un pipeline en cuya entrada se encuentra un middleware de gestión de errores, y que a continuación se divide en función de la ruta de la petición de entrada. Así, las peticiones cuyas rutas comiencen por "/SignalR" serán procesadas por los middlewares configurados en la rama de la izquierda; las que comiencen por "/API" serán procesadas a lo largo de la rama derecha, y el resto continuará su proceso por la rama central.
Vamos a ver cómo conseguir crear estas estructuras, pero, como siempre, tened en cuenta que estamos aún trabajando con una versión preliminar de ASP.NET Core y hay detalles que podrían cambiar en la versión definitiva.
IAppBuilder.Map(): crear branches en función de la ruta
Map()
es un extensor sobre IAppBuilder
que permite crear una rama del pipeline a partir de una ruta determinada, en cuyo interior podremos configurar a su vez un nuevo IAppBuilder
que definirá cómo van a procesarse las peticiones que circulen por su interior. Hmmm, dudo que haya quedado muy claro, así que mejor lo vemos con código:public void Configure(IApplicationBuilder app) { ... app.Map("/API", (IApplicationBuilder branch) => { branch.UseCors(…); branch.UseLogging(…); branch.UseNancy(…); }); ... }Como podemos observar, la llamada a
Map()
acepta un primer parámetro en el que indicamos que se trata de un mapeo que afectará a todas las peticiones que comiencen por "/API". Después, en el segundo parámetro, implementamos un delegado en el que configuramos esta rama del pipeline mediante las llamadas habituales a UseXXX()
, pero esta vez sobre el IAppBuilder
que nos envían como parámetro que representa a la rama recién creada. Una vez dentro del delegado, todo el código de configuración que encontraremos es idéntico al que solemos encontrar en el método
Configure()
, pero sólo afectará a la nueva rama porque las llamadas las hacemos sobre el IApplicationBuilder
de la rama.Internamente, lo que hace la llamada a
Map()
es añadir al pipeline una instancia de MapMiddleware
, un middleware que contiene un nuevo pipeline completo, que es el que configuramos desde el delegado anterior. Ya en ejecución, cuando llega una petición, el middleware comprueba si la ruta de entrada comienza por la que hayamos indicado y, en caso afirmativo, pasa el control a este pipeline en lugar de continuar la ejecución por la rama inicial. ¿Y no puedo usar otros criterios en lugar de sólo la ruta? Pues sí, pero en lugar de
Map()
tendremos que usar MapWhen()
.IAppBuilder.MapWhen(): crear branches en función de cualquier condición
El extensorMapWhen()
es básicamente igual que Map()
, pero en lugar de indicar la ruta a partir de la cual se crea el nuevo pipeline, se puede especificar cualquier tipo de condición en forma de predicado. De hecho, podríamos considerar Map()
como una particularización de MapWhen()
porque podríamos escribirlo con relativa facilidad usando este último:app.MapWhen( ctx => ctx.Request.Path.StartsWithSegments("/API"), branch => { // Add middlewares for this branch here, for example: // branch.UseCors(); } );El parámetro
ctx
que recibe el predicado es de tipo HttpContext
, lo que nos da acceso a prácticamente cualquier dato de la petición o su contexto. El siguiente código muestra cómo usarlo para ramificar en función de la existencia de una cookie en la petición:app.MapWhen(ctx => ctx.Request.Cookies.ContainsKey("auth"), branch => { // Add middlewares to this branch here, for example: // branch.UseIdentity(); } );Por último, sólo destacar que en caso de usar
MapWhen()
, debéis tener en cuenta que las condiciones del predicado se evaluarán en cada petición, por lo que implementar condiciones complejas o que requieran operaciones de entrada-salida podría penalizar seriamente el rendimiento de nuestras aplicaciones.Publicado en Variable not found.
4 Comentarios:
Hola!!!
No te había escrito nunca por aquí. Supongo que te acuerdas de mi de cuando estuviste en mi empresa de Madrid hace algún tiempo. Como es habitual un articulo muy interesante, muchas gracias por compartirlo.
Jose, me he quedado con una curiosidad. Igual que hemos visto como ramificar el pipeline seria posible volver a unir las distintas ramas que hemos creado en una sola? No creo que sea muy frecuente o necesario, es mas que nada por curiosidad.
Por ejemplo, imagina que tengo la rama principal, configuro en ella un par de middlewares y después le añado una rama con Map o Mapwhen, pero quiero que cuando esa rama acabe vuelva a unirse con el pipeline principal como si fuera volver a empalmar las dos tuberias. No se si me he explicado bien.
He estado haciendo unas pruebas y no he encontrado cómo podriamos hacerlo usando Map/Mapwhen, o quizás exista una alternativa, como lo ves?
Un abrazo, a ver si nos vemos pronto otra vez.
Hola, Fernando! Claro que te recuerdo, ¿qué tal va la cosa por ahí? A ver si un día charlamos un rato y nos ponemos al día :)
Sobre tu pregunta, así a bote pronto no veo una solución inmediata... probablemente habría que crear un middleware parecido a MapMiddleware, pero que fuera capaz de realizar ese "empalme" al final con la rama principal. Pero me has picado, jejeje, déjame darle una vuelta y te digo algo.
Un abrazo! Y saluda a tus compañeros de armas de mi parte!!
Ahí lo llevas ;)
http://www.variablenotfound.com/2016/04/confluencia-de-ramas-en-el-pipeline-de.html
Un saludo!
¡Muchas gracias, fiera! Duda más que solucionada.
Hablamos,
Enviar un nuevo comentario