middleware de ASP.NET Core

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 10 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 10 de este artículo.

Middleware es un software que se integra en una canalización de aplicaciones para gestionar las solicitudes y respuestas. Cada middleware:

  • Elige si se pasa la solicitud al siguiente middleware de la canalización.
  • Puede realizar trabajos antes y después del siguiente middleware de la canalización.

Los delegados de solicitudes se usan para crear la canalización de solicitudes. Los delegados de solicitud manejan cada solicitud HTTP.

Configure los delegados de solicitud mediante los métodos de extensión Run, Map y Use. Un delegado de solicitudes se puede especificar en línea como un método anónimo (denominado middleware en línea) o se puede definir en una clase reutilizable. Estos métodos anónimos insertados o clases reutilizables se denominan middleware o componentes de middleware. Cada middleware de la canalización de solicitudes es responsable de invocar el siguiente middleware de la canalización o de cortocircuitar la canalización. Cuando un middleware se cortocircuita, se denomina middleware terminal porque impide que otros middleware procesen la solicitud.

Para obtener más información sobre la diferencia entre las canalizaciones de solicitud en ASP.NET Core y ASP.NET 4.x con ejemplos de middleware adicionales, consulte Migración de módulos HTTP a ASP.NET Core middleware.

Rol de middleware por tipo de aplicación

El lado del servidor Blazor, páginas de Razor y el proceso MVC gestionan solicitudes del navegador en el servidor con middleware. Las instrucciones de este artículo se aplican a estos tipos de aplicaciones.

Las aplicaciones de Blazor WebAssembly independientes se ejecutan completamente en el cliente y no procesan solicitudes con una canalización de middleware. Las instrucciones de este artículo no se aplican a aplicaciones de Blazor WebAssembly independientes.

Análisis de código de middleware

Para obtener más información sobre los analizadores de la plataforma del compilador de ASP.NET Core que inspeccionan el código de la aplicación para garantizar su calidad, consulte Análisis de código de diagnóstico en aplicaciones de ASP.NET Core.

Crea una canalización de middleware con WebApplication

La canalización de solicitudes de ASP.NET Core consiste en una secuencia de delegados de solicitud a los que se llama de uno en uno. En el siguiente diagrama se muestra este concepto. El subproceso de ejecución sigue las flechas negras.

En el patrón de procesamiento de solicitudes se muestra una solicitud entrante, el procesamiento a través de tres middleware y la respuesta que sale de la aplicación. Cada middleware ejecuta su lógica y pasa la solicitud al middleware siguiente con la instrucción next(). Después de que el tercer middleware procese la solicitud, esta vuelve a pasar por los dos middleware anteriores en orden inverso. Esto se hace a modo de procesamiento adicional después de las instrucciones next() y antes de salir de la aplicación como respuesta al cliente.

Cada delegado puede realizar operaciones antes y después del siguiente. Los delegados que controlan excepciones deben llamarse al principio de la canalización para que puedan capturar las excepciones que se producen en las fases siguientes de la canalización.

Nota:

Para experimentar localmente con los ejemplos de código de esta sección, cree una aplicación de ASP.NET Core mediante la plantilla de proyecto Core Empty de ASP.NET . Si usa la CLI de .NET, el nombre corto de la plantilla es web (dotnet new web).

La aplicación más sencilla de ASP.NET Core llama a Run para configurar un único middleware terminal como delegado de solicitud para una función anónima para controlar las solicitudes sin una canalización de solicitudes.

En el ejemplo siguiente:

  • La llamada a RunExtensions.Run se invoca en cada solicitud y escribe "Hello world!" en la respuesta.
  • La llamada al WebApplication.Run al final del bloque de código ejecuta la aplicación y bloquea el subproceso que realiza la llamada hasta que el host se apague.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run();

Respuesta al acceder a la aplicación en un explorador en su dirección URL de inicio:

Hello world!

Encadene varios delegados de solicitudes con Use. El parámetro next representa el siguiente delegado de la canalización. Normalmente, puede realizar acciones tanto antes como después del delegado next.

En el ejemplo siguiente se muestra:

  • Dos Use llamadas, cada una de las cuales escribe en la consola:
    • Donde se puede realizar el trabajo que puede escribir en la respuesta (context.Response, HttpResponse).
    • Donde se puede realizar el trabajo que no escribe en la respuesta después de invocar el parámetro next.
  • Delegado de solicitud de terminal con una llamada a RunExtensions.Run que escribe "Hello world!" en la respuesta.
  • Una llamada final Use , que nunca se ejecuta porque sigue al delegado de solicitud de terminal Run.
  • Una llamada al WebApplication.Run al final del bloque de código para ejecutar la aplicación y bloquear el subproceso que realiza la llamada hasta que se cierre el host.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Use(async (context, next) =>
{
    Console.WriteLine("Work that can write to the response. (1)");
    await next.Invoke(context);
    Console.WriteLine("Work that doesn't write to the response. (1)");
});

app.Use(async (context, next) =>
{
    Console.WriteLine("Work that can write to the response. (2)");
    await next.Invoke(context);
    Console.WriteLine("Work that doesn't write to the response. (2)");
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Use(async (context, next) =>
{
    Console.WriteLine("This statement isn't reached. (3)");
    await next.Invoke(context);
    Console.WriteLine("This statement isn't reached. (3)");
});

app.Run();

En la ventana de la consola de la aplicación cuando se ejecuta la aplicación:

Trabajo que puede escribir en la respuesta. (1)
Trabajo que puede escribir en la respuesta. (2)
Trabajo que no se escribe en la respuesta. (2)
Trabajo que no se escribe en la respuesta. (1)

Interrumpir el flujo de solicitudes suele ser deseable porque evita el trabajo innecesario. Por ejemplo, el middleware de archivos estáticos puede actuar como middleware terminal al procesar una solicitud de un archivo estático y omitir el resto de la canalización. Middleware agregado a la canalización antes de que el middleware de terminal siga procesando el código después de sus instrucciones next.Invoke. Si no tiene previsto llamar a next.Invoke porque el objetivo es finalizar la canalización, use un Run delegado en lugar de llamar al método de extensión Use.

No llame a next.Invoke durante o después de enviar la respuesta al cliente. Después de que se inicie un HttpResponse, los cambios resultan en una excepción. Por ejemplo, al establecer encabezados o un código de estado de respuesta, se produce una excepción después de que se inicie la respuesta. Si se escribe en el cuerpo de la respuesta después de llamar a next puede:

  • Causar una infracción de protocolo, como escribir más bytes en la respuesta que la longitud de contenido indicada de la respuesta (valor de encabezado Content-Length).
  • Dañar el formato del cuerpo del documento, como escribir un pie de página HTML en un archivo CSS.

Para determinar si se ha iniciado la respuesta, compruebe el valor de HasStarted.

Para obtener más información, vea Middleware de cortocircuito después del enrutamiento.

Delegado Run

Un Run delegado no recibe un next parámetro. El primer delegado de Run siempre finaliza la canalización. Run también es una convención, y algunos middleware pueden exponer métodos Run que se ejecutan al final del flujo de procesamiento.

No se llama a los delegados de Use o Run después del primer delegado de Run.

Creación de una rama de la canalización de middleware

Map Las extensiones se utilizan convencionalmente para bifurcar la tubería de procesamiento de solicitudes. Map crea una rama de la canalización de solicitudes según las coincidencias de la ruta de solicitud proporcionada. Si la ruta de la solicitud comienza con la ruta indicada, se ejecuta la rama.

En el ejemplo siguiente, HandleMap1 se llama para las solicitudes a /map1, y HandleMap2 se llama para las solicitudes a /map2:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/map1", HandleMap1);
app.Map("/map2", HandleMap2);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate!");
});

