Middleware do ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Por Rick Anderson e Steve Smith

O middleware é um software montado em um pipeline de aplicativo para manipular solicitações e respostas. Cada componente:

  • Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
  • Pode executar o trabalho antes e depois do próximo componente no pipeline.

Os delegados de solicitação são usados para criar o pipeline de solicitação. Os delegados de solicitação manipulam cada solicitação HTTP.

Os delegados de solicitação são configurados usando os métodos de extensão Run, Map e Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado do middleware em linha) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e os métodos anônimos em linha são o middleware, também chamado de componentes do middleware. Cada componente de middleware no pipeline de solicitação é responsável por invocar o próximo componente no pipeline ou causar um curto-circuito do pipeline. Quando um middleware causa um curto-circuito, ele é chamado de middleware terminal, porque impede que outros middlewares processem a solicitação.

Migrar módulos e manipuladores HTTP para o middleware do ASP.NET Core explica a diferença entre pipelines de solicitação no ASP.NET Core e no ASP.NET 4.x e fornece mais exemplos do middleware.

Análise de código do middleware

O ASP.NET Core inclui muitos analisadores de plataforma de compilador que inspecionam o código do aplicativo em busca de qualidade. Para obter mais informações, consulte Análise de código em aplicativos ASP.NET Core

Criar um pipeline de middleware com WebApplication

O pipeline de solicitação do ASP.NET Core consiste em uma sequência de delegados de solicitação, chamados um após o outro. O diagrama a seguir demonstra o conceito. O thread de execução segue as setas pretas.

Padrão de processamento de solicitação mostrando uma solicitação chegando, sendo processada por três middleware e a resposta deixando o aplicativo. Cada middleware executa sua lógica e passa a solicitação para o próximo middleware na instrução Next(). Depois que o terceiro middleware processa a solicitação, ela passa novamente pelos dois middlewares anteriores na ordem inversa para processamento adicional após as instruções next() antes de sair do aplicativo como uma resposta ao cliente.

Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceção devem ser chamados no início do pipeline para que possam detectar exceções que ocorrem em etapas posteriores do pipeline.

O aplicativo ASP.NET Core mais simples possível define um delegado de solicitação única que controla todas as solicitações. Este caso não inclui um pipeline de solicitação real. Em vez disso, uma única função anônima é chamada em resposta a cada solicitação HTTP.

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

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

app.Run();

Encadeie vários delegados de solicitação junto com o Use. O parâmetro next representa o próximo delegado no pipeline. Lembre-se de que você pode causar um curto-circuito no pipeline ao não chamar o parâmetro next. Normalmente, você pode executar ações antes e depois do delegado next, conforme o seguinte exemplo demonstra:

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

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

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

app.Run();

Como causar o curto-circuito no pipeline de solicitação

Quando um delegado não transmite uma solicitação ao próximo delegado, considera-se que ele esteja causando um curto-circuito do pipeline de solicitação. O curto-circuito geralmente é desejável porque ele evita trabalho desnecessário. Por exemplo, o Middleware de Arquivo Estático pode agir com um middleware terminal, processando uma solicitação para um arquivo estático e causar curto-circuito no restante do pipeline. O middleware adicionado ao pipeline antes do middleware que finaliza o processamento adicional ainda processa o código após as instruções next.Invoke. No entanto, confira o seguinte aviso sobre a tentativa de gravar em uma resposta que já foi enviada.

Aviso

Não chame next.Invoke durante ou depois que a resposta tiver sido enviada ao cliente. Depois que uma HttpResponse for iniciada, as alterações resultarão em uma exceção. Por exemplo, definir cabeçalhos e um código de status gera uma exceção após o início da resposta. Gravar no corpo da resposta após a chamada next:

  • Pode causar uma violação de protocolo, como gravar mais do que o Content-Length indicado.
  • Pode corromper o formato do corpo, como escrever um rodapé HTML em um arquivo CSS.

HasStarted é uma dica útil para indicar se os cabeçalhos foram enviados ou o corpo foi gravado.

Para obter mais informações, consulte middleware de curto-circuito após o roteamento.

Representantes Run

Delegados Run não recebem um parâmetro next. O primeiro delegado Run sempre é terminal e encerra o pipeline. Run é uma convenção. A alguns componentes de middleware podem expor os métodos Run[Middleware] que são executados no final do pipeline:

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

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

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

app.Run();

Se você quiser ver os comentários de código traduzidos para idiomas diferentes do inglês, informe-nos neste problema de discussão do GitHub.

No exemplo anterior, o delegado Run grava "Hello from 2nd delegate." na resposta e encerra o pipeline. Se outro delegado Use ou Run for adicionado após o delegado Run, ele não será chamado.

Prefira a sobrecarga app.Use que requer passar o contexto para o próximo

O método de extensão app.Use sem alocação:

  • Requer passar o contexto para next.
  • Salva duas alocações internas por solicitação que são necessárias ao usar a outra sobrecarga.

Saiba mais neste tópico do GitHub.

Ordem do middleware

O diagrama a seguir mostra o pipeline de processamento de solicitação completo para aplicativos MVC do ASP.NET Core e Razor Pages. Você pode ver como, em um aplicativo típico, os middlewares existentes são ordenados e onde middlewares personalizados são adicionados. Você tem controle total sobre como reordenar os middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.

Pipeline de middleware do ASP.NET Core

O middleware do Ponto de Extremidade no diagrama anterior executa o pipeline de filtro para o tipo de aplicativo correspondente: MVC ou Razor Pages.

O middleware de Roteamento no diagrama anterior aparece seguindo Arquivos Estáticos. Essa é a ordem que os modelos de projeto implementam chamando explicitamente app.UseRouting. Se você não chamar app.UseRouting, o middleware de Roteamento será executado no início do pipeline por padrão. Para obter mais informações, consulte Roteamento.

Pipeline de filtro do ASP.NET Core

A ordem em que os componentes do middleware são adicionados ao arquivo Program.cs define a ordem em que os componentes de middleware são invocados nas solicitações e a ordem inversa para a resposta. A ordem é crítica para a segurança, o desempenho e a funcionalidade.

O seguinte código realçado em Program.cs adiciona componentes de middleware relacionados à segurança na ordem recomendada típica:

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

No código anterior:

  • O middleware que não é adicionado ao criar um aplicativo Web com contas de usuários individuais é comentado.
  • Nem todo middleware aparece nesta ordem exata, mas muitos aparecem. Por exemplo:
    • UseCors, UseAuthentication e UseAuthorization devem aparecer na ordem mostrada.
    • Atualmente, UseCors deve aparecer antes UseResponseCaching. Esse requisito é explicado no problema do GitHub dotnet/aspnetcore #23218.
    • UseRequestLocalization precisa aparecer antes de qualquer middleware que possa verificar a cultura de solicitação, por exemplo, app.UseStaticFiles().
    • UseRateLimiter deve ser chamado após UseRouting quando as APIs específicas do ponto de extremidade de limitação de taxa são usadas. Por exemplo, se o atributo [EnableRateLimiting] for usado, UseRateLimiter deverá ser chamado após UseRouting. Ao chamar apenas limitadores globais, UseRateLimiter pode ser chamado antes de UseRouting.

Em alguns cenários, o middleware tem uma ordenação diferente. Por exemplo, o cache e a ordenação de compactação são específicos do cenário e há várias ordenações válidas. Por exemplo:

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

Com o código anterior, o uso da CPU poderia ser reduzido armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando algoritmos de compactação diferentes, como Gzip ou Brotli.

A seguinte ordenação combina arquivos estáticos para permitir o cache de arquivos estáticos compactados:

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

O seguinte código Program.cs adiciona componentes de middleware para cenários de aplicativo comuns:

  1. Exceção/tratamento de erro
    • Quando o aplicativo é executado no ambiente de desenvolvimento:
      • O middleware da página de exceção do desenvolvedor (UseDeveloperExceptionPage) relata erros de runtime do aplicativo.
      • O middleware da página de erro do banco de dados (UseDatabaseErrorPage) relata erros de runtime do banco de dados.
    • Quando o aplicativo é executado no ambiente de produção:
      • O middleware do manipulador de exceção (UseExceptionHandler) captura exceções geradas nos middlewares a seguir.
      • O middleware do protocolo HTTP Strict Transport Security (HSTS) (UseHsts) adiciona o cabeçalho Strict-Transport-Security.
  2. O middleware de redirecionamento para HTTPS (UseHttpsRedirection) redireciona as solicitações HTTP para HTTPS.
  3. O middleware de arquivo estático (UseStaticFiles) retorna arquivos estáticos e impede o processamento de novas solicitações.
  4. O middleware da política de Cookie (UseCookiePolicy) adapta o aplicativo às normas do RGPD (Regulamento Geral sobre a Proteção de Dados).
  5. Middleware de Roteamento (UseRouting) para rotear solicitações.
  6. O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes de ele ter acesso aos recursos seguros.
  7. O Middleware de Autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
  8. O middleware de sessão (UseSession) estabelece e mantém o estado de sessão. Se o aplicativo usa o estado de sessão, chame o middleware de sessão após o middleware de política de Cookie, e antes do middleware do MVC.
  9. Middleware de roteamento de ponto de extremidade (UseEndpoints com MapRazorPages) para adicionar pontos de extremidade do Razor Pages ao pipeline de solicitação.
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();

