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 ;)

18 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, 5 de noviembre de 2013
Usando la KatanaHasta ahora, por simplificar, siempre hemos utilizado Use() para introducir módulos en el pipeline, aunque también vimos que existen middlewares que aportan sus propios extensores sobre IAppBuilder para que resulte más sencilla su utilización.

Hoy vamos a ver Map()y Run() otros extensores que, al igual que Use(), nos permiten insertar módulos en el pipeline, aunque en este caso están orientados a cubrir escenarios más específicos.


Pero antes, para lo que acabáis de llegar, este es un resumen de lo que llevamos de serie:

1. Selección por URL, un patrón habitual

Observad un middleware que creamos en la anterior entrega de la serie:
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
    }
}
Ese “if” que vemos ahí arriba es un patrón muy habitual, pues muy frecuentemente nos encontraremos con módulos que sólo deben realizar sus tareas para una serie de URLs predeterminadas, como ocurre en el ejemplo anterior, que procesábamos únicamente las peticiones dirigidas a la dirección “/hello”.

Otro escenario frecuente es que puedan necesitarse distintos pipelines también función de la URL de la petición. Esto puede ocurrir, por ejemplo, si en el mismo contexto OWIN conviven distintas aplicaciones, cada una implementada sobre un framework (compatible OWIN, por supuesto).

Gráficamente, el siguiente diagrama muestra un posible escenario de convivencia entre un servidor de archivos estáticos, un servicio en tiempo real de SignalR a escuchando en “/SignalR” y, por ejemplo, servicios REST expuestos con NancyFx a partir de la dirección “/API”. Aunque existe un tronco común, cada rama del pipeline incluye los middlewares que necesita para funcionar:


OWIN pipeline branches

Un escenario como este no sería sencillo implementarlo añadiendo los módulos al pipeline directamente con Use(), como hemos hecho hasta ahora. Y por eso existe Map().

2. El extensor Map()

Map() es un extensor de IAppBuilder aportado por Katana para facilitar la creación de “branches” o ramas en el pipeline OWIN a partir de una porción de URL.

Su uso es conceptualmente muy sencillo. Basta con indicar la porción de URL para la cual vamos a crear el pipeline, y un delegado o lambda en la que recibiremos el IAppBuilder específico para esa rama y sobre la cual debemos añadir los módulos que deseamos sean ejecutados.

Así, el diagrama anterior podríamos implementarlo con el siguiente código (ojo, los nombres de módulos middleware no son reales, es sólo para que pilléis la idea):
public void Configuration(IAppBuilder app)
{
    // Main pipeline
    app.Use<ErrorHandlerMiddleware>();

    // New branch
    app.Map("/Signalr", signalrBuilder =>
                    {
                        // SignalR pipeline
                        signalrBuilder.Use<CorsMiddleware>();
                        signalrBuilder.Use<SignalRMiddleware>();
                    });

    // New branch
    app.Map("/API", apiBuilder =>
                    {
                        // API pipeline
                        apiBuilder.Use<CorsMiddleware>();
                        apiBuilder.Use<LoggingMiddleware>();
                        apiBuilder.Use<NancyFxMiddleware>();
                    });

    // Main pipeline again
    app.Use<CompressionMiddleware>();
    app.Use<StaticFilesMiddleware>();
}
Internamente, el extensor Map() crea un nuevo pipeline (un objeto IAppBuilder) para la rama y se lo envía a la lambda o delegado que hayamos especificado para que lo configure. Tras ello, añade un módulo de tipo MapMiddleware al pipeline principal (o aquél donde lo hayamos definido), que es el que se encargará en tiempo de ejecución de analizar la URL de la petición y enviarla al primer middleware de la rama asociada a dicha dirección.

También podemos llamar a Map() desde dentro de una rama, lo que crearía una subdivisión en ésta para procesar las peticiones dirigidas a direcciones específicas:
app.Map("/API", apiBuilder =>
        {
            apiBuilder.Map("/blob", 
                apiBlobBuilder =>
                {
                    apiBlobBuilder.Use<CompressionMiddleware>();
                    apiBlobBuilder.Use<BlobManagementMiddleware>(); 
                });
            apiBuilder.Use<CorsMiddleware>();
            apiBuilder.Use<LoggingMiddleware>();
            apiBuilder.Use<NancyFxMiddleware>();
        });
A la hora de crear ramas, hay que tener en cuenta que:
  • Las URL siempre deben comenzar con una barra “/”, pero son relativas a la dirección de inicio de la rama en la que nos encontramos. En el ejemplo anterior, se accedería al pipeline para tratar blobs a través de la dirección "/API/blob".
  • Una vez la petición entra en una rama, ésta debe procesarla por completo, pues no se ejecutarán los módulos definidos en otras ramas, ni siquiera el tronco desde el que han partido.
Ah, y una última nota: existe un extensor adicional llamado MapWhen(), que funciona exactamente igual que Map(), salvo en que no utiliza la URL como criterio para determinar si la petición debe pasar a la rama o no. En este caso, la decisión  se tomará evaluando un predicado que le habremos suministrado en la llamada:
app.MapWhen(ctx => ctx.Request.User.Identity.IsAuthenticated,
    authenticatedUserBuilder =>
    {
        // Pipeline for authenticated users
        authenticatedUserBuilder.Use<Middleware1>();
        authenticatedUserBuilder.Use<Middleware2>();
        ...

    });

3. Usar middlewares finales: Run()

Run() es otro extensor que permite añadir middlewares al pipeline, aunque en este caso es bastante más limitado, por lo que vamos a despacharlo pronto  ;-)

Se trata de nuevo de un extensor sobre IAppBuilder, que sirve para añadir un middleware final en el pipeline, es decir, un módulo que procesará de forma completa las peticiones sin dar oportunidad a los siguientes módulos a ejecutarse. Su uso, igual de sencillo que el resto; debemos suministrar como argumento una lambda que recibe el contexto de la petición (IOwinContext) y retorna la tarea que se encargará de procesarla:
public void Configuration(IAppBuilder app)
{
    app.Run(ctx=> ctx.Response.WriteAsync("Hello, world!" ));
}
En esta aplicación, todas las peticiones serán procesadas de la misma forma, retornando el clásico “hola, mundo” al cliente.

Bueno, y lo dejamos aquí de momento. Aunque creo que ya hemos visto lo principal para poder enfrentarnos a nuestros enemigos Katana en mano, aún nos faltan algunas cosillas por tratar en la serie para poder aspirar al cinturón negro. Seguiremos con ello en próximos posts.

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!