app.Run();

private static void HandleMap1(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map 1");
    });
}

private static void HandleMap2(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map 2");
    });
}

En la tabla siguiente se muestran las solicitudes y respuestas mediante el código anterior.

Solicitud Respuesta
/ Hello from the non-Map delegate.
/map1 Map 1
/map2 Map 2
/map3 Hello from the non-Map delegate.

Cuando se usa Map, los segmentos de ruta que coincidan se eliminan de HttpRequest.Path y se anexan a HttpRequest.PathBase para cada solicitud.

Map puede coincidir a la vez con varios segmentos.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/map1/segment1", HandleMultipleSegments);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate.");
});

app.Run();

private static void HandleMultipleSegments(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Processing '/map1/segment1'");
    });
}

En la tabla siguiente se muestran las solicitudes y respuestas mediante el código anterior.

Solicitud Respuesta
/ Hello from the non-Map delegate.
/map1/segment1 Processing '/map1/segment1'

Map admite el anidamiento:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        level2AApp.Run(async context =>
        {
            await context.Response.WriteAsync("Processing '/level1/level2a'");
        });
    });
    level1App.Map("/level2b", level2BApp => {
        level2BApp.Run(async context =>
        {
            await context.Response.WriteAsync("Processing '/level1/level2b'");
        });
    });
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate!");
});

app.Run();

En la tabla siguiente se muestran las solicitudes y respuestas mediante el código anterior.

Solicitud Respuesta
/ Hello from the non-Map delegate.
/level1/level2a Processing '/level1/level2a'
/level1/level2b Processing '/level1/level2b'

MapWhen crea una rama de la canalización de solicitudes según el resultado del predicado proporcionado. Se puede usar cualquier predicado de tipo Func<HttpContext, bool> para asignar solicitudes a nuevas ramas de la canalización. En el ejemplo siguiente, se usa un predicado para detectar la presencia de una variable de cadena de consulta denominada "branch":

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate.");
});

app.Run();

private static void HandleBranch(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        var branchVer = context.Request.Query["branch"];
        await context.Response.WriteAsync($"Branch used = '{branchVer}'");
    });
}

En la tabla siguiente se muestran las solicitudes y respuestas mediante el código anterior.

Solicitud Respuesta
/ Hello from the non-Map delegate.
/?branch=main Branch used = 'main'

UseWhen puede bifurcar la canalización de solicitudes en función del resultado del predicado especificado. A diferencia de MapWhen, la rama se vuelve a unir a la canalización principal si no contiene un middleware de terminal:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
    appBuilder => HandleBranchAndRejoin(appBuilder));

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate.");
});

app.Run();

void HandleBranchAndRejoin(IApplicationBuilder app)
{
    var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>(); 

    app.Use(async (context, next) =>
    {
        var branchVer = context.Request.Query["branch"];
        logger.LogInformation("Branch used = {branchVer}", branchVer.ToString());

        Console.WriteLine("Work that can write to the response.");
        await next.Invoke(context);
        Console.WriteLine("Work that doesn't write to the response.");
    });
}

En el ejemplo anterior, se escribe una respuesta de "Hello from the non-Map delegate." para todas las solicitudes. Si la solicitud incluye una variable de cadena de consulta denominada "branch", su valor se registra antes de que se vuelva a unir la canalización principal.

Crea una canalización de middleware con IApplicationBuilder

La canalización de solicitudes de ASP.NET Core consiste en una secuencia de delegados de solicitud a los que se llama de uno en uno. En el siguiente diagrama se muestra este concepto. El subproceso de ejecución sigue las flechas negras.

En el patrón de procesamiento de solicitudes se muestra una solicitud entrante, el procesamiento a través de tres middleware y la respuesta que sale de la aplicación. Cada middleware ejecuta su lógica y pasa la solicitud al middleware siguiente con la instrucción next(). Después de que el tercer middleware procese la solicitud, esta vuelve a pasar por los dos middleware anteriores en orden inverso. Esto se hace a modo de procesamiento adicional después de las instrucciones next() y antes de salir de la aplicación como respuesta al cliente.

Cada delegado puede realizar operaciones antes y después del siguiente. Los delegados que controlan excepciones deben llamarse al principio de la canalización para que puedan capturar las excepciones que se producen en las fases siguientes de la canalización.

La aplicación ASP.NET Core más sencilla posible configura un solo delegado de solicitudes que controla todas las solicitudes. En este caso no se incluye una canalización de solicitudes real. En su lugar, solo se llama a una única función anónima en respuesta a todas las solicitudes HTTP.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

Encadene varios delegados de solicitudes con Use. El parámetro next representa el siguiente delegado de la canalización. Si no llama al siguiente parámetro, puede cortocircuitar la canalización. Normalmente, puede realizar acciones antes y después del siguiente delegado, tal como se muestra en el ejemplo siguiente:

app.Use(async (context, next) =>
{
    // Do work that doesn't write to the Response.
    await next.Invoke();
    // Do logging or other work that doesn't write to the Response.
});

Cuando un delegado no pasa una solicitud al siguiente delegado, se denomina cortocircuitar la canalización de solicitudes. Este proceso es necesario muchas veces, ya que previene la realización de trabajo innecesario. Por ejemplo, el middleware de archivos estáticos puede actuar como middleware terminal al procesar una solicitud de un archivo estático y omitir el resto de la canalización. El middleware agregado a la canalización antes del middleware que finaliza el procesamiento sigue procesando código después de sus instrucciones next.Invoke. Sin embargo, consulte la siguiente advertencia acerca de intentar escribir en una respuesta que ya ha sido enviada.

Advertencia

No llame a next.Invoke después de haber enviado la respuesta al cliente. Si se modifica HttpResponse después de haber iniciado la respuesta, se producirá una excepción. Por ejemplo, al establecer encabezados y un código de estado se produce una excepción. Si escribe en el cuerpo de la respuesta después de llamar a next:

  • Puede provocar una infracción del protocolo. Por ejemplo, si escribe más de la longitud Content-Length establecida.
  • Puede dañar el formato del cuerpo. Por ejemplo, escribir un pie de página en HTML para un archivo CSS.

HasStarted es una sugerencia útil para indicar si se han enviado los encabezados o se han realizado escrituras en el cuerpo.

Los delegados de Run no reciben un parámetro next. El primer delegado de Run siempre es terminal y finaliza la canalización. Run es una convención. Es posible que algunos componentes de middleware expongan métodos Run[Middleware] que se ejecutan al final de la canalización:

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from 2nd delegate.");
});