No código de exemplo anterior, cada método de extensão de middleware é exposto em WebApplicationBuilder por meio do namespace Microsoft.AspNetCore.Builder.

UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o middleware de manipulador de exceção captura todas as exceções que ocorrem em chamadas posteriores.

O Middleware de Arquivo Estático é chamado no início do pipeline para que possa controlar as solicitações e causar o curto-circuito sem passar pelos componentes restantes. O Middleware de Arquivo Estático não fornece nenhuma verificação de autorização. Todos os arquivos fornecidos pelo Middleware de Arquivo Estático, incluindo aqueles em wwwroot, estão disponíveis publicamente. Para conhecer uma abordagem para proteger arquivos estáticos, veja Arquivos estáticos no ASP.NET Core.

Se a solicitação não for controlada pelo Middleware de Arquivo Estático, ela será transmitida para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação causa curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique as solicitações, a autorização (e a rejeição) ocorre somente depois que o MVC seleciona um Razor Page específica ou um controlador MVC e uma ação.

O exemplo a seguir demonstra uma solicitação de middleware cujas solicitações de arquivos estáticos são manipuladas pelo Middleware de Arquivo Estático antes do Middleware de Compactação de Resposta. Arquivos estáticos não são compactados com este pedido de middleware. As respostas do Razor Pages pode ser compactada.

// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

Para obter informações sobre aplicativos de página única, confira Visão geral de aplicativos de página única (SPAs) no ASP.NET Core.

Ordem de UseCors e UseStaticFiles

A ordem de chamada de UseCors e UseStaticFiles depende do aplicativo. Para obter mais informações, consulte Ordem de UseCors e UseStaticFiles

Ordem de Middleware de Cabeçalhos Encaminhados

Middlewares de Cabeçalhos Encaminhados devem ser executados antes de outros middlewares. Essa ordenação garantirá que o middleware conte com informações de cabeçalhos encaminhadas que podem consumir os valores de cabeçalho para processamento. Para executar o Middleware de Cabeçalhos Encaminhados após o diagnóstico e o middleware de tratamento de erros, consulte Ordem do Middleware de Cabeçalhos Encaminhados.

Ramificar o pipeline do middleware

As extensões Map são usadas como uma convenção de ramificação do pipeline. Map ramifica o pipeline de solicitação com base na correspondência do caminho da solicitação em questão. Se o caminho da solicitação iniciar com o caminho especificado, o branch será executado.

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

app.Map("/map1", HandleMapTest1);

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

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

app.Run();

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

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

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior.

Solicitação Response
localhost:1234 Saudação do delegado diferente de Map.
localhost:1234/map1 Teste de Map 1
localhost:1234/map2 Teste de Map 2
localhost:1234/map3 Saudação do delegado diferente de Map.

Ao usar Map, os segmentos de caminho correspondentes são removidos de HttpRequest.Path e anexados a HttpRequest.PathBase para cada solicitação.

Map é compatível com aninhamento, por exemplo:

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

Map também pode ser correspondido com vários segmentos de uma vez:

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

app.Map("/map1/seg1", HandleMultiSeg);

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

app.Run();

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

MapWhen ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Qualquer predicado do tipo Func<HttpContext, bool> pode ser usado para mapear as solicitações para um novo branch do pipeline. No exemplo a seguir, um predicado é usado para detectar a presença de uma variável de cadeia de caracteres de consulta 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 non-Map delegate.");
});

app.Run();

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

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior:

Solicitação Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen também ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Diferente do que acontece com MapWhen, esse branch será retornado ao pipeline principal se ele não tiver um curto-circuito ou contiver um 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 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);

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

No exemplo anterior, uma resposta de Hello from non-Map delegate. é gravada para todas as solicitações. Se a solicitação incluir uma variável de cadeia de caracteres de consulta branch, seu valor será registrado antes que o pipeline principal seja retornado.

Middleware interno

O ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Ordem fornece observações sobre o posicionamento do middleware no pipeline de processamento da solicitação e sob quais condições o middleware podem encerrar o processamento da solicitação. Quando um middleware causa um curto-circuito na solicitação ao processar o pipeline e impede outros middleware downstream de processar uma solicitação, ele é chamado de middleware terminal. Para obter mais informações sobre curto-circuito, confira a seção Criar um pipeline de middleware com WebApplication.

Middleware Descrição Ordem
Autenticação Fornece suporte à autenticação. Antes de HttpContext.User ser necessário. Terminal para retornos de chamada OAuth.
Autorização Fornece suporte à autorização. Imediatamente após o Middleware de Autenticação.
Política de Cookie Acompanha o consentimento dos usuários para o armazenamento de informações pessoais e impõe padrões mínimos para campos de cookie, tais como secure e SameSite. Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão e MVC (TempData).
CORS Configura o Compartilhamento de Recursos entre Origens. Antes de componentes que usam o CORS. Atualmente, UseCors deve ir antes de UseResponseCaching devido a este bug.
DeveloperExceptionPage Gera uma página com informações de erro destinadas a uso somente no ambiente de Desenvolvimento. Antes dos componentes que geram erros. Os modelos de projeto registram automaticamente esse middleware como o primeiro middleware no pipeline quando o ambiente é de Desenvolvimento.
Diagnóstico Vários middlewares separados que fornecem uma página de exceção do desenvolvedor, tratamento de exceções, páginas de código de status e a página da Web padrão para novos aplicativos. Antes dos componentes que geram erros. Terminal para exceções ou para atender a página da Web padrão para novos aplicativos.
Cabeçalhos encaminhados Encaminha cabeçalhos como proxy para a solicitação atual. Antes dos componentes que consomem os campos atualizados. Exemplos: esquema, host, IP do cliente e método.
Verificações de integridade Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como a verificação da disponibilidade do banco de dados. Terminal, se uma solicitação corresponde a um ponto de extremidade da verificação de integridade.
Propagação de cabeçalho Propaga cabeçalhos HTTP da solicitação de entrada para solicitações de cliente HTTP de saída.
Log HTTP Registra solicitações e respostas HTTP. No início do pipeline do middleware.
Substituição do Método HTTP Permite que uma solicitação de entrada POST substitua o método. Antes dos componentes que consomem o método atualizado.
Redirecionamento de HTTPS Redirecione todas as solicitações HTTP para HTTPS. Antes dos componentes que consomem a URL.
Segurança de Transporte Estrita de HTTP (HSTS) Middleware de aprimoramento de segurança que adiciona um cabeçalho de resposta especial. Antes das respostas serem enviadas e depois dos componentes que modificam solicitações. Exemplos: Cabeçalhos encaminhados, regravação de URL.
MVC Processa as solicitações com MVC/Razor Pages. Terminal, se uma solicitação corresponder a uma rota.
OWIN Interoperabilidade com aplicativos baseados em OWIN, em servidores e em middleware. Terminal, se o middleware OWIN processa totalmente a solicitação.
Cache de saída Fornece suporte para respostas de cache com base na configuração. Antes dos componentes que exigem armazenamento em cache. UseRouting deve vir antes de UseOutputCaching. UseCORS deve vir antes de UseOutputCaching.
Cache de resposta Fornece suporte para as respostas em cache. Isso requer a participação do cliente para funcionar. Use o cache de saída para o controle completo do servidor. Antes dos componentes que exigem armazenamento em cache. UseCORS deve vir antes de UseResponseCaching. Normalmente, não é benéfico para aplicativos de interface do usuário, como o Razor Pages, porque os navegadores geralmente definem cabeçalhos de solicitação que impedem o cache. O cache de saída beneficia aplicativos de interface do usuário.
Solicitação de descompactação Dá suporte a solicitações de descompactação. Antes dos componentes que leem o corpo da solicitação.
Compactação de resposta Fornece suporte para a compactação de respostas. Antes dos componentes que exigem compactação.
Localização de Solicitação Fornece suporte à localização. Antes dos componentes de localização importantes. Deve aparecer após o Middleware de Roteamento ao usar RouteDataRequestCultureProvider.
Tempos Limite de Solicitação Fornece suporte para configurar os tempos limite de solicitação, globais e por ponto de extremidade. UseRequestTimeouts deve vir depois de UseExceptionHandler, UseDeveloperExceptionPage e UseRouting.
Roteamento de ponto de extremidade Define e restringe as rotas de solicitação. Terminal de rotas correspondentes.
SPA Manipula todas as solicitações desse ponto na cadeia de middleware retornando a página padrão do SPA (Aplicativo de Página Única) No final da cadeia, para que outro middleware que serve arquivos estáticos, ações de MVC etc., tenha precedência.
Sessão Fornece suporte para gerenciar sessões de usuário. Antes de componentes que exigem a sessão.
Arquivos estáticos Fornece suporte para servir arquivos estáticos e pesquisa no diretório. Terminal, se uma solicitação corresponde a um arquivo.
Regravação de URL Fornece suporte para regravar URLs e redirecionar solicitações. Antes dos componentes que consomem a URL.
W3CLogging Gera logs de acesso do servidor no Formato de arquivo de log estendido W3C. No início do pipeline do middleware.
WebSockets Habilita o protocolo WebSockets. Antes dos componentes que são necessários para aceitar solicitações de WebSocket.

