Um importante aspecto arquitetural precisa ser levado em conta ao enviarmos uma requisição HTTP para uma aplicação ASP.NET Core: diversos middlewares podem ser acionados durante o processamento desta solicitação e, mesmo, logo após a produção do resultado. Vale lembrar que a arquitetura do ASP.NET Core está centrada em uma pilha de execução de middlewares, sendo comum que esses componentes envolvam funcionalidades como autenticação/autorização, CORS (Cross-Origin Resource Sharing), compressão de respostas e logging/monitoramento. Pensando em performance e também em simplificar o processamento de alguns tipos de requisições, o ASP.NET Core traz agora no .NET 8 uma estrutura chamada short-circuit middleware. Esta nova implementação permite que configuremos endpoints que serão acionados imediatamente quando da chegada de uma requisição HTTP, sem que ocorra o acionamento dos middlewares configurados na pilha de execução da aplicação.
Para demonstrar esta nova capacidade criei em um projeto de testes um middleware – classe MiddlewareExecutionNotificator – que gera notificações de alerta, com isto acontecendo antes (linha 16) e após o processamento de uma requisição HTTP (linha 18):
namespace APIContagem.Middlewares; public class MiddlewareExecutionNotificator { private readonly RequestDelegate _next; public MiddlewareExecutionNotificator(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { var logger = (ILogger<MiddlewareExecutionNotificator>)httpContext .RequestServices.GetService(typeof(ILogger<MiddlewareExecutionNotificator>))!; logger.LogWarning("MiddlewareExecutionNotificator - Inicio da execucao"); await _next(httpContext); logger.LogWarning("MiddlewareExecutionNotificator - Fim da execucao"); } }
O Extension Method UseMiddlewareExecutionNotificator permitirá que o middleware seja ativado para os endpoints da API de testes:
namespace APIContagem.Middlewares; public static class MiddlewareExecutionNotificatorExtensions { public static IApplicationBuilder UseMiddlewareExecutionNotificator( this IApplicationBuilder builder) { return builder.UseMiddleware<MiddlewareExecutionNotificator>(); } }
Na listagem seguinte podemos observar:
- A ativação do middleware de testes, por meio de uma chamada ao método UseMiddlewareExecutionNotificator;
- O endpoint /status foi definido como um Health Check (linhas 19 a 23), indicando que a aplicação está funcionando normalmente;
- Já o endpoint /contador fará uma contagem de acessos, sendo incrementado a cada nova requisição.
using APIContagem; using APIContagem.Middlewares; using APIContagem.Models; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddSingleton<Contador>(); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.UseHttpsRedirection(); app.UseMiddlewareExecutionNotificator(); app.MapGet("/status", () => { app.Logger.LogInformation("Acionado endpoint de Health Check"); return "API Contagem - OK"; }); app.MapGet("/contador", (Contador contador) => { int valorAtualContador; lock (contador) { contador.Incrementar(); valorAtualContador = contador.ValorAtual; } app.Logger.LogInformation($"Contador - Valor atual: {valorAtualContador}"); return TypedResults.Ok(new ResultadoContador() { ValorAtual = contador.ValorAtual, Local = contador.Local, Kernel = contador.Kernel, Framework = contador.Framework, Mensagem = app.Configuration["Saudacao"] }); }).Produces<ResultadoContador>().WithOpenApi(); app.Run();
Para ativar o middleware de short-circuit para o endpoint /status basta acionar o método ShortCircuit (linha 23):
using APIContagem; using APIContagem.Middlewares; using APIContagem.Models; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddSingleton<Contador>(); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.UseHttpsRedirection(); app.UseMiddlewareExecutionNotificator(); app.MapGet("/status", () => { app.Logger.LogInformation("Acionado endpoint de Health Check"); return "API Contagem - OK"; }).ShortCircuit(); app.MapGet("/contador", (Contador contador) => { int valorAtualContador; lock (contador) { contador.Incrementar(); valorAtualContador = contador.ValorAtual; } app.Logger.LogInformation($"Contador - Valor atual: {valorAtualContador}"); return TypedResults.Ok(new ResultadoContador() { ValorAtual = contador.ValorAtual, Local = contador.Local, Kernel = contador.Kernel, Framework = contador.Framework, Mensagem = app.Configuration["Saudacao"] }); }).Produces<ResultadoContador>().WithOpenApi(); app.Run();
Novos testes com os endpoints da aplicação resultarão em diferenças. Enquanto as requisições direcionadas a /contador exibirão mensagens (em amarelo) antes e após a execução do código que implementa o endpoint, o mesmo comportamento deixará de acontecer com o endpoint /status:
Temos ainda a possibilidade de utilizar o método MapShortCircuit, definindo um status code e uma lista de rotas em que a execução da pilha de middlewares será ignorada. No exemplo a seguir (linha 45) o status 404 (Not Found) será retornado em tentativas de acesso a arquivos como robots.txt e favicon.ico (os quais normalmente não existem em projetos como APIs REST):
using APIContagem; using APIContagem.Middlewares; using APIContagem.Models; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddSingleton<Contador>(); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.UseHttpsRedirection(); app.UseMiddlewareExecutionNotificator(); app.MapGet("/status", () => { app.Logger.LogInformation("Acionado endpoint de Health Check"); return "API Contagem - OK"; }).ShortCircuit(); app.MapGet("/contador", (Contador contador) => { int valorAtualContador; lock (contador) { contador.Incrementar(); valorAtualContador = contador.ValorAtual; } app.Logger.LogInformation($"Contador - Valor atual: {valorAtualContador}"); return TypedResults.Ok(new ResultadoContador() { ValorAtual = contador.ValorAtual, Local = contador.Local, Kernel = contador.Kernel, Framework = contador.Framework, Mensagem = app.Configuration["Saudacao"] }); }).Produces<ResultadoContador>().WithOpenApi(); app.MapShortCircuit(404, "robots.txt", "favicon.ico"); app.Run();
Observamos na animação seguinte que ao usar o método MapShortCircuit o middleware de testes sequer foi acionado (em tentativas envolvendo o acesso a robots.txt e favicon.ico):
Este projeto foi disponibilizado no GitHub:
https://github.com/renatogroffe/ASPNETCore8-REST_API-Minimal-ShortCircuit_ContagemAcessos
Caso achem útil esta solução, peço por favor um ⭐️ no repositório apoiando. Fica também o convite para que vocês me sigam lá no GitHub!