.NET

18 dez, 2023

Novidades do .NET 8: Short-circuit middleware em Minimal APIs

Publicidade

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();
Ao executar a aplicação com essas configurações serão geradas mensagens no Terminal do Visual Studio Code (em amarelo) antes e após a execução da implementação de cada endpoint, em virtude da ativação do middleware de testes:
Clique nesta imagem para visualizar com uma melhor resolução

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:

Clique nesta imagem para visualizar com uma melhor resolução

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):

Clique nesta imagem para visualizar com uma melhor resolução

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!