Recursos adicionais

Por Rick Anderson e Steve Smith

O middleware é um software montado em um pipeline de aplicativo para manipular solicitações e respostas. Cada componente:

  • Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
  • Pode executar o trabalho antes e depois do próximo componente no pipeline.

Os delegados de solicitação são usados para criar o pipeline de solicitação. Os delegados de solicitação manipulam cada solicitação HTTP.

Os delegados de solicitação são configurados usando os métodos de extensão Run, Map e Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado do middleware em linha) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e os métodos anônimos em linha são o middleware, também chamado de componentes do middleware. Cada componente de middleware no pipeline de solicitação é responsável por invocar o próximo componente no pipeline ou causar um curto-circuito do pipeline. Quando um middleware causa um curto-circuito, ele é chamado de middleware terminal, porque impede que outros middlewares processem a solicitação.

Migrar módulos e manipuladores HTTP para o middleware do ASP.NET Core explica a diferença entre pipelines de solicitação no ASP.NET Core e no ASP.NET 4.x e fornece mais exemplos do middleware.

Análise de código do middleware

O ASP.NET Core inclui muitos analisadores de plataforma de compilador que inspecionam o código do aplicativo em busca de qualidade. Para obter mais informações, consulte Análise de código em aplicativos ASP.NET Core

Criar um pipeline de middleware com WebApplication

O pipeline de solicitação do ASP.NET Core consiste em uma sequência de delegados de solicitação, chamados um após o outro. O diagrama a seguir demonstra o conceito. O thread de execução segue as setas pretas.

Padrão de processamento de solicitação mostrando uma solicitação chegando, sendo processada por três middleware e a resposta deixando o aplicativo. Cada middleware executa sua lógica e passa a solicitação para o próximo middleware na instrução Next(). Depois que o terceiro middleware processa a solicitação, ela passa novamente pelos dois middlewares anteriores na ordem inversa para processamento adicional após as instruções next() antes de sair do aplicativo como uma resposta ao cliente.

Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceção devem ser chamados no início do pipeline para que possam detectar exceções que ocorrem em etapas posteriores do pipeline.

O aplicativo ASP.NET Core mais simples possível define um delegado de solicitação única que controla todas as solicitações. Este caso não inclui um pipeline de solicitação real. Em vez disso, uma única função anônima é chamada em resposta a cada solicitação HTTP.

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

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

app.Run();

Encadeie vários delegados de solicitação junto com o Use. O parâmetro next representa o próximo delegado no pipeline. Lembre-se de que você pode causar um curto-circuito no pipeline ao não chamar o parâmetro next. Normalmente, você pode executar ações antes e depois do delegado next, conforme o seguinte exemplo demonstra:

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

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

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

app.Run();

Quando um delegado não transmite uma solicitação ao próximo delegado, considera-se que ele esteja causando um curto-circuito do pipeline de solicitação. O curto-circuito geralmente é desejável porque ele evita trabalho desnecessário. Por exemplo, o Middleware de Arquivo Estático pode agir com um middleware terminal, processando uma solicitação para um arquivo estático e causar curto-circuito no restante do pipeline. O middleware adicionado ao pipeline antes do middleware que finaliza o processamento adicional ainda processa o código após as instruções next.Invoke. No entanto, confira o seguinte aviso sobre a tentativa de gravar em uma resposta que já foi enviada.

Aviso

Não chame next.Invoke depois que a resposta tiver sido enviada ao cliente. Altera para HttpResponse depois de a resposta ser iniciada e lança uma exceção. Por exemplo, a configuração de cabeçalhos e o código de status lançam uma exceção. Gravar no corpo da resposta após a chamada next:

  • Pode causar uma violação do protocolo. Por exemplo, gravar mais do que o Content-Length indicado.
  • Pode corromper o formato do corpo. Por exemplo, gravar um rodapé HTML em um arquivo CSS.

HasStarted é uma dica útil para indicar se os cabeçalhos foram enviados ou o corpo foi gravado.

Delegados Run não recebem um parâmetro next. O primeiro delegado Run sempre é terminal e encerra o pipeline. Run é uma convenção. A alguns componentes de middleware podem expor os métodos Run[Middleware] que são executados no final do pipeline:

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

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

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

app.Run();

Se você quiser ver os comentários de código traduzidos para idiomas diferentes do inglês, informe-nos neste problema de discussão do GitHub.

No exemplo anterior, o delegado Run grava "Hello from 2nd delegate." na resposta e encerra o pipeline. Se outro delegado Use ou Run for adicionado após o delegado Run, ele não será chamado.

Prefira a sobrecarga app.Use que requer passar o contexto para o próximo

O método de extensão app.Use sem alocação:

  • Requer passar o contexto para next.
  • Salva duas alocações internas por solicitação que são necessárias ao usar a outra sobrecarga.

Saiba mais neste tópico do GitHub.

Ordem do middleware

O diagrama a seguir mostra o pipeline de processamento de solicitação completo para aplicativos MVC do ASP.NET Core e Razor Pages. Você pode ver como, em um aplicativo típico, os middlewares existentes são ordenados e onde middlewares personalizados são adicionados. Você tem controle total sobre como reordenar os middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.

Pipeline de middleware do ASP.NET Core

O middleware do Ponto de Extremidade no diagrama anterior executa o pipeline de filtro para o tipo de aplicativo correspondente: MVC ou Razor Pages.

O middleware de Roteamento no diagrama anterior aparece seguindo Arquivos Estáticos. Essa é a ordem que os modelos de projeto implementam chamando explicitamente app.UseRouting. Se você não chamar app.UseRouting, o middleware de Roteamento será executado no início do pipeline por padrão. Para obter mais informações, consulte Roteamento.

Pipeline de filtro do ASP.NET Core

A ordem em que os componentes do middleware são adicionados ao arquivo Program.cs define a ordem em que os componentes de middleware são invocados nas solicitações e a ordem inversa para a resposta. A ordem é crítica para a segurança, o desempenho e a funcionalidade.

O seguinte código realçado em Program.cs adiciona componentes de middleware relacionados à segurança na ordem recomendada típica:

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

No código anterior:

  • O middleware que não é adicionado ao criar um aplicativo Web com contas de usuários individuais é comentado.
  • Nem todo middleware aparece nesta ordem exata, mas muitos aparecem. Por exemplo:
    • UseCors, UseAuthentication e UseAuthorization devem aparecer na ordem mostrada.
    • Atualmente, UseCors deve aparecer antes UseResponseCaching. Esse requisito é explicado no problema do GitHub dotnet/aspnetcore #23218.
    • UseRequestLocalization precisa aparecer antes de qualquer middleware que possa verificar a cultura de solicitação, por exemplo, app.UseStaticFiles().
    • UseRateLimiter deve ser chamado após UseRouting quando as APIs específicas do ponto de extremidade de limitação de taxa são usadas. Por exemplo, se o atributo [EnableRateLimiting] for usado, UseRateLimiter deverá ser chamado após UseRouting. Ao chamar apenas limitadores globais, UseRateLimiter pode ser chamado antes de UseRouting.

Em alguns cenários, o middleware tem uma ordenação diferente. Por exemplo, o cache e a ordenação de compactação são específicos do cenário e há várias ordenações válidas. Por exemplo:

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

Com o código anterior, o uso da CPU poderia ser reduzido armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando algoritmos de compactação diferentes, como Gzip ou Brotli.

A seguinte ordenação combina arquivos estáticos para permitir o cache de arquivos estáticos compactados:

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

