ASP.NET Middleware principal
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.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Por Rick Anderson e Steve Smith
Middleware é um software montado em um pipeline de aplicativos para lidar com solicitações e respostas. Cada componente:
- Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
- Pode executar tarefas antes e depois do próximo componente na cadeia de processos.
Os delegados de solicitação são usados para criar o pipeline de solicitação. Os delegados de solicitação lidam com cada solicitação HTTP.
Os delegados de solicitação são configurados usando os métodos de extensão Run, Mape Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado middleware in-line) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e métodos anônimos em linha são middleware, também chamados de componentes de middleware . Cada componente de middleware no pipeline de solicitação é responsável por invocar o próximo componente no pipeline ou curto-circuitar o pipeline. Quando um middleware entra em curto-circuito, é chamado de middleware de terminal porque impede que outros middleware processem a solicitação.
Migrar manipuladores e módulos HTTP para middleware do ASP.NET Core explica a diferença entre os fluxos de execução de pedidos no ASP.NET Core e no ASP.NET 4.x e fornece exemplos adicionais de middleware.
A função do middleware por tipo de aplicativo
Blazor Web Apps, Razor Pages e MVC processam solicitações de navegador no servidor com middleware. As orientações neste artigo aplicam-se a estes tipos de aplicações.
Os aplicativos Blazor WebAssembly autônomos são executados inteiramente no cliente e não processam solicitações com um pipeline de middleware. As orientações neste artigo não se aplicam a aplicativos Blazor WebAssembly autônomos.
Análise de código de middleware
ASP.NET Core inclui muitos analisadores de plataforma de compilador que inspecionam o código do aplicativo quanto à qualidade. Para obter mais informações, consulte Análise de código em aplicativos ASP.NET Core
Crie um pipeline de middleware com WebApplication
O pipeline de solicitação 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 fio da execução segue as setas pretas.
Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceções devem ser chamados no início do pipeline, para que possam interceptar exceções que ocorrem em estágios posteriores do pipeline.
O aplicativo ASP.NET Core mais simples possível configura um único delegado de solicitação que lida com 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 Use. O parâmetro next
indica o próximo delegatário no pipeline. Você pode curto-circuitar o pipeline não chamando o parâmetro next
. Geralmente, é possível realizar ações antes e depois do delegado next
, como demonstra o exemplo a seguir:
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();
Encerramento rápido do pipeline de pedidos
Quando um delegado não passa uma solicitação para o próximo delegado, ele é chamado de curto-circuito pipeline de solicitação. O curto-circuito é muitas vezes desejável porque evita trabalho desnecessário. Por exemplo, o middleware de arquivo estático pode atuar como um middleware de terminal ao processar um pedido de um arquivo estático e interromper o rest do pipeline. O middleware adicionado ao pipeline antes do middleware que encerra o processamento adicional ainda processa o código após suas instruções next.Invoke
. No entanto, veja o seguinte aviso sobre tentar escrever numa resposta que já foi enviada.
Advertência
Não ligue para next.Invoke
durante ou após a resposta ter sido enviada ao cliente. Após o início de uma HttpResponse, as alterações resultam em uma exceção. Por exemplo, cabeçalhos de configuração e um código de status lançam uma exceção após o início da resposta. Escrevendo para o corpo de resposta depois de ligar para next
:
- Pode causar uma violação de protocolo, como escrever mais do que o
Content-Length
declarado. - 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 se o corpo foi gravado.
Para obter mais informações, consulte middleware de curto-circuito após o roteamento.
Run
delegados
Os delegados Run não recebem um parâmetro next
. O primeiro Run
delegado é sempre terminal e encerra o pipeline.
Run
é uma convenção. Alguns componentes de middleware podem expor Run[Middleware]
métodos 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 quiser ver os comentários de código traduzidos para outros idiomas além do inglês, avise-nos nesta discussão do GitHub .
No exemplo anterior, o delegado Run
grava "Hello from 2nd delegate."
na resposta e, em seguida, encerra o pipeline. Se um delegado Use
ou Run
for adicionado após o delegado Run
, ele não será chamado.
Prefira o aplicativo. Usar sobrecarga que requer passar o contexto para o próximo
A não alocação aplicativo. Use método de extensão:
- Requer passar o contexto para
next
. - Poupa duas alocações internas por pedido que são necessárias quando se utiliza a outra sobrecarga.
Para mais informações, veja este problema do GitHub
Ordem de middleware
O diagrama a seguir mostra o pipeline completo de processamento de solicitações para ASP.NET aplicativos Core MVC e Razor Pages. Você pode ver como, numa aplicação típica, os middlewares existentes são ordenados e onde os middlewares personalizados são adicionados. Você tem controle total sobre como reordenar middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.
O middleware Endpoint no diagrama anterior executa o pipeline de filtro para o tipo de aplicação correspondente — MVC ou Razor Pages.
O middleware Roteamento no diagrama anterior é mostrado depois de Arquivos estáticos. Esta é a ordem que os modelos de projeto implementam chamando explicitamente app.UseRouting. Se você não chamar app.UseRouting
, o middleware Roteamento é executado no início do pipeline por padrão. Para obter mais informações, consulte roteamento .
A ordem em que os componentes de middleware são adicionados no arquivo de 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 segurança, desempenho e funcionalidade.
O código realçado a seguir no 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:
- Middleware que não é adicionado ao criar um novo aplicativo Web com contas de usuários individuais é comentado.
- Nem todo middleware aparece nessa ordem exata, mas muitos aparecem. Por exemplo:
-
UseCors
,UseAuthentication
eUseAuthorization
devem aparecer na ordem mostrada. -
UseCors
atualmente deve surgir antes doUseResponseCaching
. Este requisito é explicado em problema do GitHub dotnet/aspnetcore #23218. -
UseRequestLocalization
deve 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 APIs específicas de limite de taxa são usadas. Por exemplo, se o atributo[EnableRateLimiting]
for usado,UseRateLimiter
deverá ser chamado apósUseRouting
. Ao chamar apenas limitadores globais,UseRateLimiter
pode ser chamado antes deUseRouting
.
-
Em alguns cenários, o middleware pode ter uma ordenação diferente. Por exemplo, a ordem de cache e compactação é específica do cenário e há várias ordens válidas. Por exemplo:
app.UseResponseCaching();
app.UseResponseCompression();
Com o código anterior, o uso da CPU pode ser reduzido armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando diferentes algoritmos de compactação, como Gzip ou Brotli.
A seguinte ordenação combina arquivos estáticos para permitir o armazenamento em cache de arquivos estáticos compactados:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
O código Program.cs
a seguir adiciona componentes de middleware para cenários comuns de aplicativos:
- Tratamento de exceções/erros
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- O Middleware da Página de Exceção do Desenvolvedor (UseDeveloperExceptionPage) relata erros de tempo de execução do aplicativo.
- O middleware de página de erro de banco de dados (UseDatabaseErrorPage) relata erros de tempo de execução do banco de dados.
- Quando o aplicativo é executado no ambiente de produção:
- O Exception Handler Middleware (UseExceptionHandler) captura exceções lançadas nos seguintes middlewares.
- HTTP Strict Transport Security Protocol (HSTS) Middleware (UseHsts) adiciona o cabeçalho
Strict-Transport-Security
.
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- HTTPS Redirection Middleware (UseHttpsRedirection) redireciona solicitações HTTP para HTTPS.
- Static File Middleware (UseStaticFiles) retorna arquivos estáticos e interrompe o processamento adicional de solicitações.
- Cookie Policy Middleware (UseCookiePolicy) está em conformidade com a aplicação com os regulamentos do Regulamento Geral sobre a Proteção de Dados (RGPD) da UE.
- Middleware de roteamento (UseRouting) para rotear solicitações.
- O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes que ele tenha acesso a recursos seguros.
- O middleware de autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
- O middleware de sessão (UseSession) estabelece e mantém o estado da sessão. Se a aplicação usar o estado da sessão, chame o Middleware de Sessão depois do Middleware de Política de Cookie e antes do Middleware MVC.
- Middleware de Roteamento de Ponto Final (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 através do namespace Microsoft.AspNetCore.Builder.
UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o Exception Handler Middleware captura quaisquer exceções que ocorram em chamadas posteriores.
O Middleware de Ficheiros Estáticos é chamado no início do pipeline para que possa lidar com solicitações e interromper o processo sem passar pelos componentes restantes. O middleware de arquivo estático não fornece nenhuma verificação de autorização. Todos os arquivos servidos pelo Static File Middleware, incluindo aqueles sob wwwroot, estão disponíveis publicamente. Para obter uma abordagem para proteger arquivos estáticos, consulte Arquivos estáticos no ASP.NET Core.
Se a solicitação não for tratada pelo Middleware de Arquivo Estático, ela será passada para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação não provoca curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique as solicitações, a autorização (e rejeição) ocorre apenas depois que o MVC seleciona uma Página de Razor específica ou um controlador MVC e sua respectiva ação.
O exemplo a seguir demonstra uma ordem de middleware em que as solicitações de arquivos estáticos são tratadas pelo Static File Middleware antes do Response Compression Middleware. Os arquivos estáticos não são compactados com essa ordem de middleware. As respostas do Razor Pages podem ser compactadas.
// 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 Visão geral de aplicativos de página única (SPAs) em ASP.NET Core.
Ordem de UseCors e UseStaticFiles
A ordem para chamar UseCors
e UseStaticFiles
depende do aplicativo. Para obter mais informações, consulte ordem UseCors e UseStaticFiles
Ordem do Middleware de Cabeçalhos Reencaminhados
O Middleware de Cabeçalhos Encaminhados deve ser executado antes de outros middleware. Essa ordenação garante que o middleware que depende das informações de cabeçalhos encaminhados possa consumir os valores de cabeçalho para processamento. Para executar o middleware de cabeçalhos encaminhados após o middleware de diagnóstico e tratamento de erros, consulte ordem de middleware de cabeçalhos encaminhados.
Ramificar a linha de processamento de middleware
As extensões Map são usadas como convenção para ramificar o pipeline.
Map
ramifica o pipeline de solicitação com base nas correspondências do caminho de solicitação fornecido. Se o caminho da solicitação começar com o caminho fornecido, a ramificação será executada.
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 respostas de http://localhost:1234
usando o código anterior.
Solicitar | Resposta |
---|---|
localhost:1234 | Olá do delegado não associado ao Mapa. |
localhost:1234/map1 | Teste de Mapa 1 |
localhost:1234/map2 | Teste de Mapa 2 |
localhost:1234/map3 | Olá de um delegado que não está no mapa. |
Quando Map
é usado, os segmentos de caminho correspondentes são removidos do HttpRequest.Path
e anexados ao HttpRequest.PathBase
para cada solicitação.
Map
suporta 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 corresponder a vários segmentos ao mesmo tempo:
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 dado. Qualquer predicado do tipo Func<HttpContext, bool>
pode ser usado para mapear solicitações para uma nova ramificação do pipeline. No exemplo a seguir, um predicado é usado para detetar 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 respostas de http://localhost:1234
usando o código anterior:
Pedido | Resposta |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
UseWhen também divide o fluxo de solicitações com base no resultado do predicado dado. Ao contrário do MapWhen
, essa ramificação é unida novamente ao pipeline principal se não 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.
é escrita para todas as solicitações. Se a solicitação incluir uma variável de cadeia de caracteres de consulta branch
, o seu valor será registado antes que o pipeline principal seja reintegrado.
Middleware integrado
ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Order fornece notas sobre o posicionamento do middleware no pipeline de processamento de solicitações e em que condições o middleware pode encerrar o processamento de solicitações. Quando um middleware interrompe o pipeline de processamento de solicitações e impede que qualquer outro middleware subsequente processe uma solicitação, é chamado de middleware terminal. Para mais informações sobre curto-circuitagem, consulte a seção Criar um pipeline de middleware com WebApplication.
Middleware | Descrição | Encomenda |
---|---|---|
Autenticação | Fornece suporte à autenticação. | Antes HttpContext.User é necessário. Terminal para retornos de chamada OAuth. |
Autorização | Fornece suporte de autorização. | Imediatamente após o middleware de autenticação. |
Política Cookie | Rastreia o consentimento dos utilizadores para armazenar informações pessoais e impõe padrões mínimos para campos cookie, como secure e SameSite . |
Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão, MVC (TempData). |
CORS | Configura o compartilhamento de recursos entre origens. | Antes de componentes que usam CORS. Atualmente, UseCors deve ir antes de UseResponseCaching devido a este bug . |
Página de Exceção do Desenvolvedor | Gera uma página com informações de erro que se destina a ser usada somente no ambiente de desenvolvimento. | Antes de componentes que originam erros. Os modelos de projeto registram automaticamente esse middleware como o primeiro middleware no pipeline quando o ambiente é Desenvolvimento. |
Diagnósticos | 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 de componentes que geram erros. Terminal para gestão de exceções ou para servir a página web padrão para novas aplicações. |
Cabeçalhos encaminhados | Encaminha cabeçalhos com proxy para a solicitação atual. | Antes dos componentes que consomem os campos atualizados. Exemplos: esquema, host, IP do cliente, método. |
Verificação de Saúde | Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como verificar a disponibilidade do banco de dados. | Terminal se uma solicitação corresponder a um ponto de extremidade de verificação de integridade. |
Propagação de Cabeçalho | Propaga cabeçalhos HTTP da solicitação de entrada para as solicitações de cliente HTTP de saída. | |
Registo HTTP | Registra solicitações e respostas HTTP. | No início do pipeline de middleware. |
Substituição de Método HTTP | Permite que uma solicitação POST de entrada substitua o método. | Antes de componentes que consomem o método atualizado. |
Redirecionamento HTTPS | Redirecionar todas as solicitações HTTP para HTTPS. | Antes dos componentes que consomem a URL. |
Segurança de Transporte Rígida 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 as solicitações. Exemplos: cabeçalhos encaminhados, reescrita de URL. |
MVC | Processa solicitações com MVC/Razor Pages. | Terminal se um pedido corresponder a uma rota. |
OWIN | Interoperabilidade com aplicativos, servidores e middleware baseados em OWIN. | Terminal se o OWIN Middleware processar totalmente a solicitação. |
cache de saída | Fornece suporte para respostas de cache com base na configuração. | Antes de componentes que exigem cache.
UseRouting deve vir antes UseOutputCaching .
UseCORS deve vir antes UseOutputCaching . |
Cache de Resposta | Fornece suporte para respostas em cache. Isso requer a participação do cliente para trabalhar. Use o cache de saída para controle completo do servidor. | Antes de componentes que requerem cache.
UseCORS deve vir antes UseResponseCaching . Normalmente, não é benéfico para aplicativos de interface do usuário, como Razor Pages, porque os navegadores geralmente definem cabeçalhos de solicitação que impedem o armazenamento em cache.
A cache de saída beneficia aplicações de interface de utilizador. |
Solicitação de descompressão | Fornece suporte para solicitações de descompactação. | Antes dos componentes que leem o corpo da solicitação. |
Compressão de Resposta | Fornece suporte para compressão de respostas. | Antes de componentes que requerem compressão. |
Solicitar localização | Fornece suporte à localização. | Antes da localização de componentes sensíveis. Deve aparecer após o Routing Middleware ao usar RouteDataRequestCultureProvider. |
Tempo limite de solicitação | Fornece suporte para configurar tempos limite de solicitação, globais e por ponto de extremidade. |
UseRequestTimeouts deve vir depois de UseExceptionHandler , UseDeveloperExceptionPage e UseRouting . |
Roteamento de Endpoint | Define e restringe rotas de solicitação. | Terminal para combinação de rotas. |
SPA | Trata todas as solicitações deste ponto na cadeia de middleware, retornando a página padrão para a Aplicação de Página Única (SPA) | No final da cadeia, de modo que outro middleware, como para servir ficheiros estáticos e ações MVC, tenha precedência. |
Sessão | Fornece suporte para gerenciar sessões de usuário. | Antes de componentes que requerem Sessão. |
Arquivos Estáticos | Fornece suporte para servir arquivos estáticos e navegação em diretórios. | Terminal se uma solicitação corresponder a um arquivo. |
Reescrita de URL | Fornece suporte para reescrever URLs e redirecionar solicitações. | Antes dos componentes que consomem a URL. |
W3CLogging | Gera logs de acesso ao servidor no W3C Extended Log File Format. | No início do pipeline de middleware. |
WebSockets | Habilita o protocolo WebSockets. | Antes dos componentes necessários para processar pedidos WebSocket. |
Recursos adicionais
- Opções de tempos de vida e registo contém uma amostra completa de middleware com serviços de ciclo de vida: com escopo, transitórioe singleton.
- Escrever middleware ASP.NET Core personalizado
- Teste ASP.NET middleware Core
- Configurar o gRPC-Web no ASP.NET Core
- Migrar manipuladores e módulos HTTP para ASP.NET middleware Core
- Inicialização do aplicativo no ASP.NET Core
- Solicitar Funcionalidades no ASP.NET Core
- Ativação de middleware baseada em fábrica no ASP.NET Core
- ativação do middleware com um contêiner de terceiros no ASP.NET Core
Por Rick Anderson e Steve Smith
Middleware é um software montado em um pipeline de aplicativos para lidar com solicitações e respostas. Cada componente:
- Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
- Pode executar tarefas antes e depois do próximo componente na pipeline.
Os delegados de solicitação são usados para criar o pipeline de solicitação. Os delegados de solicitação lidam com cada solicitação HTTP.
Os delegados de solicitação são configurados usando os métodos de extensão Run, Mape Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado middleware in-line) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e métodos anônimos em linha são middleware, também chamados de componentes de middleware . Cada componente de middleware no pipeline de solicitação é responsável por invocar o próximo componente no pipeline ou curto-circuitar o pipeline. Quando um middleware entra em curto-circuito, ele é chamado de de middleware de terminal
Migrar manipuladores e módulos HTTP para ASP.NET middleware Core explica a diferença entre pipelines de solicitação no ASP.NET Core e no ASP.NET 4.x e fornece exemplos adicionais de middleware.
A função do middleware por tipo de aplicativo
Razor Pages, MVC, Blazor Servere o projeto do lado do servidor de uma solução de Blazor WebAssembly hospedada processam solicitações de navegador no servidor com middleware. As orientações neste artigo aplicam-se a estes tipos de aplicações.
Os aplicativos Blazor WebAssembly autônomos são executados inteiramente no cliente e não processam solicitações com um pipeline de middleware. As orientações neste artigo não se aplicam a aplicativos Blazor WebAssembly autônomos.
Análise de código de middleware
ASP.NET Core inclui muitos analisadores de plataforma de compilador que inspecionam o código do aplicativo quanto à qualidade. Para obter mais informações, consulte Análise de código em aplicativos ASP.NET Core
Crie uma cadeia de middleware com WebApplication
O pipeline de solicitação 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 fio da execução segue as setas pretas.
Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceções devem ser invocados no início do pipeline, para que possam intercetar exceções que ocorram em fases posteriores do pipeline.
O aplicativo ASP.NET Core mais simples possível configura um único delegado de solicitação que lida com 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 com Use. O parâmetro next
representa o próximo delegado no pipeline. Você pode curto-circuitar o pipeline não chamando o parâmetro next
. Normalmente, pode realizar ações tanto antes como depois do delegado next
, como demonstra o exemplo a seguir:
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 passa uma solicitação para o próximo delegado, ele é chamado de curto-circuito pipeline de solicitação. O curto-circuito é muitas vezes desejável porque evita trabalho desnecessário. Por exemplo, next.Invoke
. No entanto, consulte o seguinte aviso sobre tentar escrever uma resposta que já foi enviada.
Advertência
Não ligue para next.Invoke
depois que a resposta for enviada ao cliente. Alterações no HttpResponse após o início da resposta geram uma exceção. Por exemplo, definir cabeçalhos e um código de status lançam uma exceção. Escrevendo para o corpo de resposta depois de ligar para next
:
- Pode causar uma violação de protocolo. Por exemplo, escrever mais do que o indicado
Content-Length
. - Pode corromper o formato do corpo. Por exemplo, escrever um rodapé HTML em um arquivo CSS.
HasStarted é uma dica útil para indicar se os cabeçalhos já foram enviados ou se o corpo já foi escrito.
Run delegados não recebem o parâmetro next
. O primeiro Run
delegado é sempre terminal e encerra o pipeline.
Run
é uma convenção. Alguns componentes de middleware podem expor Run[Middleware]
métodos 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 gostaria de ver os comentários de código traduzidos para outros idiomas além do inglês, informe-nos nesta questão de discussão no GitHub .
No exemplo anterior, o delegado Run
escreve "Hello from 2nd delegate."
na resposta e, em seguida, termina o pipeline. Se outro delegado Use
ou Run
for adicionado após o delegado Run
, ele não será chamado.
Prefira o aplicativo. Usar sobrecarga que requer passar o contexto para o próximo
A não alocação aplicativo. Use método de extensão:
- É necessário passar o contexto para
next
. - Salva duas alocações internas por solicitação que são necessárias ao usar a outra sobrecarga.
Para obter mais informações, consulte este problema do GitHub.
Ordem de middleware
O diagrama a seguir mostra o pipeline completo de processamento de solicitações para ASP.NET aplicativos Core MVC e Razor Pages. Você pode ver como, numa aplicação típica, os middlewares existentes são ordenados e onde são adicionados os middlewares personalizados. Você tem controle total sobre como reordenar middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.
O middleware Endpoint no diagrama anterior executa o pipeline de filtro para o tipo de aplicativo correspondente — MVC ou Razor Pages.
O middleware de Roteamento é mostrado no diagrama anterior após os Arquivos Estáticos . Esta é a ordem que os modelos de projeto implementam chamando explicitamente aplicativo. UseRouting. Se você não chamar app.UseRouting
, o middleware Roteamento é executado no início do pipeline por padrão. Para obter mais informações, consulte Roteamento.
A ordem em que os componentes de middleware são adicionados no arquivo de 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 segurança, desempenho e funcionalidade.
O código realçado a seguir no 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:
- Middleware que não é adicionado ao criar um novo aplicativo Web com contas de usuários individuais é comentado.
- Nem todo middleware aparece nessa ordem exata, mas muitos aparecem. Por exemplo:
-
UseCors
,UseAuthentication
eUseAuthorization
devem aparecer na ordem mostrada. -
UseCors
atualmente deve ser colocado antes deUseResponseCaching
. Este requisito é explicado na issue do GitHub dotnet/aspnetcore #23218. -
UseRequestLocalization
deve 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 APIs específicas de limite de taxa são usadas. Por exemplo, se o atributo[EnableRateLimiting]
for usado,UseRateLimiter
deverá ser chamado apósUseRouting
. Ao chamar apenas limitadores globais,UseRateLimiter
pode ser chamado antes deUseRouting
.
-
Em alguns cenários, o middleware tem ordenamentos diferentes. Por exemplo, a ordem de cache e compactação é específica do cenário e há várias ordens válidas. Por exemplo:
app.UseResponseCaching();
app.UseResponseCompression();
Com o código anterior, o uso da CPU pode ser reduzido armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando diferentes algoritmos de compactação, como Gzip ou Brotli.
A seguinte ordenação combina arquivos estáticos para permitir o armazenamento em cache de arquivos estáticos compactados:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
O código Program.cs
a seguir adiciona componentes de middleware para cenários comuns de aplicativos:
- Tratamento de exceções/erros
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- O Middleware da Página de Exceção do Desenvolvedor (UseDeveloperExceptionPage) relata erros de tempo de execução do aplicativo.
- A página de erro do banco de dados no middleware (UseDatabaseErrorPage) relata erros durante a execução do banco de dados.
- Quando o aplicativo é executado no ambiente de produção:
- O Exception Handler Middleware (UseExceptionHandler) captura exceções lançadas nos seguintes middlewares.
- HTTP Strict Transport Security Protocol (HSTS) Middleware (UseHsts) adiciona o cabeçalho
Strict-Transport-Security
.
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- HTTPS Redirection Middleware (UseHttpsRedirection) redireciona solicitações HTTP para HTTPS.
- Static File Middleware (UseStaticFiles) retorna ficheiros estáticos e interrompe o processamento de solicitações adicionais.
- Cookie Policy Middleware (UseCookiePolicy) está em conformidade com a aplicação com os regulamentos do Regulamento Geral sobre a Proteção de Dados (RGPD) da UE.
- Middleware de roteamento (UseRouting) para rotear solicitações.
- O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes que ele tenha acesso a recursos seguros.
- O middleware de autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
- O middleware de sessão (UseSession) estabelece e mantém o estado da sessão. Se a aplicação utilizar o estado de sessão, chame o Middleware de Sessão após o Middleware de Política Cookie e antes do Middleware MVC.
- Middleware de Roteamento de Ponto Final (UseEndpoints com MapRazorPages) para adicionar endpoints das Páginas Razor 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 através do namespace Microsoft.AspNetCore.Builder.
UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o Exception Handler Middleware captura quaisquer exceções que ocorram em chamadas posteriores.
O Middleware de Arquivo Estático é invocado no início do pipeline para que possa processar as solicitações e interromper o processamento sem passar pelos componentes restantes. O middleware de arquivo estático não fornece nenhuma verificação de autorização. Todos os arquivos servidos pelo Static File Middleware, incluindo aqueles sob wwwroot, estão disponíveis publicamente. Para obter uma abordagem para proteger arquivos estáticos, consulte Arquivos estáticos no ASP.NET Core.
Se a solicitação não for tratada pelo Middleware de Arquivo Estático, ela será passada para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação não provoca curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique os pedidos, a autorização (e a rejeição) ocorre somente depois de o MVC selecionar uma página de Razor ou um controlador e uma ação MVC determinados.
O exemplo a seguir demonstra uma ordem de middleware em que as solicitações de arquivos estáticos são tratadas pelo Static File Middleware antes do Response Compression Middleware. Os arquivos estáticos não são compactados com essa ordem de middleware. As respostas do Razor Pages podem ser compactadas.
// 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 para os modelos de projeto React e Angular.
Ordem de UseCors e UseStaticFiles
A ordem para chamar UseCors
e UseStaticFiles
depende do aplicativo. Para mais informações, consulte a ordem UseCors e UseStaticFiles
Ordem do Middleware de Encaminhamento de Cabeçalhos
O middleware de cabeçalhos encaminhados deve ser executado antes de outros middleware. Essa ordenação garante que o middleware que depende das informações de cabeçalhos encaminhados possa consumir os valores de cabeçalho para processamento. Para executar o middleware de cabeçalhos encaminhados após o middleware de diagnóstico e tratamento de erros, consulte ordem de middleware de cabeçalhos encaminhados.
Ramificar o pipeline de middleware
Extensões Map são utilizadas como convenção para ramificar o pipeline.
Map
ramifica o pipeline de solicitação com base nas correspondências do caminho de solicitação fornecido. Se o caminho da solicitação começar com o caminho fornecido, a ramificação será executada.
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 respostas de http://localhost:1234
usando o código anterior.
Solicitar | Resposta |
---|---|
localhost:1234 | Olá do delegado que não pertence ao Mapa. |
localhost:1234/mapa1 | Teste de Mapa 1 |
localhost:1234/map2 | Teste de Mapa 2 |
localhost:1234/map3 | Olá do delegado de fora do Mapa. |
Quando Map
é usado, os segmentos de caminho correspondentes são removidos do HttpRequest.Path
e anexados ao HttpRequest.PathBase
para cada solicitação.
Map
suporta 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 corresponder a vários segmentos ao mesmo tempo:
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 dado. Qualquer predicado do tipo Func<HttpContext, bool>
pode ser usado para mapear solicitações para uma nova ramificação do pipeline. No exemplo a seguir, um predicado é usado para detetar 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 respostas de http://localhost:1234
usando o código anterior:
Pedido | 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 dado. Ao contrário de MapWhen
, este ramo é ligado novamente ao pipeline principal se não causar curto-circuito nem contiver um middleware 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.
é escrita para todas as solicitações. Se a solicitação incluir um parâmetro de consulta branch
, o seu valor será registado antes que o pipeline principal seja retomado.
Middleware integrado
ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Order fornece notas sobre o posicionamento do middleware no pipeline de processamento de solicitações e em que condições o middleware pode encerrar o processamento de solicitações. Quando um middleware curto-circuita o pipeline de processamento de solicitações e impede que mais middleware downstream processe uma solicitação, ele é chamado de middleware de terminal . Para obter mais informações sobre curto-circuito, consulte a seção Criar um pipeline de middleware com WebApplication.
Middleware | Descrição | Encomenda |
---|---|---|
Autenticação | Fornece suporte à autenticação. | Antes HttpContext.User é necessário. Terminal para retornos de chamada OAuth. |
Autorização | Fornece suporte de autorização. | Imediatamente após o middleware de autenticação. |
Política Cookie | Rastreia o consentimento dos utilizadores para o armazenamento de informações pessoais e impõe padrões mínimos para campos cookie, como secure e SameSite . |
Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão, MVC (TempData). |
CORS | Configura o compartilhamento de recursos entre origens. | Antes de componentes que usam CORS.
UseCors atualmente deve ir antes de UseResponseCaching devido a este bug . |
PáginaDeExceçãoDeDesenvolvedor | Gera uma página com informações de erro que se destina a ser usada somente no ambiente de desenvolvimento. | Antes de componentes que geram erros. Os modelos de projeto registram automaticamente esse middleware como o primeiro middleware no pipeline quando o ambiente é Desenvolvimento. |
Diagnósticos | 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 de componentes que geram erros. Terminal para exceções ou servindo a página da Web padrão para novos aplicativos. |
Cabeçalhos encaminhados | Redireciona cabeçalhos proxy para a solicitação atual. | Antes de os componentes consumirem os campos atualizados. Exemplos: esquema, host, IP do cliente, método. |
verificação de integridade | Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como verificar a disponibilidade do banco de dados. | Terminal se uma solicitação corresponder a um endpoint de verificação de integridade. |
Propagação de Cabeçalho | Propaga cabeçalhos HTTP da solicitação de entrada para as solicitações de cliente HTTP de saída. | |
Registo HTTP | Registra solicitações e respostas HTTP. | No início do pipeline de middleware. |
Substituição de Método HTTP | Permite que uma solicitação POST de entrada substitua o método. | Antes de componentes que consomem o método atualizado. |
Redirecionamento HTTPS | Redirecionar todas as solicitações HTTP para HTTPS. | Antes dos componentes que utilizam a URL. |
Segurança de Transporte Estrita HTTP (HSTS) | Middleware de aprimoramento de segurança que adiciona um cabeçalho de resposta especial. | Antes de as respostas serem enviadas e após os componentes que modificam as solicitações. Exemplos: cabeçalhos encaminhados, reescrita de URL. |
MVC | Processa solicitações com MVC/Razor Pages. | Terminal caso uma requisição corresponda a uma rota. |
OWIN | Interoperabilidade com aplicativos, servidores e middleware baseados em OWIN. | Terminal se o OWIN Middleware processar totalmente a solicitação. |
cache de saída | Fornece suporte para respostas de cache com base na configuração. | Antes de componentes que necessitam de cache.
UseRouting deve vir antes UseOutputCaching .
UseCORS deve vir antes UseOutputCaching . |
Cache de Resposta | Fornece suporte para respostas em cache. Isso requer a participação do cliente para trabalhar. Use o cache de saída para controle completo do servidor. | Antes de componentes que exigem cache.
UseCORS deve vir antes UseResponseCaching . Normalmente, não é benéfico para aplicativos de interface do usuário, como Razor Pages, porque os navegadores geralmente definem cabeçalhos de solicitação que impedem o armazenamento em cache.
O cache de saída beneficia os aplicativos da interface do usuário. |
Pedido de Descompressão | Fornece suporte para solicitações de descompactação. | Antes dos componentes que leem o corpo da solicitação. |
Compressão de Resposta | Fornece suporte para compressão de respostas. | Antes de componentes que requerem compressão. |
Solicitar localização | Fornece suporte à localização. | Antes da localização de componentes sensíveis. Deve aparecer após o Routing Middleware ao usar RouteDataRequestCultureProvider. |
Roteamento de Endpoint | Define e restringe rotas de solicitação. | Terminal para emparelhamento de rotas. |
SPA | Trata de todas as solicitações a partir deste ponto na cadeia de middleware, retornando a página padrão para a Aplicação de Página Única (SPA) | No final da cadeia, para que outro middleware para servir arquivos estáticos, ações MVC, etc., possa ter prioridade. |
Sessão | Fornece suporte para gerenciar sessões de usuário. | Antes de componentes que requerem Sessão. |
Arquivos Estáticos | Fornece suporte para servir arquivos estáticos e navegação em diretórios. | Terminal se uma solicitação corresponder a um arquivo. |
Reescrita de URL | Fornece suporte para reescrever URLs e redirecionar solicitações. | Antes dos componentes que consomem a URL. |
W3CLogging | Gera logs de acesso ao servidor no W3C Extended Log File Format. | No início do pipeline de middleware. |
WebSockets | Habilita o protocolo WebSockets. | Antes dos componentes necessários para aceitar solicitações WebSocket. |
Recursos adicionais
- Opções de ciclo de vida e registo contém uma amostra completa de middleware com serviços de ciclo de vida com escopo, transitório, e singleton.
- Escrever middleware ASP.NET Core personalizado
- Teste ASP.NET middleware Core
- Configurar o gRPC-Web no ASP.NET Core
- Migrar manipuladores e módulos HTTP para ASP.NET middleware Core
- Inicialização do aplicativo no ASP.NET Core
- recursos de solicitação em ASP.NET principal
- Ativação de middleware baseada em fábrica no ASP.NET Core
- ativação do middleware com um contêiner de terceiros no ASP.NET Core
Por Rick Anderson e Steve Smith
Middleware é um software montado em um pipeline de aplicativos para lidar com solicitações e respostas. Cada componente:
- Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
- Pode executar 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 lidam com cada solicitação HTTP.
Os delegados de solicitação são configurados usando métodos de extensão Run, Mape Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado middleware in-line) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e métodos anônimos em linha são middleware, também chamados de componentes de middleware . Cada componente de middleware no pipeline de solicitação é responsável por invocar o próximo componente no pipeline ou curto-circuitar o pipeline. Quando um middleware faz curto-circuito, é chamado de middleware terminal porque impede que mais middleware processe a solicitação.
Migrar manipuladores e módulos HTTP para middleware do ASP.NET Core explica a diferença entre pipelines de pedidos no ASP.NET Core e no ASP.NET 4.x e fornece exemplos adicionais de middleware.
Análise de código de middleware
ASP.NET Core inclui muitos analisadores de plataforma de compilador que inspecionam o código do aplicativo quanto à qualidade. Para obter mais informações, consulte Análise de código em aplicativos ASP.NET Core
Crie uma pipeline de middleware com WebApplication
O pipeline de solicitação 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 fio da execução segue as setas pretas.
Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceções devem ser chamados no início do pipeline, para que possam capturar exceções que ocorrem em estágios posteriores do pipeline.
O aplicativo ASP.NET Core mais simples possível configura um único delegado de solicitação que lida com 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 Use. O parâmetro next
representa o próximo delegado no pipeline. Você pode curto-circuitar o pipeline não chamando o parâmetro next
. Normalmente, você pode executar ações antes e depois do next
delegado, como demonstra o exemplo a seguir:
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 passa uma solicitação para o próximo delegado, ele é chamado de curto-circuito pipeline de solicitação. O curto-circuito é frequentemente desejável porque evita trabalho desnecessário. Por exemplo, next.Invoke
. No entanto, consulte o seguinte aviso sobre tentar escrever em uma resposta que já foi enviada.
Advertência
Não ligue para next.Invoke
depois que a resposta for enviada ao cliente. Alterações no HttpResponse após o início da resposta geram uma exceção. Por exemplo, definir cabeçalhos e um código de status lança uma exceção. Escrevendo para o corpo de resposta depois de ligar para next
:
- Pode causar uma violação de protocolo. Por exemplo, escrever mais do que o que foi indicado
Content-Length
. - Pode corromper o formato do corpo. Por exemplo, escrever um rodapé HTML em um arquivo CSS.
HasStarted é uma sugestão útil para indicar se os cabeçalhos foram enviados ou se o corpo foi escrito.
Run delegados não recebem o parâmetro next
. O primeiro Run
delegate é sempre terminal e encerra o pipeline.
Run
é uma convenção. Alguns componentes de middleware podem expor 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ê gostaria de ver os comentários de código traduzidos para outros idiomas além do inglês, informe-nos em este problema de discussão do GitHub.
No exemplo anterior, o delegado Run
grava "Hello from 2nd delegate."
na resposta e depois encerra o pipeline. Se um delegado Use
ou Run
for adicionado após o delegado Run
, esse delegado não será chamado.
Prefira o aplicativo. Usar sobrecarga que requer passar o contexto para o próximo
A não alocação aplicativo. Use método de extensão:
- Requer passar o contexto para
next
. - Evita duas alocações internas por cada pedido que são necessárias ao utilizar a outra sobrecarga.
Para obter mais informações, consulte este problema do GitHub.
Ordem de middleware
O diagrama a seguir mostra o pipeline completo de processamento de solicitações para ASP.NET aplicativos Core MVC e Razor Pages. Você pode ver como, numa aplicação típica, os middlewares existentes são ordenados e onde os middlewares personalizados são adicionados. Você tem controle total sobre como reordenar middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.
O middleware Endpoint no diagrama anterior executa o pipeline de filtro para o tipo de aplicativo correspondente — MVC ou Razor Pages.
O middleware Roteamento no diagrama anterior é mostrado após Arquivos Estáticos. Esta é a ordem que os modelos de projeto implementam chamando explicitamente app.UseRouting. Se você não chamar app.UseRouting
, o middleware Roteamento é executado no início do pipeline por padrão. Para obter mais informações, consulte Enrutamento.
A ordem em que os componentes de middleware são adicionados no arquivo de 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 segurança, desempenho e funcionalidade.
O código realçado a seguir no 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:
- Middleware que não é adicionado ao criar um novo aplicativo Web com contas de usuários individuais é comentado.
- Nem todo middleware aparece nessa ordem exata, mas muitos aparecem. Por exemplo:
-
UseCors
,UseAuthentication
eUseAuthorization
devem aparecer na ordem mostrada. -
UseCors
atualmente deve comparecer antes deUseResponseCaching
. Este requisito é explicado em problema do GitHub dotnet/aspnetcore #23218. -
UseRequestLocalization
deve aparecer antes de qualquer middleware que possa verificar a cultura de solicitação (por exemplo,app.UseMvcWithDefaultRoute()
).
-
Em alguns cenários, a middleware tem uma ordem diferente. Por exemplo, a ordem de cache e compactação é específica do cenário e há várias ordens válidas. Por exemplo:
app.UseResponseCaching();
app.UseResponseCompression();
Com o código anterior, o uso da CPU pode ser reduzido armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando diferentes algoritmos de compactação, como Gzip ou Brotli.
A seguinte ordenação combina arquivos estáticos para permitir o armazenamento em cache de arquivos estáticos compactados:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
O código Program.cs
a seguir adiciona componentes de middleware para cenários comuns de aplicativos:
- Tratamento de exceções/erros
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- O Middleware da Página de Exceção do Desenvolvedor (UseDeveloperExceptionPage) relata erros de execução da aplicação.
- O middleware da página de erro do banco de dados (UseDatabaseErrorPage) reporta erros em tempo de execução do banco de dados.
- Quando o aplicativo é executado no ambiente de produção:
- O Exception Handler Middleware (UseExceptionHandler) captura exceções lançadas nos seguintes middlewares.
- HTTP Strict Transport Security Protocol (HSTS) Middleware (UseHsts) adiciona o cabeçalho
Strict-Transport-Security
.
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- HTTPS Redirection Middleware (UseHttpsRedirection) redireciona solicitações HTTP para HTTPS.
- Static File Middleware (UseStaticFiles) retorna ficheiros estáticos e interrompe o processamento adicional de solicitações.
- Cookie Policy Middleware (UseCookiePolicy) está em conformidade com a aplicação com os regulamentos do Regulamento Geral sobre a Proteção de Dados (RGPD) da UE.
- Middleware de roteamento (UseRouting) para rotear solicitações.
- O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes que ele tenha acesso a recursos seguros.
- O middleware de autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
- O middleware de sessão (UseSession) estabelece e mantém o estado da sessão. Se a aplicação usar o estado da sessão, chame o Middleware de Sessão depois do Middleware de Política de Cookie e antes do Middleware MVC.
- Middleware de Endpoint Routing (UseEndpoints com MapRazorPages) para adicionar endpoints de 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 através do namespace Microsoft.AspNetCore.Builder.
UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o Exception Handler Middleware captura quaisquer exceções que ocorram em chamadas posteriores.
O Middleware de Arquivo Estático é chamado no início do pipeline para que possa lidar com solicitações e 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 servidos pelo Static File Middleware, incluindo aqueles sob wwwroot, estão disponíveis publicamente. Para obter uma abordagem para proteger arquivos estáticos, consulte Arquivos estáticos no ASP.NET Core.
Se a solicitação não for tratada pelo Middleware de Arquivo Estático, ela será passada para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação não provoca curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique pedidos, a autorização (e a rejeição) ocorre apenas depois que o MVC seleciona uma página específica de Razor ou um controlador e ação específicos do MVC.
O exemplo a seguir demonstra uma ordem de middleware em que as solicitações de arquivos estáticos são tratadas pelo Static File Middleware antes do Response Compression Middleware. Os arquivos estáticos não são compactados com essa ordem de middleware. As respostas do Razor Pages podem ser compactadas.
// 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 para os modelos de projeto React e Angular.
A ordem de UseCors e UseStaticFiles
A ordem para chamar UseCors
e UseStaticFiles
depende do aplicativo. Para obter mais informações, consulte ordem UseCors e UseStaticFiles
Ordem de middleware de cabeçalhos encaminhados
O Middleware dos Cabeçalhos Encaminhados deve ser executado antes de outros middlewares. Essa ordenação garante que o middleware que depende das informações de cabeçalhos encaminhados possa consumir os valores de cabeçalho para processamento. Para executar o middleware de cabeçalhos encaminhados após o middleware de diagnóstico e tratamento de erros, consulte a ordem do middleware de cabeçalhos encaminhados .
Ramifique o pipeline de middleware
As extensões Map são utilizadas como uma convenção para ramificar o pipeline.
Map
segmenta o pipeline de solicitação com base nas correspondências do caminho de pedido especificado. Se o caminho da solicitação começar com o caminho fornecido, a ramificação será executada.
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 respostas de http://localhost:1234
usando o código anterior.
Pedido | Resposta |
---|---|
localhost:1234 | Olá do delegado fora do Mapa. |
localhost:1234/mapa1 | Teste de Mapa 1 |
localhost:1234/map2 | Teste de Mapa 2 |
localhost:1234/map3 | Olá do delegado fora do Mapa. |
Quando Map
é usado, os segmentos de caminho correspondentes são removidos do HttpRequest.Path
e anexados ao HttpRequest.PathBase
para cada solicitação.
Map
suporta 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 corresponder a vários segmentos ao mesmo tempo:
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 dado. Qualquer predicado do tipo Func<HttpContext, bool>
pode ser usado para mapear solicitações para uma nova ramificação do pipeline. No exemplo a seguir, um predicado é usado para detetar 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 respostas de http://localhost:1234
usando o código anterior:
Solicitar | 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 dado. Ao contrário do MapWhen
, essa ramificação é unida novamente ao pipeline principal se não houver 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.
é escrita para todas as solicitações. Se a solicitação incluir uma variável de cadeia de caracteres de consulta branch
, o seu valor será registado antes de ser retornado ao fluxo principal.
Middleware integrado
ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Order fornece notas sobre o posicionamento do middleware no pipeline de processamento de solicitações e em que condições o middleware pode encerrar o processamento de solicitações. Quando um middleware interrompe o pipeline de processamento de solicitações e impede que outros middlewares na sequência processem uma solicitação, é chamado de middleware terminal . Para obter mais informações sobre curto-circuitagem, consulte a seção Criar um pipeline de middleware com WebApplication.
Middleware | Descrição | Encomenda |
---|---|---|
Autenticação | Fornece suporte à autenticação. | Antes HttpContext.User é necessário. Terminal para chamadas de retorno OAuth. |
Autorização | Fornece suporte de autorização. | Imediatamente após o middleware de autenticação. |
Política Cookie | Rastreia o consentimento dos utilizadores para armazenar informações pessoais e impõe normas mínimas para campos cookie, como secure e SameSite . |
Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão, MVC (TempData). |
CORS | Configura o compartilhamento de recursos entre origens. | Antes de componentes que usam CORS.
UseCors atualmente deve ir antes de UseResponseCaching devido a este bug. |
PáginaDeExceçãoDoDesenvolvedor DeveloperExceptionPage | Gera uma página com informações de erro que se destina a ser usada somente no ambiente de desenvolvimento. | Antes de componentes que geram erros. Os modelos de projeto registram automaticamente esse middleware como o primeiro middleware no pipeline quando o ambiente é 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 gerir exceções e servir a página Web padrão para novas aplicações. |
Cabeçalhos encaminhados | Encaminha cabeçalhos com proxy para a solicitação atual. | Antes dos componentes que consomem os campos atualizados. Exemplos: esquema, host, IP do cliente, método. |
de verificação de saúde | Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como verificar a disponibilidade do banco de dados. | Terminal se uma solicitação corresponder a um ponto de extremidade de verificação de integridade. |
Propagação de Cabeçalhos | Propaga cabeçalhos HTTP da solicitação de entrada para as solicitações de cliente HTTP de saída. | |
Registo HTTP | Registra solicitações e respostas HTTP. | No início do pipeline de middleware. |
Substituição de Método HTTP | Permite que uma solicitação POST de entrada substitua o método. | Antes dos componentes que utilizam o método atualizado. |
Redirecionamento HTTPS | Redirecionar todas as solicitações HTTP para HTTPS. | Antes dos componentes que consomem a URL. |
Segurança Rigorosa de Transporte HTTP (HSTS) | Middleware de aprimoramento de segurança que adiciona um cabeçalho de resposta especial. | Antes de as respostas serem enviadas e após os componentes que modificam as requisições. Exemplos: cabeçalhos encaminhados, reescrita de URL. |
MVC | Processa solicitações com MVC/Razor Pages. | Terminal se um pedido corresponder a uma rota. |
OWIN | Interoperabilidade com aplicativos, servidores e middleware baseados em OWIN. | Terminal caso o Middleware OWIN tenha processado totalmente a solicitação. |
Solicitar descompressão | Fornece suporte para solicitações de descompactação. | Antes dos componentes responsáveis por ler o corpo da solicitação. |
Cache de Resposta | Fornece suporte para respostas em cache. | Antes de componentes que exigem armazenamento em cache.
UseCORS deve vir antes UseResponseCaching . |
Compressão de Resposta | Fornece suporte para compressão de respostas. | Antes de componentes que requerem compressão. |
Solicitar localização | Fornece suporte à localização. | Antes da localização de componentes sensíveis. Deve aparecer após o Routing Middleware ao usar RouteDataRequestCultureProvider. |
de roteamento de pontos finais | Define e restringe rotas de solicitação. | Terminal para correspondência de rotas. |
SPA | Manipula todas as solicitações deste ponto na cadeia de middleware, retornando a página padrão para a Aplicação de Página Única (SPA) | No final da cadeia, de modo que o outro middleware que serve ficheiros estáticos, ações MVC, etc., tenha precedência. |
Sessão | Fornece suporte para gerenciar sessões de usuário. | Antes dos componentes que requerem sessão. |
arquivos estáticos | Fornece suporte para servir arquivos estáticos e navegação em diretórios. | Terminal se uma solicitação corresponder a um arquivo. |
Reescrita de URL | Fornece suporte para reescrever URLs e redirecionar solicitações. | Antes dos componentes que consomem a URL. |
W3CLogging | Gera logs de acesso ao servidor no W3C Extended Log File Format. | No início do pipeline de middleware. |
WebSockets | Habilita o protocolo WebSockets. | Antes dos componentes necessários para aceitar solicitações WebSocket. |
Recursos adicionais
- Opções de ciclo de vida e registo contém uma amostra completa de middleware com serviços de ciclo de vida com escopo, transitórioe singleton.
- Escreva middleware ASP.NET Core personalizado
- Teste ASP.NET middleware Core
- Configurar o gRPC-Web no ASP.NET Core
- Migrar manipuladores e módulos HTTP para ASP.NET middleware Core
- Inicialização do aplicativo no ASP.NET Core
- Funcionalidades de Requisição no ASP.NET Core
- Ativação de middleware baseada em fábrica no ASP.NET Core
- Activação do middleware com um contentor de terceiros no ASP.NET Core
Por Rick Anderson e Steve Smith
Middleware é um software montado em um pipeline de aplicativos para lidar com solicitações e respostas. Cada componente:
- Escolhe se deseja passar a solicitação para o próximo componente no pipeline.
- Pode executar trabalho antes e depois do próximo componente na canalização.
Os delegados de solicitação são usados para criar o pipeline de pedidos. Os delegados de solicitação lidam com cada solicitação HTTP.
Os delegados de solicitação são configurados usando métodos de extensão Run, Mape Use. Um delegado de solicitação individual pode ser especificado em linha como um método anônimo (chamado middleware in-line) ou pode ser definido em uma classe reutilizável. Essas classes reutilizáveis e métodos anônimos em linha são middleware, também chamados de componentes de middleware . Cada componente de middleware no fluxo de solicitação é responsável por invocar o próximo componente no fluxo ou interromper o fluxo. Quando um middleware corta o circuito, é chamado de middleware terminal porque impede que outros middleware processem a solicitação.
Migrar manipuladores e módulos HTTP para Middleware do ASP.NET Core explica a diferença entre os pipelines de solicitação no ASP.NET Core e no ASP.NET 4.x, e fornece exemplos adicionais de middleware.
Criar um pipeline de middleware com IApplicationBuilder
O pipeline de solicitação 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 fio da execução segue as setas pretas.
Cada delegado pode executar operações antes e depois do próximo delegado. Os delegados de tratamento de exceções devem ser chamados no início do pipeline, para que possam interceptar exceções que ocorrem em estágios posteriores do pipeline.
O aplicativo ASP.NET Core mais simples possível configura um único delegado de solicitação que lida com 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 Use. O parâmetro next
representa o próximo delegado no pipeline. Você pode curto-circuitar o pipeline não chamando o próximo parâmetro. Normalmente, você pode executar ações antes e depois do próximo delegado, como demonstra o exemplo a seguir:
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 passa uma solicitação para o próximo delegado, ele é chamado de curto-circuito pipeline de solicitação. O curto-circuito é muitas vezes desejável porque evita trabalho desnecessário. Por exemplo, o next.Invoke
. No entanto, consulte o seguinte aviso sobre tentar gravar numa resposta que já foi enviada.
Advertência
Não ligue para next.Invoke
depois que a resposta for enviada ao cliente. Alterações no HttpResponse após o início da resposta geram uma exceção. Por exemplo, definir cabeçalhos e um código de estado lança uma exceção. Escrevendo para o corpo de resposta depois de ligar para next
:
- Pode causar uma violação de protocolo. Por exemplo, escrever mais do que o indicado
Content-Length
. - Pode corromper o formato do corpo do documento. Por exemplo, escrever um rodapé HTML em um arquivo CSS.
HasStarted é uma dica útil para indicar se os cabeçalhos foram enviados ou se o corpo foi escrito.
Run delegates não recebem o parâmetro next
. O primeiro delegado Run
é sempre terminal e encerra o pipeline.
Run
é uma convenção. Alguns componentes de middleware podem expor os Run[Middleware]
métodos 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.");
});
}
}
Caso gostaria de ver os comentários de código traduzidos para outros idiomas além do inglês, diz-nos neste tópico de discussão no GitHub
No exemplo anterior, o delegado Run
grava "Hello from 2nd delegate."
na resposta e, em seguida, encerra o pipeline. Se outro Use
ou Run
delegado for adicionado após o Run
delegado, ele não será chamado.
Ordem de middleware
O diagrama a seguir mostra o pipeline completo de processamento de solicitações para ASP.NET aplicativos Core MVC e Razor Pages. Pode ver como, num aplicativo típico, os middlewares existentes são ordenados e onde os middlewares personalizados são adicionados. Você tem controle total sobre como reordenar middlewares existentes ou injetar novos middlewares personalizados conforme necessário para seus cenários.
O middleware Endpoint no diagrama anterior executa o pipeline de filtro para o tipo de aplicativo correspondente — MVC ou Razor Pages.
A ordem em que os componentes de middleware são adicionados no método Startup.Configure
define a ordem na qual os componentes de middleware são invocados em solicitações e a ordem inversa para a resposta. A ordem é crítica para segurança, desempenho e 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:
- Middleware que não é adicionado ao criar uma nova aplicação Web com contas de utilizadores individuais é desativado com comentário.
- Nem todo middleware aparece nessa ordem exata, mas muitos aparecem. Por exemplo:
-
UseCors
,UseAuthentication
eUseAuthorization
devem aparecer na ordem mostrada. -
UseCors
atualmente deve aparecer antes deUseResponseCaching
devido a este bug . -
UseRequestLocalization
deve 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 ordem diferente. Por exemplo, a ordem de cache e compactação é específica do cenário, e há vários pedidos válidos. Por exemplo:
app.UseResponseCaching();
app.UseResponseCompression();
Com o código anterior, a CPU poderia ser salva armazenando em cache a resposta compactada, mas você pode acabar armazenando em cache várias representações de um recurso usando diferentes algoritmos de compactação, como Gzip ou Brotli.
A seguinte ordenação combina arquivos estáticos para permitir o armazenamento em 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 comuns de aplicativos:
- Tratamento de exceções/erros
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- O Middleware da Página de Exceção do Desenvolvedor (UseDeveloperExceptionPage) reporta erros de tempo de execução da aplicação.
- Página de Erro de Banco de Dados O Middleware relata erros de tempo de execução do banco de dados.
- Quando o aplicativo é executado no ambiente de produção:
- O Exception Handler Middleware (UseExceptionHandler) captura exceções lançadas nos seguintes middlewares.
- HTTP Strict Transport Security Protocol (HSTS) Middleware (UseHsts) adiciona o cabeçalho
Strict-Transport-Security
.
- Quando o aplicativo é executado no ambiente de desenvolvimento:
- HTTPS Redirection Middleware (UseHttpsRedirection) redireciona solicitações HTTP para HTTPS.
- Static File Middleware (UseStaticFiles) retorna ficheiros estáticos e interrompe o processamento adicional das solicitações.
- Cookie Policy Middleware (UseCookiePolicy) está em conformidade com a aplicação com os regulamentos do Regulamento Geral sobre a Proteção de Dados (RGPD) da UE.
- Middleware de roteamento (UseRouting) para rotear solicitações.
- O middleware de autenticação (UseAuthentication) tenta autenticar o usuário antes que ele tenha acesso a recursos seguros.
- O middleware de autorização (UseAuthorization) autoriza um usuário a acessar recursos seguros.
- O middleware de sessão (UseSession) estabelece e mantém o estado da sessão. Se a aplicação utilizar o estado de sessão, invoque o Middleware de Sessão depois do Middleware de Política de Cookie e antes do Middleware MVC.
- Middleware de Roteamento de Ponto Final (UseEndpoints com MapRazorPages) para adicionar os endpoints de Razor Pages ao pipeline de pedidos.
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 através do namespace Microsoft.AspNetCore.Builder.
UseExceptionHandler é o primeiro componente de middleware adicionado ao pipeline. Portanto, o Exception Handler Middleware captura quaisquer exceções que ocorram em chamadas posteriores.
O Middleware de Arquivo Estático é chamado no início do pipeline para que possa lidar com solicitações e 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 servidos pelo Static File Middleware, incluindo aqueles sob wwwroot, estão disponíveis publicamente. Para obter uma abordagem para proteger arquivos estáticos, consulte Arquivos estáticos no ASP.NET Core.
Se a solicitação não for tratada pelo Middleware de Arquivo Estático, ela será passada para o Middleware de Autenticação (UseAuthentication), que executa a autenticação. A autenticação não provoca curto-circuito em solicitações não autenticadas. Embora o Middleware de Autenticação autentique solicitações, a autorização (e a rejeição) ocorre apenas após o MVC selecionar uma página específica identificada por Razor ou um controlador e uma ação de MVC específicos.
O exemplo a seguir demonstra uma ordem de middleware em que as solicitações de arquivos estáticos são tratadas pelo Static File Middleware antes do Response Compression Middleware. Os arquivos estáticos não são compactados com essa ordem de middleware. As respostas do Razor Pages podem ser compactadas.
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 aplicações de página única (SPAs), o middleware SPA UseSpaStaticFiles geralmente é colocado por último no pipeline de middleware. O middleware 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 do servidor.
Para obter mais detalhes sobre SPAs, consulte os guias dos modelos de projeto React e Angular.
Ordem de middleware de cabeçalhos encaminhados
O Middleware de Cabeçalhos Encaminhados deve ser executado antes dos outros middleware. Essa ordenação garante que o middleware que depende das informações de cabeçalhos encaminhados possa consumir os valores de cabeçalho para processamento. Para correr o middleware de cabeçalhos encaminhados após o middleware de diagnóstico e tratamento de erros, consulte ordem do middleware de cabeçalhos encaminhados.
Ramificar o pipeline de middleware
Extensões Map são usadas como uma convenção para ramificar o pipeline.
Map
ramifica o pipeline de solicitação com base nas correspondências do caminho de solicitação fornecido. Se o caminho da solicitação começar com o caminho fornecido, a ramificação será executada.
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 respostas de http://localhost:1234
usando o código anterior.
Solicitar | Resposta |
---|---|
localhost:1234 | Olá do delegado que não pertence ao Mapa. |
localhost:1234/mapa1 | Teste de Mapa 1 |
localhost:1234/map2 | Teste de Mapa 2 |
localhost:1234/map3 | Olá do delegado fora do Mapa. |
Quando Map
é usado, os segmentos de caminho correspondentes são removidos do HttpRequest.Path
e anexados ao HttpRequest.PathBase
para cada solicitação.
Map
suporta 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 corresponder a vários segmentos ao mesmo tempo:
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 dado. Qualquer predicado do tipo Func<HttpContext, bool>
pode ser usado para mapear solicitações para uma nova ramificação do pipeline. No exemplo a seguir, um predicado é usado para detetar 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 respostas de http://localhost:1234
usando o código anterior:
Pedido | Resposta |
---|---|
localhost:1234 | Olá do representante não-Mapa. |
localhost:1234/?branch=main | Ramificação utilizada = principal |
UseWhen também ramifica o pipeline de solicitação com base no resultado do predicado dado. Ao contrário do MapWhen
, essa ramificação é unida novamente ao pipeline principal se não houver 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." é dada para todas as solicitações. Se a solicitação incluir um parâmetro de consulta branch
, o seu valor será registado antes que o pipeline principal seja retomado.
Middleware integrado
ASP.NET Core é fornecido com os seguintes componentes de middleware. A coluna Order fornece notas sobre o posicionamento do middleware no pipeline de processamento de solicitações e em que condições o middleware pode encerrar o processamento de solicitações. Quando um middleware interrompe o pipeline de processamento de solicitações e impede que outros middleware posteriores processem uma solicitação, é chamado de middleware terminal . Para obter mais informações sobre curto-circuito, consulte a seção Criar um pipeline de middleware com IApplicationBuilder.
Middleware | Descrição | Encomenda |
---|---|---|
autenticação | Fornece suporte à autenticação. | Antes de HttpContext.User ser necessário. Terminal para retornos de chamada OAuth. |
Autorização | Fornece suporte de autorização. | Imediatamente após o middleware de autenticação. |
Política Cookie | Rastreia o consentimento dos utilizadores para armazenar informações pessoais e impõe padrões mínimos para campos cookie, como secure e SameSite . |
Antes do middleware que emite cookies. Exemplos: Autenticação, Sessão, MVC (TempData). |
CORS | Configura o compartilhamento de recursos entre origens. | Antes de componentes que usam CORS.
UseCors atualmente 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 de componentes que geram erros. Terminal para tratamento de exceções ou fornecer a página web padrão para novas aplicações. |
Cabeçalhos encaminhados | Encaminha cabeçalhos com proxy para a solicitação atual. | Antes dos componentes que consomem os campos atualizados. Exemplos: esquema, host, IP do cliente, método. |
Verificação de Saúde | Verifica a integridade de um aplicativo ASP.NET Core e suas dependências, como verificar a disponibilidade do banco de dados. | Terminal se um pedido corresponder a um endpoint de verificação de saúde. |
Propagação de Cabeçalho | Propaga cabeçalhos HTTP da solicitação de entrada para as solicitações de cliente HTTP de saída. | |
Substituição de Método HTTP | Permite que uma solicitação POST de entrada substitua o método. | Antes dos componentes que utilizam o método atualizado. |
Redirecionamento HTTPS | Redirecionar todas as solicitações HTTP para HTTPS. | Antes dos componentes que consomem a URL. |
Segurança de Transporte Rígida 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 as solicitações. Exemplos: cabeçalhos encaminhados, reescrita de URL. |
MVC | Processa solicitações com MVC/Razor Pages. | Terminal se um pedido corresponder a uma rota. |
OWIN | Interoperabilidade com aplicativos, servidores e middleware baseados em OWIN. | Terminal se o OWIN Middleware processar totalmente a solicitação. |
Cache de Resposta | Fornece suporte para respostas em cache. | Antes dos componentes que necessitam de cache.
UseCORS deve vir antes UseResponseCaching . |
Compressão de Resposta | Fornece suporte para compressão de respostas. | Antes de componentes que requerem compressão. |
Solicitar localização | Fornece suporte à localização. | Antes da localização de componentes sensíveis. Deve aparecer após o Routing Middleware ao usar RouteDataRequestCultureProvider. |
Roteamento de Endpoint | Define e restringe rotas de solicitação. | Terminal para correspondência de itinerários. |
SPA | Processa todas as solicitações a partir deste ponto na cadeia de middleware ao devolver a página padrão para a Aplicação de Página Única (SPA) | No final da cadeia, para que o outro middleware que serve arquivos estáticos, ações MVC, etc., tenha precedência. |
Sessão | Fornece suporte para gerenciar sessões de usuário. | Antes de componentes que requerem sessão. |
arquivos estáticos | Fornece suporte para servir arquivos estáticos e navegação em diretórios. | Terminal se uma solicitação corresponder a um arquivo. |
Reescrita de URL | Fornece suporte para reescrever URLs e redirecionar solicitações. | Antes dos componentes que consomem a URL. |
WebSockets | Habilita o protocolo WebSocket. | Antes dos componentes necessários para aceitar solicitações WebSocket. |
Recursos adicionais
- Opções de tempo de vida e registo contém uma amostra completa de middleware com serviços de tempo de vida com escopo, transitórioe singleton.
- Escreva middleware personalizado ASP.NET Core
- Teste ASP.NET middleware Core
- Migrar manipuladores e módulos HTTP para ASP.NET middleware Core
- Inicialização do aplicativo no ASP.NET Core
- Pedidos de funcionalidades em ASP.NET Core
- Ativação de middleware baseada em fábrica no ASP.NET Core
- Ativação do middleware com um contentor de terceiros no ASP.NET Core