Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 19 de abril de 2016
ASP.NET Core pipeline
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.

Ramas en el pipeline de ASP.NET CoreSin 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 extensor MapWhen() 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:

Fernando Valverde dijo...

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.

José María Aguilar dijo...

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!!

José María Aguilar dijo...

Ahí lo llevas ;)

http://www.variablenotfound.com/2016/04/confluencia-de-ramas-en-el-pipeline-de.html

Un saludo!

Fernando Valverde dijo...

¡Muchas gracias, fiera! Duda más que solucionada.

Hablamos,