O seguinte código Program.cs adiciona componentes de middleware para cenários de aplicativo comuns:

  1. Exceção/tratamento de erro
    • Quando o aplicativo é executado no ambiente de desenvolvimento:
      • O middleware da página de exceção do desenvolvedor (UseDeveloperExceptionPage) relata erros de runtime do aplicativo.
      • O middleware da página de erro do banco de dados (UseDatabaseErrorPage) relata erros de runtime do banco de dados.
    • Quando o aplicativo é executado no ambiente de produção:
      • O middleware do manipulador de exceção (UseExceptionHandler) captura exceções geradas nos middlewares a seguir.
      • O middleware do protocolo HTTP Strict Transport Security (HSTS) (UseHsts) adiciona o cabeçalho Strict-Transport-Security.
  2. O middleware de redirecionamento para HTTPS (UseHttpsRedirection) redireciona as solicitações HTTP para HTTPS.
  3. O middleware de arquivo estático (UseStaticFiles) retorna arquivos estáticos e impede o processamento de novas solicitações.
  4. O middleware da política de Cookie (UseCookiePolicy) adapta o aplicativo às normas do RGPD (Regulamento Geral sobre a Proteção de Dados).
  5. Middleware de Roteamento (UseRouting) para rotear solicitações.
  6. O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes de ele ter acesso aos recursos seguros.
  7. O Middleware de Autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
  8. O middleware de sessão (UseSession) estabelece e mantém o estado de sessão. Se o aplicativo usa o estado de sessão, chame o middleware de sessão após o middleware de política de Cookie, e antes do middleware do MVC.
  9. Middleware de roteamento de ponto de extremidade (UseEndpoints com MapRazorPages) para adicionar pontos de extremidade do Razor Pages ao pipeline de solicitação.
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();

No código de exemplo anterior, cada método de extensão de middleware é exposto em WebApplicationBuilder por meio do namespace Microsoft.AspNetCore.Builder.

UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o middleware de manipulador de exceção captura todas as exceções que ocorrem em chamadas posteriores.

O Middleware de Arquivo Estático é chamado no início do pipeline para que possa controlar as solicitações e causar o curto-circuito sem passar pelos componentes restantes. O Middleware de Arquivo Estático não fornece nenhuma verificação de autorização. Todos os arquivos fornecidos pelo Middleware de Arquivo Estático, incluindo aqueles em wwwroot, estão disponíveis publicamente. Para conhecer uma abordagem para proteger arquivos estáticos, veja Arquivos estáticos no ASP.NET Core.

Se a solicitação não for controlada pelo Middleware de Arquivo Estático, ela será transmitida para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação causa curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique as solicitações, a autorização (e a rejeição) ocorre somente depois que o MVC seleciona um Razor Page específica ou um controlador MVC e uma ação.

O exemplo a seguir demonstra uma solicitação de middleware cujas solicitações de arquivos estáticos são manipuladas pelo Middleware de Arquivo Estático antes do Middleware de Compactação de Resposta. Arquivos estáticos não são compactados com este pedido de middleware. As respostas do Razor Pages pode ser compactada.

// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

Para obter informações sobre aplicativos de página única, consulte os guias dos modelos de projeto React e Angular.

Ordem de UseCors e UseStaticFiles

A ordem de chamada de UseCors e UseStaticFiles depende do aplicativo. Para obter mais informações, consulte Ordem de UseCors e UseStaticFiles

Ordem de Middleware de Cabeçalhos Encaminhados

Middlewares de Cabeçalhos Encaminhados devem ser executados antes de outros middlewares. Essa ordenação garantirá que o middleware conte com informações de cabeçalhos encaminhadas que podem consumir os valores de cabeçalho para processamento. Para executar o Middleware de Cabeçalhos Encaminhados após o diagnóstico e o middleware de tratamento de erros, consulte Ordem do Middleware de Cabeçalhos Encaminhados.

Ramificar o pipeline do middleware

As extensões Map são usadas como uma convenção de ramificação do pipeline. Map ramifica o pipeline de solicitação com base na correspondência do caminho da solicitação em questão. Se o caminho da solicitação iniciar com o caminho especificado, o branch será executado.

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

app.Map("/map1", HandleMapTest1);

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

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

app.Run();

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

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

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior.

Solicitação Response
localhost:1234 Saudação do delegado diferente de Map.
localhost:1234/map1 Teste de Map 1
localhost:1234/map2 Teste de Map 2
localhost:1234/map3 Saudação do delegado diferente de Map.

Ao usar Map, os segmentos de caminho correspondentes são removidos de HttpRequest.Path e anexados a HttpRequest.PathBase para cada solicitação.

Map é compatível com aninhamento, por exemplo:

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

Map também pode ser correspondido com vários segmentos de uma vez:

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

app.Map("/map1/seg1", HandleMultiSeg);

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

app.Run();

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

MapWhen ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Qualquer predicado do tipo Func<HttpContext, bool> pode ser usado para mapear as solicitações para um novo branch do pipeline. No exemplo a seguir, um predicado é usado para detectar a presença de uma variável de cadeia de caracteres de consulta 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 non-Map delegate.");
});

app.Run();

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

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior:

Solicitação Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen também ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Diferente do que acontece com MapWhen, esse branch será retornado ao pipeline principal se ele não tiver um curto-circuito ou contiver um 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 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);

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

No exemplo anterior, uma resposta de Hello from non-Map delegate. é gravada para todas as solicitações. Se a solicitação incluir uma variável de cadeia de caracteres de consulta branch, seu valor será registrado antes que o pipeline principal seja retornado.

Middleware interno

O ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Ordem fornece observações sobre o posicionamento do middleware no pipeline de processamento da solicitação e sob quais condições o middleware podem encerrar o processamento da solicitação. Quando um middleware causa um curto-circuito na solicitação ao processar o pipeline e impede outros middleware downstream de processar uma solicitação, ele é chamado de middleware terminal. Para obter mais informações sobre curto-circuito, confira a seção Criar um pipeline de middleware com WebApplication.

Middleware Descrição Ordem
Autenticação Fornece suporte à autenticação. Antes de HttpContext.User ser necessário. Terminal para retornos de chamada OAuth.
Autorização Fornece suporte à autorização. Imediatamente após o Middleware de Autenticação.
Política de Cookie Acompanha o consentimento dos usuários para o armazenamento de informações pessoais e impõe padrões mínimos para campos de cookie, tais como secure e SameSite. Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão e MVC (TempData).
CORS Configura o Compartilhamento de Recursos entre Origens. Antes de componentes que usam o CORS. Atualmente, UseCors deve ir antes de UseResponseCaching devido a este bug.
DeveloperExceptionPage Gera uma página com informações de erro destinadas a uso somente no ambiente de Desenvolvimento. Antes dos componentes que geram erros. Os modelos de projeto registram automaticamente esse middleware como o primeiro middleware no pipeline quando o ambiente é de Desenvolvimento.
Diagnóstico Vários middlewares separados que fornecem uma página de exceção do desenvolvedor, tratamento de exceções, páginas de código de status e a página da Web padrão para novos aplicativos. Antes dos componentes que geram erros. Terminal para exceções ou para atender a página da Web padrão para novos aplicativos.
Cabeçalhos encaminhados Encaminha cabeçalhos como proxy para a solicitação atual. Antes dos componentes que consomem os campos atualizados. Exemplos: esquema, host, IP do cliente e método.
Verificações de integridade Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como a verificação da disponibilidade do banco de dados. Terminal, se uma solicitação corresponde a um ponto de extremidade da verificação de integridade.
Propagação de cabeçalho Propaga cabeçalhos HTTP da solicitação de entrada para solicitações de cliente HTTP de saída.
Log HTTP Registra solicitações e respostas HTTP. No início do pipeline do middleware.
Substituição do Método HTTP Permite que uma solicitação de entrada POST substitua o método. Antes dos componentes que consomem o método atualizado.
Redirecionamento de HTTPS Redirecione todas as solicitações HTTP para HTTPS. Antes dos componentes que consomem a URL.
Segurança de Transporte Estrita de HTTP (HSTS) Middleware de aprimoramento de segurança que adiciona um cabeçalho de resposta especial. Antes das respostas serem enviadas e depois dos componentes que modificam solicitações. Exemplos: Cabeçalhos encaminhados, regravação de URL.
MVC Processa as solicitações com MVC/Razor Pages. Terminal, se uma solicitação corresponder a uma rota.
OWIN Interoperabilidade com aplicativos baseados em OWIN, em servidores e em middleware. Terminal, se o middleware OWIN processa totalmente a solicitação.
Cache de saída Fornece suporte para respostas de cache com base na configuração. Antes dos componentes que exigem armazenamento em cache. UseRouting deve vir antes de UseOutputCaching. UseCORS deve vir antes de UseOutputCaching.
Cache de resposta Fornece suporte para as respostas em cache. Isso requer a participação do cliente para funcionar. Use o cache de saída para o controle completo do servidor. Antes dos componentes que exigem armazenamento em cache. UseCORS deve vir antes de UseResponseCaching. Normalmente, não é benéfico para aplicativos de interface do usuário, como o Razor Pages, porque os navegadores geralmente definem cabeçalhos de solicitação que impedem o cache. O cache de saída beneficia aplicativos de interface do usuário.
Solicitação de descompactação Dá suporte a solicitações de descompactação. Antes dos componentes que leem o corpo da solicitação.
Compactação de resposta Fornece suporte para a compactação de respostas. Antes dos componentes que exigem compactação.
Localização de Solicitação Fornece suporte à localização. Antes dos componentes de localização importantes. Deve aparecer após o Middleware de Roteamento ao usar RouteDataRequestCultureProvider.
Roteamento de ponto de extremidade Define e restringe as rotas de solicitação. Terminal de rotas correspondentes.
SPA Manipula todas as solicitações desse ponto na cadeia de middleware retornando a página padrão do SPA (Aplicativo de Página Única) No final da cadeia, para que outro middleware que serve arquivos estáticos, ações de MVC etc., tenha precedência.
Sessão Fornece suporte para gerenciar sessões de usuário. Antes de componentes que exigem a sessão.
Arquivos estáticos Fornece suporte para servir arquivos estáticos e pesquisa no diretório. Terminal, se uma solicitação corresponde a um arquivo.
Regravação de URL Fornece suporte para regravar URLs e redirecionar solicitações. Antes dos componentes que consomem a URL.
W3CLogging Gera logs de acesso do servidor no Formato de arquivo de log estendido W3C. No início do pipeline do middleware.
WebSockets Habilita o protocolo WebSockets. Antes dos componentes que são necessários para aceitar solicitações de WebSocket.