En el ejemplo anterior, el delegado Run escribe "Hello from 2nd delegate." en la respuesta y, después, termina la canalización. Si se añade otro delegado Use o Run después del delegado Run, este no es llamado.

Creación de una rama de la canalización de middleware

Las extensiones Map se usan como convención para la creación de ramas en la canalización. Map crea una rama de la canalización de solicitudes según las coincidencias de la ruta de solicitud proporcionada. Si la ruta de la solicitud comienza con la ruta indicada, se ejecuta la rama.

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

En la siguiente tabla se muestran las solicitudes y las respuestas de http://localhost:1234 con el código anterior.

Solicitud Respuesta
localhost:1234 Saludos del delegado sin Map.
localhost:1234/map1 Prueba de Mapa 1
localhost:1234/map2 Prueba del Mapa 2
localhost:1234/map3 Saludos del delegado sin Map.

Cuando se usa Map, los segmentos de ruta que coincidan se eliminan de HttpRequest.Path y se anexan a HttpRequest.PathBase para cada solicitud.

Map admite la anidación, por ejemplo:

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map puede hacer coincidir varios segmentos a la vez:

public class Startup
{
    private static void HandleMultiSeg(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map multiple segments.");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1/seg1", HandleMultiSeg);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

MapWhen crea una rama de la canalización de solicitudes según el resultado del predicado proporcionado. Puede usar cualquier predicado de tipo Func<HttpContext, bool> para asignar solicitudes a una nueva rama de la canalización. En el ejemplo siguiente, un predicado detecta la presencia de una variable branchde cadena de consulta :

public class Startup
{
    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            var branchVer = context.Request.Query["branch"];
            await context.Response.WriteAsync($"Branch used = {branchVer}");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranch);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

En la siguiente tabla se muestran las solicitudes y las respuestas de http://localhost:1234 con el código anterior.

Solicitud Respuesta
localhost:1234 Saludos del delegado sin Map.
localhost:1234/?branch=main Rama usada = principal

UseWhen también crea una rama de la canalización de solicitudes según el resultado del predicado proporcionado. A diferencia de lo que sucede con MapWhen, esta rama se vuelve a unir a la canalización principal si no realiza un cortocircuito ni contiene un middleware de terminal:

public class Startup
{
    private void HandleBranchAndRejoin(IApplicationBuilder app, ILogger<Startup> logger)
    {
        app.Use(async (context, next) =>
        {
            var branchVer = context.Request.Query["branch"];
            logger.LogInformation("Branch used = {branchVer}", branchVer.ToString());

            // Do work that doesn't write to the Response.
            await next();
            // Do other work that doesn't write to the Response.
        });
    }

    public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
    {
        app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
                               appBuilder => HandleBranchAndRejoin(appBuilder, logger));

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from main pipeline.");
        });
    }
}

En el ejemplo anterior, se escribe una respuesta de "Hola desde la canalización principal" para todas las solicitudes. Si la solicitud incluye una variable de cadena de consulta branch, su valor se registra antes de que se vuelva a unir la canalización principal.

Middleware agregado automáticamente por WebApplication

WebApplicationagrega automáticamente el siguiente middleware en ASP.NET Core aplicaciones en función de determinadas condiciones:

  • UseDeveloperExceptionPage se agrega primero cuando HostingEnvironment es "Development".
  • UseRouting se agrega como segundo paso si el código de usuario todavía no ha llamado a UseRouting y si hay extremos configurados, por ejemplo app.MapGet.
  • UseEndpoints se agrega al final de la canalización de middleware si hay algún punto de conexión configurado.
  • UseAuthentication se agrega inmediatamente después de UseRouting si el código de usuario no llamó aún a UseAuthentication y si IAuthenticationSchemeProvider se puede detectar en el proveedor de servicios. IAuthenticationSchemeProvider se agrega de forma predeterminada cuando se usa AddAuthentication, y los servicios se detectan mediante IServiceProviderIsService.
  • UseAuthorization se añade a continuación si el código de usuario todavía no ha llamado a UseAuthorization y si IAuthorizationHandlerProvider puede detectarse en el proveedor de servicios. IAuthorizationHandlerProvider se agrega de forma predeterminada cuando se usa AddAuthorization, y los servicios se detectan mediante IServiceProviderIsService.
  • El middleware y los puntos de conexión configurados por el usuario se agregan entre UseRouting y UseEndpoints.

El código siguiente es eficazmente lo que produce el middleware automático que se agrega a la aplicación:

if (isDevelopment)
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();

if (isAuthenticationConfigured)
{
    app.UseAuthentication();
}

if (isAuthorizationConfigured)
{
    app.UseAuthorization();
}

// user middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// end user middleware/endpoints

app.UseEndpoints(e => {});

En algunos casos, la configuración predeterminada del middleware no es correcta para la aplicación y requiere modificación. Por ejemplo, UseCors debe llamarse antes que UseAuthentication y UseAuthorization. La aplicación debe llamar a UseAuthentication y UseAuthorization si se llama a UseCors:

app.UseCors();
app.UseAuthentication();
app.UseAuthorization();

Si se debe ejecutar el middleware antes de que se produzca la coincidencia de rutas, se debe llamar a UseRouting y se debe colocar el middleware antes de la llamada a UseRouting. UseEndpoints no es necesario en este caso, ya que se agrega automáticamente como se ha descrito anteriormente:

app.Use((context, next) =>
{
    return next(context);
});

app.UseRouting();

// other middleware and endpoints

Al agregar un middleware de terminal:

  • El middleware debe agregarse después de UseEndpoints.
  • La aplicación debe llamar a UseRouting y UseEndpoints para que el middleware del terminal se pueda colocar en la ubicación correcta.
app.UseRouting();

app.MapGet("/", () => "hello world");

app.UseEndpoints(e => {});

app.Run(context =>
{
    context.Response.StatusCode = 404;
    return Task.CompletedTask;
});

El middleware terminal es un tipo de middleware que se ejecuta si ningún endpoint maneja la solicitud.

WebApplication agrega automáticamente el siguiente middleware en ASP.NET Core aplicaciones en función de determinadas condiciones:

  • UseDeveloperExceptionPage se agrega primero cuando HostingEnvironment es "Development".

  • UseRouting se agrega en segundo lugar, si el código de usuario aún no llamó a UseRouting y los puntos de conexión están configurados, por ejemplo app.MapGet.

  • UseEndpoints se agrega al final de la canalización de middleware si los puntos de conexión están configurados.

  • UseAuthentication se agrega inmediatamente después de UseRouting, si el código de usuario aún no llama a UseAuthentication y si se puede detectar IAuthenticationSchemeProvider en el proveedor de servicios. IAuthenticationSchemeProvider se agrega de forma predeterminada cuando se usa AddAuthentication y se detectan servicios mediante IServiceProviderIsService.

  • UseAuthorization se agrega a continuación, si el código de usuario aún no ha llamado a UseAuthorization, y si IAuthorizationHandlerProvider se puede detectar en el proveedor de servicios. IAuthorizationHandlerProvider se agrega de forma predeterminada cuando se usa AddAuthorization y los servicios se detectan mediante IServiceProviderIsService.

  • El middleware y los puntos de conexión configurados por el usuario se agregan entre UseRouting y UseEndpoints.

