Compartilhar via


Middleware do ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 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 a versão atual, consulte a versão .NET 9 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.

A função do middleware por tipo de aplicativo

Blazor Web Apps, Razor Pages e MVC processam solicitações do navegador no servidor com middleware. As diretrizes deste artigo se aplicam a esses tipos de aplicativos.

Aplicativos Blazor WebAssembly autônomos são executados inteiramente no cliente e não processam solicitações com um pipeline de middleware. As diretrizes deste artigo não se aplicam a aplicativos Blazor WebAssembly autônomos.

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 atuar como um middleware final processando uma solicitação por um arquivo estático e interrompendo o 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 Resposta
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 Resposta
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 não tiver 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
Antifalsificação Fornece suporte à proteção contra falsificação de solicitações. Após autenticação e autorização, antes dos endpoints.
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.