Recursos adicionais

Por Rick Anderson e Steve Smith

O middleware é um software montado em um pipeline de aplicativo para manipular solicitações e respostas. Cada componente:

  • Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
  • Pode executar o trabalho antes e depois do próximo componente no pipeline.

Os delegados de solicitação são usados para criar o pipeline de solicitação. Os delegados de solicitação manipulam cada solicitação HTTP.

Os delegados de solicitação são configurados usando os métodos de extensão Run, Map e Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado do middleware em linha) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e os métodos anônimos em linha são o middleware, também chamado de componentes do middleware. Cada componente de middleware no pipeline de solicitação é responsável por invocar o próximo componente no pipeline ou causar um curto-circuito do pipeline. Quando um middleware causa um curto-circuito, ele é chamado de middleware terminal, porque impede que outros middlewares processem a solicitação.

Migrar módulos e manipuladores HTTP para o middleware do ASP.NET Core explica a diferença entre pipelines de solicitação no ASP.NET Core e no ASP.NET 4.x e fornece mais exemplos do middleware.

Análise de código do middleware

O ASP.NET Core inclui muitos analisadores de plataforma de compilador que inspecionam o código do aplicativo em busca de qualidade. Para obter mais informações, consulte Análise de código em aplicativos ASP.NET Core

Criar um pipeline de middleware com WebApplication

O pipeline de solicitação do ASP.NET Core consiste em uma sequência de delegados de solicitação, chamados um após o outro. O diagrama a seguir demonstra o conceito. O thread de execução segue as setas pretas.

Padrão de processamento de solicitação mostrando uma solicitação chegando, sendo processada por três middleware e a resposta deixando o aplicativo. Cada middleware executa sua lógica e passa a solicitação para o próximo middleware na instrução Next(). Depois que o terceiro middleware processa a solicitação, ela passa novamente pelos dois middlewares anteriores na ordem inversa para processamento adicional após as instruções next() antes de sair do aplicativo como uma resposta ao cliente.

Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceção devem ser chamados no início do pipeline para que possam detectar exceções que ocorrem em etapas posteriores do pipeline.

O aplicativo ASP.NET Core mais simples possível define um delegado de solicitação única que controla todas as solicitações. Este caso não inclui um pipeline de solicitação real. Em vez disso, uma única função anônima é chamada em resposta a cada solicitação HTTP.

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

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

app.Run();

Encadeie vários delegados de solicitação junto com o Use. O parâmetro next representa o próximo delegado no pipeline. Lembre-se de que você pode causar um curto-circuito no pipeline ao não chamar o parâmetro next. Normalmente, você pode executar ações antes e depois do delegado next, conforme o seguinte exemplo demonstra:

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

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

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

app.Run();

Quando um delegado não transmite uma solicitação ao próximo delegado, considera-se que ele esteja causando um curto-circuito do pipeline de solicitação. O curto-circuito geralmente é desejável porque ele evita trabalho desnecessário. Por exemplo, o Middleware de Arquivo Estático pode agir com um middleware terminal, processando uma solicitação para um arquivo estático e causar curto-circuito no restante do pipeline. O middleware adicionado ao pipeline antes do middleware que finaliza o processamento adicional ainda processa o código após as instruções next.Invoke. No entanto, confira o seguinte aviso sobre a tentativa de gravar em uma resposta que já foi enviada.

Aviso

Não chame next.Invoke depois que a resposta tiver sido enviada ao cliente. Altera para HttpResponse depois de a resposta ser iniciada e lança uma exceção. Por exemplo, a configuração de cabeçalhos e o código de status lançam uma exceção. Gravar no corpo da resposta após a chamada next:

  • Pode causar uma violação do protocolo. Por exemplo, gravar mais do que o Content-Length indicado.
  • Pode corromper o formato do corpo. Por exemplo, gravar um rodapé HTML em um arquivo CSS.

HasStarted é uma dica útil para indicar se os cabeçalhos foram enviados ou o corpo foi gravado.

Delegados Run não recebem um parâmetro next. O primeiro delegado Run sempre é terminal e encerra o pipeline. Run é uma convenção. A alguns componentes de middleware podem expor os métodos Run[Middleware] que são executados no final do pipeline:

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

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

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

app.Run();

Se você quiser ver os comentários de código traduzidos para idiomas diferentes do inglês, informe-nos neste problema de discussão do GitHub.

No exemplo anterior, o delegado Run grava "Hello from 2nd delegate." na resposta e encerra o pipeline. Se outro delegado Use ou Run for adicionado após o delegado Run, ele não será chamado.

Prefira a sobrecarga app.Use que requer passar o contexto para o próximo

O método de extensão app.Use sem alocação:

  • Requer passar o contexto para next.
  • Salva duas alocações internas por solicitação que são necessárias ao usar a outra sobrecarga.

Saiba mais neste tópico do GitHub.

Ordem do middleware

O diagrama a seguir mostra o pipeline de processamento de solicitação completo para aplicativos MVC do ASP.NET Core e Razor Pages. Você pode ver como, em um aplicativo típico, os middlewares existentes são ordenados e onde middlewares personalizados são adicionados. Você tem controle total sobre como reordenar os middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.

Pipeline de middleware do ASP.NET Core

O middleware do Ponto de Extremidade no diagrama anterior executa o pipeline de filtro para o tipo de aplicativo correspondente: MVC ou Razor Pages.

O middleware de Roteamento no diagrama anterior aparece seguindo Arquivos Estáticos. Essa é a ordem que os modelos de projeto implementam chamando explicitamente app.UseRouting. Se você não chamar app.UseRouting, o middleware de Roteamento será executado no início do pipeline por padrão. Para obter mais informações, consulte Roteamento.

Pipeline de filtro do ASP.NET Core

A ordem em que os componentes do middleware são adicionados ao arquivo Program.cs define a ordem em que os componentes de middleware são invocados nas solicitações e a ordem inversa para a resposta. A ordem é crítica para a segurança, o desempenho e a funcionalidade.

O seguinte código realçado em Program.cs adiciona componentes de middleware relacionados à segurança na ordem recomendada típica:

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

No código anterior:

  • O middleware que não é adicionado ao criar um aplicativo Web com contas de usuários individuais é comentado.
  • Nem todo middleware aparece nesta ordem exata, mas muitos aparecem. Por exemplo:
    • UseCors, UseAuthentication e UseAuthorization devem aparecer na ordem mostrada.
    • Atualmente, UseCors deve aparecer antes UseResponseCaching. Esse requisito é explicado no problema do GitHub dotnet/aspnetcore #23218.
    • UseRequestLocalization precisa aparecer antes de qualquer middleware que possa verificar a cultura de solicitação (por exemplo, app.UseMvcWithDefaultRoute()).

Em alguns cenários, o middleware tem uma ordenação diferente. Por exemplo, o cache e a ordenação de compactação são específicos do cenário e há várias ordenações válidas. Por exemplo:

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

Com o código anterior, o uso da CPU poderia ser reduzido armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando algoritmos de compactação diferentes, como Gzip ou Brotli.