El código siguiente es eficazmente lo que produce el middleware automático que se agrega a la aplicación:

if (isDevelopment)
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();

if (isAuthenticationConfigured)
{
    app.UseAuthentication();
}

if (isAuthorizationConfigured)
{
    app.UseAuthorization();
}

// User middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// End user middleware/endpoints

app.UseEndpoints(e => {});

En algunos casos, la configuración predeterminada del middleware no es correcta para la aplicación y requiere modificación. Por ejemplo, UseCors debe llamarse antes que UseAuthentication y UseAuthorization. La aplicación debe llamar a UseAuthentication y UseAuthorization si se llama a UseCors:

app.UseCors();
app.UseAuthentication();
app.UseAuthorization();

Si el middleware debe ejecutarse antes de que se produzca la coincidencia de rutas, se debe llamar a UseRouting y colocar el middleware antes de la llamada a UseRouting. UseEndpoints no es necesario en este caso porque se agrega automáticamente como se ha descrito anteriormente:

app.Use((context, next) =>
{
    return next(context);
});

app.UseRouting();

// Other middleware and endpoints

Al agregar un middleware de terminal:

  • El middleware debe agregarse después de UseEndpoints.

  • La aplicación debe llamar UseRouting a y UseEndpoints para que el middleware de terminal se pueda colocar en la ubicación correcta.

app.UseRouting();

app.MapGet("/", () => "hello world");

app.UseEndpoints(e => {});

app.Run(context =>
{
    context.Response.StatusCode = 404;
    return Task.CompletedTask;
});

El middleware terminal es un tipo de middleware que se ejecuta si ningún endpoint maneja la solicitud.

Para obtener información sobre el middleware antifalsificación en las APIs mínimas, consulte Prevenir ataques de falsificación de solicitudes entre sitios (XSRF/CSRF) en ASP.NET Core.

Orden del middleware

El orden en que aparece el middleware en el archivo de la aplicación Program define el orden en el que se invoca el middleware en una solicitud con el orden inverso de la respuesta.

Tiene control total sobre el orden del middleware y la capacidad de agregar middleware personalizado para escenarios de procesamiento de solicitudes, teniendo en cuenta que el orden del middleware puede ser crítico para la seguridad, el rendimiento y la funcionalidad.

En los ejemplos siguientes se muestra el orden de middleware para escenarios comunes de aplicaciones. Cada método de extensión de middleware se expone en WebApplicationBuilder a través del espacio de nombres Microsoft.AspNetCore.Builder.

  1. Control de excepciones y errores
    • Cuando la aplicación se ejecuta en el Development entorno:
      • El middleware de la página de excepciones para desarrolladores (UseDeveloperExceptionPage) informa de los errores en tiempo de ejecución de la aplicación.
      • El middleware de la página de errores de la base de datos (UseDatabaseErrorPage) informa los errores en tiempo de ejecución de la base de datos.
    • Cuando la aplicación se ejecuta en el Production entorno:
      • El middleware de control de excepciones (UseExceptionHandler) captura las excepciones lanzadas por los siguientes middlewares.
      • El middleware (UseHsts) del protocolo de seguridad de transporte estricto HTTP (HSTS) agrega la cabecera Strict-Transport-Security.
  2. El middleware de redireccionamiento HTTPS (UseHttpsRedirection) redirige las solicitudes HTTP a HTTPS.
  3. El middleware de archivos estáticos (si es necesario, UseStaticFiles) devuelve archivos estáticos e interrumpe el procesamiento posterior de solicitudes.
  4. El middleware de directivas de Cookie (UseCookiePolicy) permite que la aplicación cumpla con las normas del Reglamento general de protección de datos (RGPD) de la Unión Europea.
  5. Middleware de enrutamiento (UseRouting) para enrutar las solicitudes.
  6. El middleware de autenticación (UseAuthentication) intenta autenticar al usuario antes de permitir el acceso a recursos seguros.
  7. El middleware de autorización (UseAuthorization) autoriza a un usuario a acceder a recursos seguros.
  8. El middleware antifalsificación (UseAntiforgery) agrega middleware antifalsificación a la canalización UseAntiforgery y debe colocarse después de las llamadas a UseAuthentication y UseAuthorization.
  9. El middleware de sesiones (Razor solo para páginas y MVC, UseSession) establece y mantiene el estado de sesión. Si la aplicación usa el estado de sesión, llame al middleware de sesiones después del middleware de directivas de cookie y antes del Razor middleware de páginas/MVC.
  10. Middleware de cálculo de ruta de puntos de conexión
  • MapRazorComponents para agregar Razor puntos de conexión de componentes a la canalización de solicitud.
  • MapRazorPages para agregar Razor puntos de conexión de páginas a la canalización de solicitud.
  • MapControllerRoute para agregar puntos de conexión de controlador a la canalización de solicitud.

Canalización típica de middleware Blazor Web App

app.UseWebAssemblyDebugging(); // Development environment with client-side rendering
app.UseMigrationsEndPoint(); // Development environment with ASP.NET Core Identity

app.UseExceptionHandler("/Error", createScopeForErrors: true); // Non-Development environment
app.UseHsts(); // Non-Development environment with HTTPS protocol

app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);

app.UseHttpsRedirection(); // With HTTPS protocol

app.UseAntiforgery();

app.MapStaticAssets();

app.MapRazorComponents<App>(); // With additional extension methods for render modes

app.MapAdditionalIdentityEndpoints(); // With ASP.NET Core Identity

app.Run();

Canalización típica de middleware para páginas/MVC Razor.

app.UseMigrationsEndPoint(); // Development environment with ASP.NET Core Identity

app.UseExceptionHandler("/Error"); // Non-Development environment
app.UseHsts(); // Non-Development environment with HTTPS protocol

app.UseHttpsRedirection(); // With HTTPS protocol

// app.UseCookiePolicy();
app.UseRouting(); // If not called, runs at the beginning of the pipeline by default
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();

// app.UseAuthentication(); // Called internally for ASP.NET Core Identity
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();

app.MapStaticAssets();

app.MapControllerRoute(...); // For MVC controllers

app.MapRazorPages(); // For Razor Pages pages

app.MapControllers(); // With authentication in a Razor Pages app

app.Run();

