Compor antes de receber
WebApplication.CreateBuilder(args) prepara configuração, serviços e o host. Build cria a aplicação com essa composição. MapGet regista um endpoint para um método e caminho; o handler só participa quando um pedido chega e o pipeline permite a sua execução. app.Run() mantém o host em execução até ao encerramento.
O modelo desta aula usa ASP.NET Core 10.0 sobre .NET 10 LTS. Já deves reconhecer lambdas, await e tipos de C#. A decisão nova é onde cada função entra no percurso HTTP. Um registo de endpoint não equivale a uma chamada imediata nem obriga middleware anterior a continuar.
Entrada, continuação e retorno
Middleware pode executar trabalho antes de chamar next, aguardar a continuação e executar trabalho depois. Com A seguido de B e de um terminal T, o percurso normal é A-before, B-before, T, B-after, A-after. A sequência de retorno resulta da composição das chamadas; não é uma segunda passagem na ordem de registo.
Esta previsão assume que todos os componentes continuam normalmente. Uma exceção pode impedir o código depois de await next. Uma interceção que não chama next impede os componentes seguintes, mas pode devolver controlo aos anteriores. Para raciocinar, assinala cada chamada e o seu retorno, em vez de ordenar apenas as linhas do ficheiro.
Delegados terminais e rotas
app.Run(async context => ...) regista um delegado terminal, sem parâmetro next. É diferente de app.Run(), que executa o host. Se o delegado terminal responde a todos os pedidos antes da execução dos endpoints, um MapGet existente não será suficiente para recuperar o handler. Dar-lhe outro nome também não introduz continuação.
WebApplication pode acrescentar routing e execução de endpoints automaticamente quando há endpoints registados. A ausência de UseRouting explícito, por si só, não demonstra uma falha. Para um estado simples do processo, um endpoint num caminho próprio permite conservar os outros contratos. Esse estado não deve ser confundido com prontidão das dependências.
A posição protege uma fronteira
O tratamento de exceções deve envolver os componentes cujas falhas precisa de tratar. Se M lança antes de chamar next e o handler está depois de M, o pedido ainda não entrou nesse handler. Acrescentar outra instância no fim não envolve retroativamente a exceção. A fronteira relevante é a pilha de chamadas efetivamente percorrida.
Quando se usa uma composição explícita, routing seleciona o endpoint, autenticação estabelece a identidade e autorização aplica as condições antes da execução. Configuração e serviços continuam necessários. A ordem não fornece, por si só, uma política de acesso nem demonstra que todas as rotas a exigem.
Observar uma resposta já iniciada
Um cabeçalho fixo conhecido à entrada pode ser definido antes de next, enquanto a resposta ainda não começou. Depois de HasStarted ser verdadeiro, os cabeçalhos e o estado já não são livremente alteráveis. Ler o estado para observação é diferente de tentar substituir o contrato enviado.
Acrescentar texto de diagnóstico ao fim de JSON pode invalidar o documento e contrariar Content-Length. O cliente não transforma esses bytes num cabeçalho. No exemplo, os logs recebem apenas etiquetas e estado; o corpo mantém a sua finalidade. Uma transformação real exige controlo explícito da representação e do momento de emissão.
Ramos e caminhos efetivos
Um ramo de middleware Map("/ops", ...) retira o prefixo correspondente de Path e acrescenta-o a PathBase. Para /ops/status, sem PathBase anterior, o interior observa Path=/status e PathBase=/ops. A leitura de caminhos deve indicar em que ponto do pipeline foi feita, para não confundir prefixos de montagem com a rota interna.
UseWhen pode voltar ao pipeline principal se o ramo não terminar o pedido e chamar next. Um ramo que termina já não tem essa continuação. Não confundas o Map de middleware com MapGet: um compõe um ramo por caminho, o outro regista um endpoint HTTP.
Leitura guiada e decisão
No exemplo, fixa GET /demo e percorre A, routing, B e a execução do endpoint. Anota o texto esperado, o cabeçalho fixo e a ordem dos quatro registos. Agora imagina um Run terminal entre A e B: B e o endpoint deixam de ser alcançados, mesmo que a resposta desse terminal tenha estado 200.
Num segundo cenário, move a atribuição do cabeçalho para depois de next e assume HasStarted=true. A correção deve recuperar o momento em que o valor ainda pode ser escrito. As avaliações pedem escolher interpretações e decisões a partir destas premissas; não exigem executar C# nem iniciar um servidor.
// Program.cs: ASP.NET Core 10.0, Microsoft.NET.Sdk.Web, net10.0.
// ImplicitUsings enabled. Teaching endpoint; no external operation.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
context.Response.Headers["X-Training-Contract"] = "pipeline-v1";
app.Logger.LogInformation("A before");
await next(context);
app.Logger.LogInformation("A after: {Status}", context.Response.StatusCode);
});
app.UseRouting();
app.Use(async (context, next) =>
{
app.Logger.LogInformation("B before");
await next(context);
app.Logger.LogInformation("B after");
});
app.MapGet("/demo", () => Results.Text("ready-for-reading"));
app.Run();
Para GET /demo, sem exceções nem outro middleware terminal, A before precede B before; o endpoint produz o texto; B after precede A after. O cabeçalho foi definido antes da emissão. O texto não comprova uma operação de negócio.
Armadilhas comuns
Confundir app.Run() com um delegado Run; tratar MapGet como execução imediata; escrever cabeçalhos depois de HasStarted; colocar tratamento de exceções depois do componente que deve proteger.
Tópicos relacionados: Dependências, ciclos de vida e configuração · Respostas, erros e contratos de API · Autenticação e autorização
Desenha as chamadas a next e identifica onde terminam. A seleção de um endpoint e a execução desse endpoint são etapas distintas; o retorno permite observar resultados, mas não torna uma resposta já enviada novamente editável.
Referência: WebApplication and WebApplicationBuilder · ASP.NET Core 10.0 / .NET 10 / C# 14