A seguinte ordenação combina arquivos estáticos para permitir o cache de arquivos estáticos compactados:

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

O seguinte código Program.cs adiciona componentes de middleware para cenários de aplicativo comuns:

  1. Exceção/tratamento de erro
    • Quando o aplicativo é executado no ambiente de desenvolvimento:
      • O middleware da página de exceção do desenvolvedor (UseDeveloperExceptionPage) relata erros de runtime do aplicativo.
      • O middleware da página de erro do banco de dados (UseDatabaseErrorPage) relata erros de runtime do banco de dados.
    • Quando o aplicativo é executado no ambiente de produção:
      • O middleware do manipulador de exceção (UseExceptionHandler) captura exceções geradas nos middlewares a seguir.
      • O middleware do protocolo HTTP Strict Transport Security (HSTS) (UseHsts) adiciona o cabeçalho Strict-Transport-Security.
  2. O middleware de redirecionamento para HTTPS (UseHttpsRedirection) redireciona as solicitações HTTP para HTTPS.
  3. O middleware de arquivo estático (UseStaticFiles) retorna arquivos estáticos e impede o processamento de novas solicitações.
  4. O middleware da política de Cookie (UseCookiePolicy) adapta o aplicativo às normas do RGPD (Regulamento Geral sobre a Proteção de Dados).
  5. Middleware de Roteamento (UseRouting) para rotear solicitações.
  6. O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes de ele ter acesso aos recursos seguros.
  7. O Middleware de Autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
  8. O middleware de sessão (UseSession) estabelece e mantém o estado de sessão. Se o aplicativo usa o estado de sessão, chame o middleware de sessão após o middleware de política de Cookie, e antes do middleware do MVC.
  9. Middleware de roteamento de ponto de extremidade (UseEndpoints com MapRazorPages) para adicionar pontos de extremidade do Razor Pages ao pipeline de solicitação.
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();

No código de exemplo anterior, cada método de extensão de middleware é exposto em WebApplicationBuilder por meio do namespace Microsoft.AspNetCore.Builder.

UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o middleware de manipulador de exceção captura todas as exceções que ocorrem em chamadas posteriores.

O Middleware de Arquivo Estático é chamado no início do pipeline para que possa controlar as solicitações e causar o curto-circuito sem passar pelos componentes restantes. O Middleware de Arquivo Estático não fornece nenhuma verificação de autorização. Todos os arquivos fornecidos pelo Middleware de Arquivo Estático, incluindo aqueles em wwwroot, estão disponíveis publicamente. Para conhecer uma abordagem para proteger arquivos estáticos, veja Arquivos estáticos no ASP.NET Core.

Se a solicitação não for controlada pelo Middleware de Arquivo Estático, ela será transmitida para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação causa curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique as solicitações, a autorização (e a rejeição) ocorre somente depois que o MVC seleciona um Razor Page específica ou um controlador MVC e uma ação.

O exemplo a seguir demonstra uma solicitação de middleware cujas solicitações de arquivos estáticos são manipuladas pelo Middleware de Arquivo Estático antes do Middleware de Compactação de Resposta. Arquivos estáticos não são compactados com este pedido de middleware. As respostas do Razor Pages pode ser compactada.

// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

Para obter informações sobre aplicativos de página única, consulte os guias dos modelos de projeto React e Angular.

Ordem de UseCors e UseStaticFiles

A ordem de chamada de UseCors e UseStaticFiles depende do aplicativo. Para obter mais informações, consulte Ordem de UseCors e UseStaticFiles

Ordem de Middleware de Cabeçalhos Encaminhados

Middlewares de Cabeçalhos Encaminhados devem ser executados antes de outros middlewares. Essa ordenação garantirá que o middleware conte com informações de cabeçalhos encaminhadas que podem consumir os valores de cabeçalho para processamento. Para executar o Middleware de Cabeçalhos Encaminhados após o diagnóstico e o middleware de tratamento de erros, consulte Ordem do Middleware de Cabeçalhos Encaminhados.

Ramificar o pipeline do middleware

As extensões Map são usadas como uma convenção de ramificação do pipeline. Map ramifica o pipeline de solicitação com base na correspondência do caminho da solicitação em questão. Se o caminho da solicitação iniciar com o caminho especificado, o branch será executado.

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

app.Map("/map1", HandleMapTest1);

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

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

app.Run();

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

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

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior.

Solicitação Response
localhost:1234 Saudação do delegado diferente de Map.
localhost:1234/map1 Teste de Map 1
localhost:1234/map2 Teste de Map 2
localhost:1234/map3 Saudação do delegado diferente de Map.

Ao usar Map, os segmentos de caminho correspondentes são removidos de HttpRequest.Path e anexados a HttpRequest.PathBase para cada solicitação.

Map é compatível com aninhamento, por exemplo:

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

Map também pode ser correspondido com vários segmentos de uma vez:

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

app.Map("/map1/seg1", HandleMultiSeg);

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

app.Run();

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

MapWhen ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Qualquer predicado do tipo Func<HttpContext, bool> pode ser usado para mapear as solicitações para um novo branch do pipeline. No exemplo a seguir, um predicado é usado para detectar a presença de uma variável de cadeia de caracteres de consulta 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 non-Map delegate.");
});

app.Run();

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

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior:

Solicitação Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen também ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Diferente do que acontece com MapWhen, esse branch será retornado ao pipeline principal se ele não tiver um curto-circuito ou contiver um 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 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);

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

No exemplo anterior, uma resposta de Hello from non-Map delegate. é gravada para todas as solicitações. Se a solicitação incluir uma variável de cadeia de caracteres de consulta branch, seu valor será registrado antes que o pipeline principal seja retornado.

Middleware interno

O ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Ordem fornece observações sobre o posicionamento do middleware no pipeline de processamento da solicitação e sob quais condições o middleware podem encerrar o processamento da solicitação. Quando um middleware causa um curto-circuito na solicitação ao processar o pipeline e impede outros middleware downstream de processar uma solicitação, ele é chamado de middleware terminal. Para obter mais informações sobre curto-circuito, confira a seção Criar um pipeline de middleware com WebApplication.

Middleware Descrição Ordem
Autenticação Fornece suporte à autenticação. Antes de HttpContext.User ser necessário. Terminal para retornos de chamada OAuth.
Autorização Fornece suporte à autorização. Imediatamente após o Middleware de Autenticação.
Política de Cookie Acompanha o consentimento dos usuários para o armazenamento de informações pessoais e impõe padrões mínimos para campos de cookie, tais como secure e SameSite. Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão e MVC (TempData).
CORS Configura o Compartilhamento de Recursos entre Origens. Antes de componentes que usam o CORS. Atualmente, UseCors deve ir antes de UseResponseCaching devido a este bug.
DeveloperExceptionPage Gera uma página com informações de erro destinadas a uso somente no ambiente de Desenvolvimento. Antes dos componentes que geram erros. Os modelos de projeto registram automaticamente esse middleware como o primeiro middleware no pipeline quando o ambiente é de Desenvolvimento.
Diagnóstico Vários middlewares separados que fornecem uma página de exceção do desenvolvedor, tratamento de exceções, páginas de código de status e a página da Web padrão para novos aplicativos. Antes dos componentes que geram erros. Terminal para exceções ou para atender a página da Web padrão para novos aplicativos.
Cabeçalhos encaminhados Encaminha cabeçalhos como proxy para a solicitação atual. Antes dos componentes que consomem os campos atualizados. Exemplos: esquema, host, IP do cliente e método.
Verificações de integridade Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como a verificação da disponibilidade do banco de dados. Terminal, se uma solicitação corresponde a um ponto de extremidade da verificação de integridade.
Propagação de cabeçalho Propaga cabeçalhos HTTP da solicitação de entrada para solicitações de cliente HTTP de saída.
Log HTTP Registra solicitações e respostas HTTP. No início do pipeline do middleware.
Substituição do Método HTTP Permite que uma solicitação de entrada POST substitua o método. Antes dos componentes que consomem o método atualizado.
Redirecionamento de HTTPS Redirecione todas as solicitações HTTP para HTTPS. Antes dos componentes que consomem a URL.
Segurança de Transporte Estrita de HTTP (HSTS) Middleware de aprimoramento de segurança que adiciona um cabeçalho de resposta especial. Antes das respostas serem enviadas e depois dos componentes que modificam solicitações. Exemplos: Cabeçalhos encaminhados, regravação de URL.
MVC Processa as solicitações com MVC/Razor Pages. Terminal, se uma solicitação corresponder a uma rota.
OWIN Interoperabilidade com aplicativos baseados em OWIN, em servidores e em middleware. Terminal, se o middleware OWIN processa totalmente a solicitação.
Solicitação de descompactação Dá suporte a solicitações de descompactação. Antes dos componentes que leem o corpo da solicitação.
Cache de resposta Fornece suporte para as respostas em cache. Antes dos componentes que exigem armazenamento em cache. UseCORS deve vir antes de UseResponseCaching.
Compactação de resposta Fornece suporte para a compactação de respostas. Antes dos componentes que exigem compactação.
Localização de Solicitação Fornece suporte à localização. Antes dos componentes de localização importantes. Deve aparecer após o Middleware de Roteamento ao usar RouteDataRequestCultureProvider.
Roteamento de ponto de extremidade Define e restringe as rotas de solicitação. Terminal de rotas correspondentes.
SPA Manipula todas as solicitações desse ponto na cadeia de middleware retornando a página padrão do SPA (Aplicativo de Página Única) No final da cadeia, para que outro middleware que serve arquivos estáticos, ações de MVC etc., tenha precedência.
Sessão Fornece suporte para gerenciar sessões de usuário. Antes de componentes que exigem a sessão.
Arquivos estáticos Fornece suporte para servir arquivos estáticos e pesquisa no diretório. Terminal, se uma solicitação corresponde a um arquivo.
Regravação de URL Fornece suporte para regravar URLs e redirecionar solicitações. Antes dos componentes que consomem a URL.
W3CLogging Gera logs de acesso do servidor no Formato de arquivo de log estendido W3C. No início do pipeline do middleware.
WebSockets Habilita o protocolo WebSockets. Antes dos componentes que são necessários para aceitar solicitações de WebSocket.