En el código anterior:

  • El middleware CORS (UseCors), el middleware de autenticación (UseAuthentication) y el middleware de autorización (UseAuthorization) deben aparecer en el orden que se muestra.
  • El middleware CORS (UseCors) debe aparecer antes del middleware de almacenamiento en caché de respuestas (UseResponseCaching) para agregar encabezados CORS en cada solicitud, incluidas las respuestas almacenadas en caché. Para obtener más información, vea No está claro que UseCORS debe venir antes de UseResponseCaching (dotnet/aspnetcore #23218.
  • El middleware de localización de solicitudes (UseRequestLocalization) debe aparecer antes de cualquier middleware que pueda comprobar la referencia cultural de la solicitud, por ejemplo, middleware de archivos estáticos (UseStaticFiles).
  • Se debe llamar al middleware de limitación de frecuencia (UseRateLimiter) después del middleware de enrutamiento (UseRouting) cuando se usan las API de limitación de frecuencia específicas de cada punto de conexión. Por ejemplo, si se usa el [EnableRateLimiting] atributo , se debe llamar al middleware de limitación de velocidad después del middleware de cálculo de ruta. Al llamar solo a los limitadores globales, se puede llamar al middleware de limitación de velocidad antes de enrutar el middleware.

En algunos escenarios, el middleware tiene un orden diferente. Por ejemplo, el almacenamiento en caché y el orden de compresión dependen de la especificación de la aplicación. En el orden siguiente, el uso de cpu podría reducirse almacenando en caché la respuesta comprimida, pero la aplicación podría terminar almacenando en caché varias representaciones de un recurso mediante algoritmos de compresión diferentes, como Gzip o Brotli:

app.UseResponseCaching();
app.UseResponseCompression();

Normalmente, los recursos estáticos se sirven al inicio del flujo de procesamiento para que la aplicación pueda omitir el procesamiento de solicitudes y así mejorar el rendimiento.

La autenticación no interrumpe las solicitudes no autenticadas. Aunque el middleware de autenticación autentica las solicitudes, la autorización se produce después de que el marco seleccione un componente Razor en una Blazor Web App, una página en una Razor aplicación de páginas o un controlador y una acción en una aplicación MVC.

En el diagrama siguiente se muestra la canalización de procesamiento de solicitudes completa para las aplicaciones de ASP.NET Core MVC y de Razor Pages. Puede ver cómo, en una aplicación típica, se ordenan los middleware existentes y dónde se agregan los middleware personalizados. Tiene control total sobre cómo reordenar los middleware existentes o insertar nuevos middleware personalizados según sea necesario para sus escenarios.

Canalización de middleware de ASP.NET Core

El middleware Punto de conexión del diagrama anterior ejecuta la canalización de filtro para el tipo de aplicación correspondiente, MVC o Razor Pages.

El diagrama anterior muestra el middleware de enrutamiento a continuación de archivos estáticos. Este orden es cómo funcionan las plantillas de proyecto llamando explícitamente a la aplicación. UseRouting. Si no llama a app.UseRouting, el middleware de Enrutamiento se ejecuta al principio de la canalización de forma predeterminada. Para más información, vea Enrutamiento.

El orden en el que se agregan componentes de middleware en el Program.cs archivo define el orden en el que se invocan los componentes de middleware en las solicitudes y el orden inverso de la respuesta. Por motivos de seguridad, rendimiento y funcionalidad, el orden es crítico.

El código destacado a continuación en Program.cs agrega componentes de middleware relacionados con la seguridad en el orden recomendado típico:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();

app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();

app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

En el código anterior:

  • El middleware comentado no se añade al crear una nueva aplicación web con cuentas de usuario individuales.
  • No todo el middleware aparece en este orden exacto, pero gran parte sí lo hace. Por ejemplo:
    • UseCors, UseAuthentication y UseAuthorization deben aparecer en el orden mostrado.
    • UseCors actualmente debe aparecer antes de UseResponseCaching. Este requisito se explica en GitHub incidencia dotnet/aspnetcore #23218.
    • UseRequestLocalization debe aparecer antes que cualquier middleware que pueda comprobar la cultura de la solicitud, por ejemplo, app.UseStaticFiles().
    • Se debe llamar a UseRateLimiter después de UseRouting cuando se usan las API específicas de limitación de velocidad del punto de conexión. Por ejemplo, si se usa el atributo [EnableRateLimiting], se debe llamar a UseRateLimiter después de UseRouting. Al llamar solo a los limitadores globales, se puede llamar a UseRateLimiter antes de UseRouting.

En algunos escenarios, el middleware tiene un orden diferente. Por ejemplo, el almacenamiento en caché y la ordenación de compresión es específico del escenario y hay varias ordenaciones válidas. Por ejemplo:

app.UseResponseCaching();
app.UseResponseCompression();

Con el código anterior, podría reducir el uso de cpu almacenando en caché la respuesta comprimida, pero podría terminar almacenando en caché varias representaciones de un recurso mediante algoritmos de compresión diferentes, como Gzip o Brotli.

La ordenación siguiente combina archivos estáticos para permitir el almacenamiento en caché de archivos estáticos comprimidos:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

El siguiente código de Program.cs agrega los componentes de middleware para escenarios de aplicaciones comunes:

  1. Control de excepciones y errores
    • Cuando la aplicación se ejecuta en el Development entorno:
      • El middleware de la página de excepciones para desarrolladores (UseDeveloperExceptionPage) informa de los errores en tiempo de ejecución de la aplicación.
      • El middleware de la página de errores de la base de datos (UseDatabaseErrorPage) informa los errores en tiempo de ejecución de la base de datos.
    • Cuando la aplicación se ejecuta en el Production entorno:
      • El middleware de control de excepciones (UseExceptionHandler) captura las excepciones lanzadas por los siguientes middlewares.
      • El middleware (UseHsts) del protocolo de seguridad de transporte estricto HTTP (HSTS) agrega la cabecera Strict-Transport-Security.
  2. El middleware de redireccionamiento HTTPS (UseHttpsRedirection) redirige las solicitudes HTTP a HTTPS.
  3. El middleware de archivos estáticos (UseStaticFiles) sirve archivos estáticos e interrumpe el procesamiento posterior de la solicitud.
  4. El middleware de directivas de Cookie (UseCookiePolicy) permite que la aplicación cumpla con las normas del Reglamento general de protección de datos (RGPD) de la UE.
  5. Middleware de enrutamiento (UseRouting) para enrutar las solicitudes.
  6. El middleware de autenticación (UseAuthentication) intenta autenticar al usuario antes de permitir el acceso a recursos seguros.
  7. El middleware de autorización (UseAuthorization) autoriza a un usuario a acceder a recursos seguros.
  8. El middleware de sesión (UseSession) establece y mantiene el estado de sesión. Si la aplicación usa el estado de sesión, llame al middleware de sesiones después del middleware de directivas de cookie y antes del middleware de MVC.
  9. Middleware de enrutamiento de punto de conexión (UseEndpoints con MapRazorPages) para agregar puntos de conexión de Razor Pages a la canalización de solicitudes.
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();

En el código de ejemplo anterior, cada método de extensión de software intermedio se expone en WebApplicationBuilder a través del espacio de nombres de Microsoft.AspNetCore.Builder.

UseExceptionHandler es el primer componente de software intermedio que se agrega a la canalización. Por lo tanto, el middleware del controlador de excepciones detecta las excepciones que se producen en llamadas posteriores.

El software intermedio de archivos estáticos se llama al principio de la canalización para que pueda controlar solicitudes y realizar cortocircuitos sin pasar por los componentes restantes. El middleware de archivos estáticos no proporciona comprobaciones de autorización. Todos los archivos servidos por middleware de archivos estáticos, incluidos los de wwwroot, están disponibles públicamente. Para obtener un enfoque para proteger archivos estáticos, consulte Servir archivos estáticos en ASP.NET Core Apps.

Si el middleware de archivos estáticos no procesa la solicitud, esta se pasa al middleware de autenticación (UseAuthentication), que realiza la autenticación. La autenticación no interrumpe las solicitudes no autenticadas. Aunque el middleware de autenticación autentica las solicitudes, la autorización (y el rechazo) solo se produce después de que MVC seleccione un controlador y una acción específicos Razor de Page o MVC.

En el ejemplo siguiente se muestra un orden de los middleware en el que las solicitudes de archivos estáticos las procesa el middleware para archivos estáticos antes que el middleware de compresión de respuestas. Los archivos estáticos no se comprimen con este middleware. Las respuestas de Razor Pages se pueden comprimir.

// Static files aren't compressed by static file middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

En el diagrama siguiente se muestra la canalización de procesamiento de solicitudes completa para las aplicaciones de ASP.NET Core MVC y de Razor Pages. Puede ver cómo, en una aplicación típica, se ordenan los middleware existentes y dónde se agregan los middleware personalizados. Tiene control total sobre cómo reordenar los middleware existentes o insertar nuevos middleware personalizados según sea necesario para sus escenarios.

Canalización de middleware de ASP.NET Core

El middleware Punto de conexión del diagrama anterior ejecuta la canalización de filtro para el tipo de aplicación correspondiente, MVC o Razor Pages.

El diagrama anterior muestra el middleware de enrutamiento a continuación de archivos estáticos. Este orden es cómo funcionan las plantillas de proyecto llamando explícitamente a la aplicación. UseRouting. Si no llama a app.UseRouting, el middleware de Enrutamiento se ejecuta al principio de la canalización de forma predeterminada. Para más información, vea Enrutamiento.

El orden en el que se agregan componentes de middleware en el Program.cs archivo define el orden en el que se invocan los componentes de middleware en las solicitudes y el orden inverso de la respuesta. Por motivos de seguridad, rendimiento y funcionalidad, el orden es crítico.

El código destacado a continuación en Program.cs agrega componentes de middleware relacionados con la seguridad en el orden recomendado típico:

using IndividualAccountsExample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();

app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();

app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();

app.MapRazorPages();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

En el código anterior:

  • El middleware comentado no se añade al crear una nueva aplicación web con cuentas de usuario individuales.
  • No todo el middleware aparece en este orden exacto, pero gran parte sí lo hace. Por ejemplo:
    • UseCors, UseAuthentication y UseAuthorization deben aparecer en el orden mostrado.
    • UseCors actualmente debe aparecer antes de UseResponseCaching. Este requisito se explica en GitHub incidencia dotnet/aspnetcore #23218.
    • UseRequestLocalization debe aparecer antes que cualquier middleware que pueda comprobar la cultura de la solicitud (por ejemplo, app.UseMvcWithDefaultRoute()).

En algunos escenarios, el middleware tiene un orden diferente. Por ejemplo, el almacenamiento en caché y la ordenación de compresión es específico del escenario y hay varias ordenaciones válidas. Por ejemplo:

app.UseResponseCaching();
app.UseResponseCompression();

Con el código anterior, podría reducir el uso de cpu almacenando en caché la respuesta comprimida, pero podría terminar almacenando en caché varias representaciones de un recurso mediante algoritmos de compresión diferentes, como Gzip o Brotli.

La ordenación siguiente combina archivos estáticos para permitir el almacenamiento en caché de archivos estáticos comprimidos:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

El siguiente código de Program.cs agrega los componentes de middleware para escenarios de aplicaciones comunes:

  1. Control de excepciones y errores
    • Cuando la aplicación se ejecuta en el Development entorno:
      • El middleware de la página de excepciones para desarrolladores (UseDeveloperExceptionPage) informa de los errores en tiempo de ejecución de la aplicación.
      • El middleware de la página de errores de la base de datos (UseDatabaseErrorPage) informa los errores en tiempo de ejecución de la base de datos.
    • Cuando la aplicación se ejecuta en el Production entorno:
      • El middleware de control de excepciones (UseExceptionHandler) captura las excepciones lanzadas por los siguientes middlewares.
      • El middleware (UseHsts) del protocolo de seguridad de transporte estricto HTTP (HSTS) agrega la cabecera Strict-Transport-Security.
  2. El middleware de redireccionamiento HTTPS (UseHttpsRedirection) redirige las solicitudes HTTP a HTTPS.
  3. El middleware de archivos estáticos (UseStaticFiles) sirve archivos estáticos e interrumpe el procesamiento posterior de la solicitud.
  4. El middleware de directivas de Cookie (UseCookiePolicy) permite que la aplicación cumpla con las normas del Reglamento general de protección de datos (RGPD) de la UE.
  5. Middleware de enrutamiento (UseRouting) para enrutar las solicitudes.
  6. El middleware de autenticación (UseAuthentication) intenta autenticar al usuario antes de permitir el acceso a recursos seguros.
  7. El middleware de autorización (UseAuthorization) autoriza a un usuario a acceder a recursos seguros.
  8. El middleware de sesión (UseSession) establece y mantiene el estado de sesión. Si la aplicación usa el estado de sesión, llame al middleware de sesiones después del middleware de directivas de cookie y antes del middleware de MVC.
  9. Middleware de enrutamiento de punto de conexión (UseEndpoints con MapRazorPages) para agregar puntos de conexión de Razor Pages a la canalización de solicitudes.
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();

En el código de ejemplo anterior, cada método de extensión de software intermedio se expone en WebApplicationBuilder a través del espacio de nombres de Microsoft.AspNetCore.Builder.

UseExceptionHandler es el primer componente de software intermedio que se agrega a la canalización. Por lo tanto, el middleware del controlador de excepciones detecta las excepciones que se producen en llamadas posteriores.

El software intermedio de archivos estáticos se llama al principio de la canalización para que pueda controlar solicitudes y realizar cortocircuitos sin pasar por los componentes restantes. El middleware de archivos estáticos no proporciona comprobaciones de autorización. Todos los archivos servidos por middleware de archivos estáticos, incluidos los de wwwroot, están disponibles públicamente. Para obtener un enfoque para proteger archivos estáticos, consulte Servir archivos estáticos en ASP.NET Core Apps.

Si el middleware de archivos estáticos no procesa la solicitud, esta se pasa al middleware de autenticación (UseAuthentication), que realiza la autenticación. La autenticación no interrumpe las solicitudes no autenticadas. Aunque el middleware de autenticación autentica las solicitudes, la autorización (y el rechazo) solo se produce después de que MVC seleccione un controlador y una acción específicos Razor de Page o MVC.

En el ejemplo siguiente se muestra un orden de los middleware en el que las solicitudes de archivos estáticos las procesa el middleware para archivos estáticos antes que el middleware de compresión de respuestas. Los archivos estáticos no se comprimen con este middleware. Las respuestas de Razor Pages se pueden comprimir.

// Static files aren't compressed by static file middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

En el diagrama siguiente se muestra la canalización de procesamiento de solicitudes completa para las aplicaciones de ASP.NET Core MVC y de Razor Pages. Puede ver cómo, en una aplicación típica, se ordenan los middleware existentes y dónde se agregan los middleware personalizados. Tiene control total sobre cómo reordenar los middleware existentes o insertar nuevos middleware personalizados según sea necesario para sus escenarios.

Canalización de middleware de ASP.NET Core

El middleware Punto de conexión del diagrama anterior ejecuta la canalización de filtro para el tipo de aplicación correspondiente, MVC o Razor Pages.

El orden en el que se agregan los componentes de software intermedio en el método Startup.Configure define el orden en el que se invocarán los componentes de software intermedio en las solicitudes y el orden inverso de la respuesta. Por motivos de seguridad, rendimiento y funcionalidad, el orden es crítico.

El método Startup.Configure siguiente agrega componentes de middleware relacionados con la seguridad en el orden recomendado típico:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    // app.UseCookiePolicy();

    app.UseRouting();
    // app.UseRequestLocalization();
    // app.UseCors();

    app.UseAuthentication();
    app.UseAuthorization();
    // app.UseSession();
    // app.UseResponseCompression();
    // app.UseResponseCaching();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

En el código anterior:

  • El middleware comentado no se añade al crear una nueva aplicación web con cuentas de usuario individuales.
  • No todo el middleware aparece en este orden exacto, pero gran parte sí lo hace. Por ejemplo:
    • UseCors, UseAuthentication y UseAuthorization deben aparecer en el orden mostrado.
    • UseCors actualmente debe ir antes de UseResponseCaching debido a este error.
    • UseRequestLocalization debe aparecer antes que cualquier middleware que pueda comprobar la cultura de la solicitud (por ejemplo, app.UseMvcWithDefaultRoute()).

En algunos escenarios, el middleware tiene un orden diferente. Por ejemplo, el almacenamiento en caché y la ordenación de compresión es específico del escenario y hay varias ordenaciones válidas. Por ejemplo:

app.UseResponseCaching();
app.UseResponseCompression();

Con el código anterior, se podría ahorrar CPU almacenando en caché la respuesta comprimida, pero es posible que termine almacenando en caché múltiples representaciones de un recurso utilizando diferentes algoritmos de compresión, como Gzip o Brotli.

La ordenación siguiente combina archivos estáticos para permitir el almacenamiento en caché de archivos estáticos comprimidos:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

El siguiente método Startup.Configure agrega los componentes de middleware para escenarios de aplicaciones comunes:

  1. Control de excepciones y errores
    • Cuando la aplicación se ejecuta en el Development entorno:
      • El middleware de la página de excepciones para desarrolladores (UseDeveloperExceptionPage) informa de los errores en tiempo de ejecución de la aplicación.
      • El middleware de la página de errores de la base de datos informa de los errores en tiempo de ejecución de la base de datos.
    • Cuando la aplicación se ejecuta en el Production entorno:
      • El middleware de control de excepciones (UseExceptionHandler) captura las excepciones lanzadas por los siguientes middlewares.
      • El middleware (UseHsts) del protocolo de seguridad de transporte estricto HTTP (HSTS) agrega la cabecera Strict-Transport-Security.
  2. El middleware de redireccionamiento HTTPS (UseHttpsRedirection) redirige las solicitudes HTTP a HTTPS.
  3. El middleware de archivos estáticos (UseStaticFiles) sirve archivos estáticos e interrumpe el procesamiento posterior de la solicitud.
  4. El middleware de directivas de Cookie (UseCookiePolicy) permite que la aplicación cumpla con las normas del Reglamento general de protección de datos (RGPD) de la UE.
  5. Middleware de enrutamiento (UseRouting) para enrutar las solicitudes.
  6. El middleware de autenticación (UseAuthentication) intenta autenticar al usuario antes de permitir el acceso a recursos seguros.
  7. El middleware de autorización (UseAuthorization) autoriza a un usuario a acceder a recursos seguros.
  8. El middleware de sesión (UseSession) establece y mantiene el estado de sesión. Si la aplicación usa el estado de sesión, llame al middleware de sesiones después del middleware de directivas de cookie y antes del middleware de MVC.
  9. Middleware de enrutamiento de punto de conexión (UseEndpoints con MapRazorPages) para agregar puntos de conexión de Razor Pages a la canalización de solicitudes.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseSession();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

En el código de ejemplo anterior, cada método de extensión de software intermedio se expone en IApplicationBuilder a través del espacio de nombres de Microsoft.AspNetCore.Builder.

UseExceptionHandler es el primer componente de software intermedio que se agrega a la canalización. Por lo tanto, el middleware del controlador de excepciones detecta las excepciones que se producen en llamadas posteriores.

El software intermedio de archivos estáticos se llama al principio de la canalización para que pueda controlar solicitudes y realizar cortocircuitos sin pasar por los componentes restantes. El middleware de archivos estáticos no proporciona comprobaciones de autorización. Todos los archivos servidos por middleware de archivos estáticos, incluidos los de wwwroot, están disponibles públicamente. Para obtener un enfoque para proteger archivos estáticos, consulte Servir archivos estáticos en ASP.NET Core Apps.

Si el middleware de archivos estáticos no procesa la solicitud, esta se pasa al middleware de autenticación (UseAuthentication), que realiza la autenticación. La autenticación no interrumpe las solicitudes no autenticadas. Aunque el middleware de autenticación autentica las solicitudes, la autorización (y el rechazo) solo se produce después de que MVC seleccione un controlador y una acción específicos Razor de Page o MVC.

En el ejemplo siguiente se muestra un orden de los middleware en el que las solicitudes de archivos estáticos las procesa el middleware para archivos estáticos antes que el middleware de compresión de respuestas. Los archivos estáticos no se comprimen con este middleware. Las respuestas de Razor Pages se pueden comprimir.

public void Configure(IApplicationBuilder app)
{
    // Static files aren't compressed by static file middleware.
    app.UseStaticFiles();

    app.UseRouting();

    app.UseResponseCompression();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

En el caso de las aplicaciones de página única (SPA), el middleware de SPA UseSpaStaticFiles normalmente se incluye en último lugar en la canalización de middleware. El middleware de SPA se incluye en último lugar:

  • Para permitir que el resto del middleware responda primero a las solicitudes coincidentes.
  • Para permitir que las SPA con enrutamiento del lado cliente se ejecuten para todas las rutas que la aplicación de servidor no reconoce.

Para obtener más información sobre las SPA, consulte las guías de las plantillas de proyecto React y Angular.

Para obtener más información sobre las aplicaciones de página única, consulte las guías de las plantillas de proyecto de React y Angular.

UseCors y UseStaticFiles en orden

Para obtener más información sobre el orden UseCors y UseStaticFiles, vea Habilitar solicitudes entre orígenes (CORS) en ASP.NET Core.

Orden del middleware de encabezados reenviados

Ejecute el middleware de encabezados reenviados antes que otros middleware para asegurarse de que el middleware que se basa en la información de los encabezados reenviados pueda consumir los valores de los encabezados para su procesamiento. Para ejecutar el middleware de encabezados reenviados después del middleware de diagnóstico y control de errores, vea Orden del middleware de encabezados reenviados.

Middleware integrado

La versión más reciente de ASP.NET Core incluye el siguiente middleware. La columna pila de IU muestra la pila de IU típica donde se usa el middleware: [Todas, Blazor Web App (BWA), Razor Páginas y MVC (RP/MVC)]. La columna Order proporciona notas sobre la ubicación del middleware en la canalización de procesamiento de solicitudes y en qué condiciones el middleware podría detener el procesamiento de solicitudes. Cuando un middleware cortocircuita la canalización de procesamiento de solicitudes e impide el procesamiento de una solicitud por parte de middleware descendente adicional, se llama middleware terminal. Para más información sobre cómo cortocircuitar, consulte la sección Creación de una canalización de middleware con la sección WebApplication.

Software intermedio Descripción Pila de interfaz de usuario Orden
Antifalsificación Proporciona soporte contra la falsificación de solicitudes. Todos Después de la autenticación y la autorización, antes de los puntos de conexión.
Autenticación Proporciona soporte de autenticación. Todos Antes de que HttpContext.User sea necesario. Terminal para devoluciones de llamadas OAuth.
Autorización Proporciona soporte de autorización. Todos Inmediatamente después del middleware de autenticación.
Cookie Política Realiza un seguimiento del consentimiento de los usuarios para almacenar información personal y aplica los estándares mínimos para los campos de las cookie, como secure y SameSite. Todos Antes del middleware que emite las cookies. Ejemplos: autenticación, sesión y MVC (TempData).
CORS Configura el uso compartido de recursos entre orígenes. Todos Antes del middleware que usa CORS. UseCors debe ir antes de UseResponseCaching. Para obtener más información, vea No está claro que UseCORS debe venir antes de UseResponseCaching (dotnet/aspnetcore #23218.
Página de excepciones para el desarrollador Genera una página con información de error que está pensada para su uso solo en el Development entorno. Todos Antes del middleware que genera errores. Las plantillas de proyecto registran automáticamente este middleware como el primer middleware de la canalización cuando el entorno es de Development.
Diagnóstico Varios middleware independientes que proporcionan una página de excepciones para el desarrollador, control de excepciones, páginas de códigos de estado y la página web predeterminada para las nuevas aplicaciones. Todos Antes del middleware que genera errores. Terminal para excepciones o con el fin de proporcionar la página web predeterminada para las nuevas aplicaciones.
Encabezados reenviados Reenvía encabezados con proxy a la solicitud actual. Todos Antes del middleware que consume los campos actualizados. Ejemplos: esquema, host, IP de cliente y método.
Comprobación de estado Comprueba el estado de una aplicación ASP.NET Core y sus dependencias, como la comprobación de disponibilidad de base de datos. Todos Terminal si una solicitud coincide con un punto de conexión de comprobación de estado.
Propagación de encabezados Permite propagar los encabezados HTTP de la solicitud entrante a las solicitudes del cliente HTTP salientes.
Todos
Registro HTTP Registra respuestas y solicitudes HTTP. Todos Al principio de la canalización del middleware.
Invalidación del método HTTP Permite que una solicitud POST entrante invalide el método. Todos Antes del middleware que consume el método actualizado.
Redireccionamiento de HTTPS Redirige todas las solicitudes HTTP a HTTPS. Todos Antes del middleware que consume la dirección URL.
Seguridad de transporte estricta de HTTP (HSTS) Middleware para mejorar la seguridad que agrega un encabezado especial en la respuesta. Todos Antes de que se envíen las respuestas y después del middleware que modifica las solicitudes. Ejemplos: encabezados reenviados y reescritura de URL.
MVC Procesa solicitudes con MVC y Razor Pages. RP/MVC Si hay una solicitud que coincida con una ruta, será final.
OWIN Puede interoperar con aplicaciones, servidores y software intermedio basados en OWIN. RP/MVC Terminal si el middleware OWIN procesa completamente la solicitud.
Almacenamiento en caché de resultados Proporciona soporte para el almacenamiento en caché de respuestas en función de la configuración. RP/MVC Antes del middleware que requiere almacenamiento en caché. UseRouting, UseCors, UseAuthenticationy UseAuthorization deben venir antes de UseOutputCache.
Cacheo de Respuestas Proporciona soporte para el almacenamiento en caché de respuestas. Este middleware requiere que la participación del cliente funcione. Use el almacenamiento en caché de resultados para un control completo del servidor. RP/MVC Antes del middleware que requiere almacenamiento en caché. UseCors debe ser anterior a UseResponseCaching. El almacenamiento en caché de respuestas no suele ser beneficioso para las aplicaciones de interfaz de usuario, como Razor Pages, ya que los exploradores suelen establecer encabezados de solicitud que impiden el almacenamiento en caché. El almacenamiento en caché de resultados beneficia a las aplicaciones de interfaz de usuario.
Descompresión de solicitudes Proporciona soporte para descomprimir solicitudes. Todos Antes del middleware que lee el cuerpo de la solicitud.
Compresión de respuesta Proporciona compatibilidad con la compresión de respuestas. Todos Antes del middleware que requiere compresión.
Localización de solicitudes Proporciona soporte de localización. Todos Antes del middleware sensible a la localización. Debe aparecer después del middleware de enrutamiento al usar RouteDataRequestCultureProvider.
Tiempos de espera de la solicitud Proporciona asistencia para configurar los tiempos de espera de la solicitud, global y por cada punto de conexión. Todos UseRequestTimeouts debe venir después de UseExceptionHandler, UseDeveloperExceptionPage y UseRouting.
Enrutamiento de puntos de conexión. Define y restringe las rutas de la solicitud. Todos Terminal para emparejamiento de rutas.
BALNEARIO Gestiona todas las solicitudes desde este punto en la cadena de procesamiento intermedio devolviendo la página predeterminada para la aplicación de una sola página (SPA). Todos Se procesa tarde en la canalización, por lo que otros middleware para servir archivos estáticos, como las acciones MVC, tienen prioridad.
Sesión Proporciona compatibilidad con la administración de sesiones de usuario. RP/MVC Antes del middleware que requiere Session.
Archivo estático Proporciona soporte para servir archivos estáticos y la exploración de directorios. Todos Si hay una solicitud que coincida con un archivo, será final.
Reescritura de URL Proporciona compatibilidad con la reescritura de direcciones URL y la redirección de solicitudes. Todos Antes del middleware que consume la dirección URL.
Registro de W3C Genera registros de acceso al servidor con el formato de archivo de registro extendido del W3C. Todos Al principio de la canalización del middleware.
Blazor WebAssembly Depuración Depure los Blazor Web App que usan renderizado del lado del cliente (CSR) en las herramientas para desarrolladores de Chromium. BWA Al principio de la canalización del middleware.
WebSockets Habilita el protocolo WebSockets. Todos Antes de los middleware necesarios para aceptar las solicitudes de WebSocket.

Recursos adicionales