Recursos adicionais

Por Rick Anderson e Steve Smith

O middleware é um software montado em um pipeline de aplicativo para manipular solicitações e respostas. Cada componente:

  • Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
  • Pode executar o trabalho antes e depois do próximo componente no pipeline.

Os delegados de solicitação são usados para criar o pipeline de solicitação. Os delegados de solicitação manipulam cada solicitação HTTP.

Os delegados de solicitação são configurados usando os métodos de extensão Run, Map e Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado do middleware em linha) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e os métodos anônimos em linha são o middleware, também chamado de componentes do middleware. Cada componente de middleware no pipeline de solicitação é responsável por invocar o próximo componente no pipeline ou causar um curto-circuito do pipeline. Quando um middleware causa um curto-circuito, ele é chamado de middleware terminal, porque impede que outros middlewares processem a solicitação.

Migrar módulos e manipuladores HTTP para o middleware do ASP.NET Core explica a diferença entre pipelines de solicitação no ASP.NET Core e no ASP.NET 4.x e fornece mais exemplos do middleware.

Criar um pipeline do middleware com o IApplicationBuilder

O pipeline de solicitação do ASP.NET Core consiste em uma sequência de delegados de solicitação, chamados um após o outro. O diagrama a seguir demonstra o conceito. O thread de execução segue as setas pretas.

Padrão de processamento de solicitação mostrando uma solicitação chegando, sendo processada por três middleware e a resposta deixando o aplicativo. Cada middleware executa sua lógica e passa a solicitação para o próximo middleware na instrução Next(). Depois que o terceiro middleware processa a solicitação, ela passa novamente pelos dois middlewares anteriores na ordem inversa para processamento adicional após as instruções next() antes de sair do aplicativo como uma resposta ao cliente.

Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceção devem ser chamados no início do pipeline para que possam detectar exceções que ocorrem em etapas posteriores do pipeline.

O aplicativo ASP.NET Core mais simples possível define um delegado de solicitação única que controla todas as solicitações. Este caso não inclui um pipeline de solicitação real. Em vez disso, uma única função anônima é chamada em resposta a cada solicitação HTTP.

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

Encadeie vários delegados de solicitação junto com o Use. O parâmetro next representa o próximo delegado no pipeline. Lembre-se de que você pode causar um curto-circuito no pipeline ao não chamar o parâmetro next. Normalmente, você pode executar ações antes e depois do próximo delegado, conforme o exemplo a seguir demonstra:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        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.
        });

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

Quando um delegado não transmite uma solicitação ao próximo delegado, considera-se que ele esteja causando um curto-circuito do pipeline de solicitação. O curto-circuito geralmente é desejável porque ele evita trabalho desnecessário. Por exemplo, o Middleware de Arquivo Estático pode agir com um middleware terminal, processando uma solicitação para um arquivo estático e causar curto-circuito no restante do pipeline. O middleware adicionado ao pipeline antes do middleware que finaliza o processamento adicional ainda processa o código após as instruções next.Invoke. No entanto, confira o seguinte aviso sobre a tentativa de gravar em uma resposta que já foi enviada.

Aviso

Não chame next.Invoke depois que a resposta tiver sido enviada ao cliente. Altera para HttpResponse depois de a resposta ser iniciada e lança uma exceção. Por exemplo, a configuração de cabeçalhos e o código de status lançam uma exceção. Gravar no corpo da resposta após a chamada next:

  • Pode causar uma violação do protocolo. Por exemplo, gravar mais do que o Content-Length indicado.
  • Pode corromper o formato do corpo. Por exemplo, gravar um rodapé HTML em um arquivo CSS.

HasStarted é uma dica útil para indicar se os cabeçalhos foram enviados ou o corpo foi gravado.

Delegados Run não recebem um parâmetro next. O primeiro delegado Run sempre é terminal e encerra o pipeline. Run é uma convenção. A alguns componentes de middleware podem expor os métodos Run[Middleware] que são executados no final do pipeline:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        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.
        });

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

Se você quiser ver os comentários de código traduzidos para idiomas diferentes do inglês, informe-nos neste problema de discussão do GitHub.

No exemplo anterior, o delegado Run grava "Hello from 2nd delegate." na resposta e encerra o pipeline. Se outro delegado Use ou Run for adicionado após o delegado Run, ele não será chamado.

Ordem do middleware

O diagrama a seguir mostra o pipeline de processamento de solicitação completo para aplicativos MVC do ASP.NET Core e Razor Pages. Você pode ver como, em um aplicativo típico, os middlewares existentes são ordenados e onde middlewares personalizados são adicionados. Você tem controle total sobre como reordenar os middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.

Pipeline de middleware do ASP.NET Core

O middleware do Ponto de Extremidade no diagrama anterior executa o pipeline de filtro para o tipo de aplicativo correspondente: MVC ou Razor Pages.

Pipeline de filtro do ASP.NET Core

A ordem em que os componentes do middleware são adicionados ao método Startup.Configure define a ordem em que os componentes de middleware são invocados nas solicitações e a ordem inversa para a resposta. A ordem é crítica para a segurança, o desempenho e a funcionalidade.

O seguinte método Startup.Configure adiciona componentes de middleware relacionados à segurança na ordem recomendada típica:

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?}");
    });
}

No código anterior:

  • O middleware que não é adicionado ao criar um aplicativo Web com contas de usuários individuais é comentado.
  • Nem todo middleware aparece nesta ordem exata, mas muitos aparecem. Por exemplo:
    • UseCors, UseAuthentication e UseAuthorization devem aparecer na ordem mostrada.
    • Atualmente, UseCors deve aparecer antes de UseResponseCaching devido a este bug.
    • UseRequestLocalization precisa aparecer antes de qualquer middleware que possa verificar a cultura de solicitação (por exemplo, app.UseMvcWithDefaultRoute()).

Em alguns cenários, o middleware tem uma ordenação diferente. Por exemplo, o cache e a ordenação de compactação são específicos do cenário e há várias ordenações válidas. Por exemplo:

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

Com o código anterior, a CPU pode ser economizada armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando algoritmos de compactação diferentes, como Gzip ou Brotli.

A seguinte ordenação combina arquivos estáticos para permitir o cache de arquivos estáticos compactados:

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

O método Startup.Configure a seguir adiciona componentes de middleware para cenários de aplicativo comuns:

  1. Exceção/tratamento de erro
    • Quando o aplicativo é executado no ambiente de desenvolvimento:
      • O middleware da página de exceção do desenvolvedor (UseDeveloperExceptionPage) relata erros de runtime do aplicativo.
      • O middleware da página de erro do banco de dados relata erros de runtime do banco de dados.
    • Quando o aplicativo é executado no ambiente de produção:
      • O middleware do manipulador de exceção (UseExceptionHandler) captura exceções geradas nos middlewares a seguir.
      • O middleware do protocolo HTTP Strict Transport Security (HSTS) (UseHsts) adiciona o cabeçalho Strict-Transport-Security.
  2. O middleware de redirecionamento para HTTPS (UseHttpsRedirection) redireciona as solicitações HTTP para HTTPS.
  3. O middleware de arquivo estático (UseStaticFiles) retorna arquivos estáticos e impede o processamento de novas solicitações.
  4. O middleware da política de Cookie (UseCookiePolicy) adapta o aplicativo às normas do RGPD (Regulamento Geral sobre a Proteção de Dados).
  5. Middleware de Roteamento (UseRouting) para rotear solicitações.
  6. O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes de ele ter acesso aos recursos seguros.
  7. O Middleware de Autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
  8. O middleware de sessão (UseSession) estabelece e mantém o estado de sessão. Se o aplicativo usa o estado de sessão, chame o middleware de sessão após o middleware de política de Cookie, e antes do middleware do MVC.
  9. Middleware de roteamento de ponto de extremidade (UseEndpoints com MapRazorPages) para adicionar pontos de extremidade do Razor Pages ao pipeline de solicitação.
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();
    });
}

No código de exemplo anterior, cada método de extensão de middleware é exposto em IApplicationBuilder por meio do namespace Microsoft.AspNetCore.Builder.

UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o middleware de manipulador de exceção captura todas as exceções que ocorrem em chamadas posteriores.

O Middleware de Arquivo Estático é chamado no início do pipeline para que possa controlar as solicitações e causar o curto-circuito sem passar pelos componentes restantes. O Middleware de Arquivo Estático não fornece nenhuma verificação de autorização. Todos os arquivos fornecidos pelo Middleware de Arquivo Estático, incluindo aqueles em wwwroot, estão disponíveis publicamente. Para conhecer uma abordagem para proteger arquivos estáticos, veja Arquivos estáticos no ASP.NET Core.

Se a solicitação não for controlada pelo Middleware de Arquivo Estático, ela será transmitida para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação causa curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique as solicitações, a autorização (e a rejeição) ocorre somente depois que o MVC seleciona um Razor Page específica ou um controlador MVC e uma ação.

O exemplo a seguir demonstra uma solicitação de middleware cujas solicitações de arquivos estáticos são manipuladas pelo Middleware de Arquivo Estático antes do Middleware de Compactação de Resposta. Arquivos estáticos não são compactados com este pedido de middleware. As respostas do Razor Pages pode ser compactada.

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

Para SPAs (Aplicativos de Página Única), o middleware de SPA UseSpaStaticFiles geralmente é o último no pipeline de middleware. O middleware de SPA vem por último:

  • Para permitir que todos os outros middlewares respondam primeiro às solicitações correspondentes.
  • Para permitir que SPAs com roteamento do lado do cliente sejam executados para todas as rotas que não são reconhecidas pelo aplicativo de servidor.

Para obter mais detalhes sobre SPAs, consulte os guias para os modelos de projeto React e Angular.

Ordem de Middleware de Cabeçalhos Encaminhados

Middlewares de Cabeçalhos Encaminhados devem ser executados antes de outros middlewares. Essa ordenação garantirá que o middleware conte com informações de cabeçalhos encaminhadas que podem consumir os valores de cabeçalho para processamento. Para executar o Middleware de Cabeçalhos Encaminhados após o diagnóstico e o middleware de tratamento de erros, consulte Ordem do Middleware de Cabeçalhos Encaminhados.

Ramificar o pipeline do middleware

As extensões Map são usadas como uma convenção de ramificação do pipeline. Map ramifica o pipeline de solicitação com base na correspondência do caminho da solicitação em questão. Se o caminho da solicitação iniciar com o caminho especificado, o branch será executado.

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.");
        });
    }
}

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior.

Solicitação Response
localhost:1234 Saudação do delegado diferente de Map.
localhost:1234/map1 Teste de Map 1
localhost:1234/map2 Teste de Map 2
localhost:1234/map3 Saudação do delegado diferente de Map.

Ao usar Map, os segmentos de caminho correspondentes são removidos de HttpRequest.Path e anexados a HttpRequest.PathBase para cada solicitação.

Map é compatível com aninhamento, por exemplo:

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

Map também pode ser correspondido com vários segmentos de uma 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 ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Qualquer predicado do tipo Func<HttpContext, bool> pode ser usado para mapear as solicitações para um novo branch do pipeline. No exemplo a seguir, um predicado é usado para detectar a presença de uma variável de cadeia de caracteres de consulta branch:

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.");
        });
    }
}

A tabela a seguir mostra as solicitações e as respostas de http://localhost:1234 usando o código anterior:

Solicitação Response
localhost:1234 Saudação do delegado diferente de Map.
localhost:1234/?branch=main Branch usado = principal

UseWhen também ramifica o pipeline de solicitação com base no resultado do predicado fornecido. Diferente do que acontece com MapWhen, esse branch será retornado ao pipeline principal se ele não tiver um curto-circuito ou contiver um 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);

            // 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.");
        });
    }
}

No exemplo anterior, uma resposta de "Olá do pipeline principal" é gravada para todas as solicitações. Se a solicitação incluir uma variável de cadeia de caracteres de consulta branch, seu valor será registrado antes que o pipeline principal seja retornado.

Middleware interno

O ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Ordem fornece observações sobre o posicionamento do middleware no pipeline de processamento da solicitação e sob quais condições o middleware podem encerrar o processamento da solicitação. Quando um middleware causa um curto-circuito na solicitação ao processar o pipeline e impede outros middleware downstream de processar uma solicitação, ele é chamado de middleware terminal. Para saber mais sobre curto-circuito, confira a seção Criar um pipeline de middleware com o IApplicationBuilder.

Middleware Descrição Ordem
Autenticação Fornece suporte à autenticação. Antes de HttpContext.User ser necessário. Terminal para retornos de chamada OAuth.
Autorização Fornece suporte à autorização. Imediatamente após o Middleware de Autenticação.
Política de Cookie Acompanha o consentimento dos usuários para o armazenamento de informações pessoais e impõe padrões mínimos para campos de cookie, tais como secure e SameSite. Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão e MVC (TempData).
CORS Configura o Compartilhamento de Recursos entre Origens. Antes de componentes que usam o CORS. Atualmente, UseCors deve ir antes de UseResponseCaching devido a este bug.
Diagnóstico Vários middlewares separados que fornecem uma página de exceção do desenvolvedor, tratamento de exceções, páginas de código de status e a página da Web padrão para novos aplicativos. Antes dos componentes que geram erros. Terminal para exceções ou para atender a página da Web padrão para novos aplicativos.
Cabeçalhos encaminhados Encaminha cabeçalhos como proxy para a solicitação atual. Antes dos componentes que consomem os campos atualizados. Exemplos: esquema, host, IP do cliente e método.
Verificações de integridade Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como a verificação da disponibilidade do banco de dados. Terminal, se uma solicitação corresponde a um ponto de extremidade da verificação de integridade.
Propagação de cabeçalho Propaga cabeçalhos HTTP da solicitação de entrada para solicitações de cliente HTTP de saída.
Substituição do Método HTTP Permite que uma solicitação de entrada POST substitua o método. Antes dos componentes que consomem o método atualizado.
Redirecionamento de HTTPS Redirecione todas as solicitações HTTP para HTTPS. Antes dos componentes que consomem a URL.
Segurança de Transporte Estrita de HTTP (HSTS) Middleware de aprimoramento de segurança que adiciona um cabeçalho de resposta especial. Antes das respostas serem enviadas e depois dos componentes que modificam solicitações. Exemplos: Cabeçalhos encaminhados, regravação de URL.
MVC Processa as solicitações com MVC/Razor Pages. Terminal, se uma solicitação corresponder a uma rota.
OWIN Interoperabilidade com aplicativos baseados em OWIN, em servidores e em middleware. Terminal, se o middleware OWIN processa totalmente a solicitação.
Cache de resposta Fornece suporte para as respostas em cache. Antes dos componentes que exigem armazenamento em cache. UseCORS deve vir antes de UseResponseCaching.
Compactação de resposta Fornece suporte para a compactação de respostas. Antes dos componentes que exigem compactação.
Localização de Solicitação Fornece suporte à localização. Antes dos componentes de localização importantes. Deve aparecer após o Middleware de Roteamento ao usar RouteDataRequestCultureProvider.
Roteamento de ponto de extremidade Define e restringe as rotas de solicitação. Terminal de rotas correspondentes.
SPA Manipula todas as solicitações desse ponto na cadeia de middleware retornando a página padrão do SPA (Aplicativo de Página Única) No final da cadeia, para que outro middleware que serve arquivos estáticos, ações de MVC etc., tenha precedência.
Sessão Fornece suporte para gerenciar sessões de usuário. Antes de componentes que exigem a sessão.
Arquivos estáticos Fornece suporte para servir arquivos estáticos e pesquisa no diretório. Terminal, se uma solicitação corresponde a um arquivo.
Regravação de URL Fornece suporte para regravar URLs e redirecionar solicitações. Antes dos componentes que consomem a URL.
WebSockets Habilita o protocolo WebSocket. Antes dos componentes que são necessários para aceitar solicitações de WebSocket.

Recursos adicionais