ASP.NET Core Minimal APIs em 2026: Arquitetura, Performance e Perguntas de Entrevista

Dominar as Minimal APIs do ASP.NET Core para entrevistas técnicas: arquitetura do pipeline de requisições, grupos de rotas, filtros de endpoint, compilação Native AOT e padrões de autenticação.

ASP.NET Core Minimal APIs em 2026: Arquitetura, Performance e Perguntas de Entrevista

As Minimal APIs do ASP.NET Core eliminam a cerimônia dos controllers MVC tradicionais, oferecendo uma abordagem simplificada para construir endpoints HTTP com significativamente menos código boilerplate. Introduzidas no .NET 6 e refinadas através do .NET 8, 9 e agora .NET 10, as Minimal APIs se tornaram uma escolha pronta para produção para microsserviços, funções serverless e serviços web leves.

Minimal APIs vs Controllers

As Minimal APIs utilizam handlers de rotas de nível superior definidos diretamente no Program.cs, enquanto os controllers MVC requerem definições de classes, atributos e roteamento baseado em convenções. Para operações CRUD simples ou microsserviços com menos de 20 endpoints, as Minimal APIs tipicamente reduzem o código em 40-60%.

Arquitetura das Minimal APIs e Pipeline de Requisições

O pipeline de requisições do ASP.NET Core processa requisições HTTP através de componentes middleware antes de alcançar os handlers de endpoints. As Minimal APIs se integram perfeitamente com este pipeline enquanto oferecem uma sintaxe mais declarativa para definição de rotas.

Program.cscsharp
var builder = WebApplication.CreateBuilder(args);

// Register services for dependency injection
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<IProductRepository, ProductRepository>();

var app = builder.Build();

// Middleware pipeline configuration
app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseAuthorization();

// Minimal API endpoint definitions
app.MapGet("/products", async (IProductRepository repo) =>
    Results.Ok(await repo.GetAllAsync()));

app.MapGet("/products/{id:int}", async (int id, IProductRepository repo) =>
    await repo.GetByIdAsync(id) is Product product
        ? Results.Ok(product)
        : Results.NotFound());

app.Run();

Este padrão centraliza as definições de rotas enquanto mantém acesso completo ao container de injeção de dependências. O parâmetro IProductRepository demonstra a injeção sem construtor diretamente nos delegates handlers.

Grupos de Rotas e Organização de Endpoints

À medida que as aplicações crescem, a organização de endpoints se torna crítica. Os grupos de rotas, introduzidos no .NET 7, fornecem namespacing e configuração compartilhada sem sacrificar a abordagem minimalista.

ProductEndpoints.cscsharp
public static class ProductEndpoints
{
    public static void MapProductEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/api/products")
            .WithTags("Products")
            .RequireAuthorization();

        group.MapGet("/", GetAllProducts);
        group.MapGet("/{id:int}", GetProductById);
        group.MapPost("/", CreateProduct)
            .Accepts<CreateProductRequest>("application/json")
            .Produces<Product>(StatusCodes.Status201Created);
        group.MapPut("/{id:int}", UpdateProduct);
        group.MapDelete("/{id:int}", DeleteProduct)
            .RequireAuthorization("AdminOnly");
    }

    private static async Task<IResult> GetAllProducts(
        IProductRepository repo,
        CancellationToken ct)
    {
        var products = await repo.GetAllAsync(ct);
        return Results.Ok(products);
    }

    private static async Task<IResult> GetProductById(
        int id,
        IProductRepository repo,
        CancellationToken ct)
    {
        var product = await repo.GetByIdAsync(id, ct);
        return product is not null
            ? Results.Ok(product)
            : Results.NotFound();
    }

    private static async Task<IResult> CreateProduct(
        CreateProductRequest request,
        IProductRepository repo,
        IValidator<CreateProductRequest> validator,
        CancellationToken ct)
    {
        var validation = await validator.ValidateAsync(request, ct);
        if (!validation.IsValid)
            return Results.ValidationProblem(validation.ToDictionary());

        var product = await repo.CreateAsync(request.ToProduct(), ct);
        return Results.Created($"/api/products/{product.Id}", product);
    }
}

Chamar app.MapProductEndpoints() no Program.cs registra todas as rotas de produtos com requisitos de autorização compartilhados e metadados OpenAPI. Esta estrutura escala bem para aplicações com centenas de endpoints.

Binding de Parâmetros e Validação

As Minimal APIs suportam múltiplas fontes de binding: parâmetros de rota, query strings, headers, corpo da requisição e serviços do DI. Entender a precedência de binding previne armadilhas comuns em entrevistas.

Program.cs - Parameter binding examplescsharp
app.MapGet("/search", (
    [FromQuery] string? query,           // Explicit query string
    [FromQuery] int page = 1,            // Default value
    [FromQuery] int pageSize = 20,       // Default value
    [FromHeader(Name = "X-Correlation-Id")] string? correlationId,
    ILogger<Program> logger) =>
{
    logger.LogInformation("Search request: {Query}, Page: {Page}, CorrelationId: {CorrelationId}",
        query, page, correlationId);

    return Results.Ok(new { query, page, pageSize, correlationId });
});

// Complex model binding with validation
app.MapPost("/orders", async (
    [FromBody] CreateOrderRequest request,
    [FromServices] IValidator<CreateOrderRequest> validator,
    [FromServices] IOrderService orderService,
    HttpContext context,
    CancellationToken ct) =>
{
    var validationResult = await validator.ValidateAsync(request, ct);

    if (!validationResult.IsValid)
    {
        return Results.ValidationProblem(
            validationResult.Errors
                .GroupBy(e => e.PropertyName)
                .ToDictionary(
                    g => g.Key,
                    g => g.Select(e => e.ErrorMessage).ToArray()));
    }

    var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
    var order = await orderService.CreateOrderAsync(request, userId!, ct);

    return Results.Created($"/orders/{order.Id}", order);
});

O atributo [FromBody] é opcional para tipos complexos mas melhora a legibilidade. FluentValidation se integra naturalmente através de injeção de dependências, mantendo a lógica de validação separada dos handlers de endpoints.

Prioridade de Fontes de Binding

Quando nenhum atributo é especificado, as Minimal APIs inferem as fontes de binding: parâmetros de rota primeiro, depois query strings para tipos simples, e o corpo da requisição para tipos complexos. Atributos explícitos como [FromQuery] ou [FromBody] sobrescrevem este comportamento.

Otimização de Performance com Native AOT

O .NET 8 introduziu suporte à compilação Native AOT (Ahead-of-Time) para Minimal APIs, produzindo executáveis autocontidos com tempos de inicialização inferiores a um milissegundo. Esta capacidade torna as Minimal APIs ideais para deploys serverless onde a latência de cold start importa.

Program.cs - AOT-compatible configurationcsharp
var builder = WebApplication.CreateSlimBuilder(args);

// AOT-friendly JSON serialization
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

var app = builder.Build();

app.MapGet("/health", () => Results.Ok(new HealthResponse("Healthy", DateTime.UtcNow)));

app.Run();

// Source-generated JSON serializer context
[JsonSerializable(typeof(HealthResponse))]
[JsonSerializable(typeof(Product))]
[JsonSerializable(typeof(List<Product>))]
internal partial class AppJsonContext : JsonSerializerContext { }

public record HealthResponse(string Status, DateTime CheckedAt);

O método CreateSlimBuilder exclui recursos desnecessários do framework, enquanto o JsonSerializerContext gerado por fonte elimina reflexão em tempo de execução para serialização JSON. Binários AOT publicados para APIs simples tipicamente medem 10-15 MB comparados a 80+ MB para deploys autocontidos padrão.

Typed Results e Metadados de Resposta

O .NET 7 introduziu TypedResults para verificação em tempo de compilação de tipos de resposta, melhorando a precisão da documentação OpenAPI e detectando incompatibilidades de tipos durante o desenvolvimento.

csharp
// Strongly-typed results with OpenAPI metadata
app.MapGet("/products/{id:int}", async Task<Results<Ok<Product>, NotFound, ProblemHttpResult>> (
    int id,
    IProductRepository repo,
    CancellationToken ct) =>
{
    try
    {
        var product = await repo.GetByIdAsync(id, ct);

        return product is not null
            ? TypedResults.Ok(product)
            : TypedResults.NotFound();
    }
    catch (Exception ex)
    {
        return TypedResults.Problem(
            detail: "An error occurred retrieving the product",
            statusCode: StatusCodes.Status500InternalServerError);
    }
})
.WithName("GetProductById")
.WithOpenApi(operation =>
{
    operation.Summary = "Retrieves a product by ID";
    operation.Description = "Returns the product details or 404 if not found";
    return operation;
});

O tipo union Results<T1, T2, T3> declara todos os tipos de resposta possíveis, que os geradores Swagger/OpenAPI utilizam para produzir documentação precisa. Este padrão é particularmente valioso ao se preparar para perguntas de entrevista sobre design de APIs no ASP.NET Core.

Pronto para mandar bem nas entrevistas de .NET?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Filtros de Endpoint para Preocupações Transversais

Os filtros de endpoint fornecem funcionalidade similar ao middleware mas limitada a endpoints ou grupos específicos, tratando preocupações como logging, cache e transformação de requisições.

ValidationFilter.cscsharp
public class ValidationFilter<T> : IEndpointFilter where T : class
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        var validator = context.HttpContext
            .RequestServices
            .GetService<IValidator<T>>();

        if (validator is null)
            return await next(context);

        var argument = context.Arguments
            .OfType<T>()
            .FirstOrDefault();

        if (argument is null)
            return await next(context);

        var validationResult = await validator.ValidateAsync(argument);

        if (!validationResult.IsValid)
        {
            return Results.ValidationProblem(
                validationResult.Errors
                    .GroupBy(e => e.PropertyName)
                    .ToDictionary(
                        g => g.Key,
                        g => g.Select(e => e.ErrorMessage).ToArray()));
        }

        return await next(context);
    }
}

// Usage in Program.cs
app.MapPost("/products", CreateProduct)
    .AddEndpointFilter<ValidationFilter<CreateProductRequest>>();

// Global filter registration via route group
var api = app.MapGroup("/api")
    .AddEndpointFilter(async (context, next) =>
    {
        var logger = context.HttpContext
            .RequestServices
            .GetRequiredService<ILogger<Program>>();

        var stopwatch = Stopwatch.StartNew();
        var result = await next(context);
        stopwatch.Stop();

        logger.LogInformation(
            "Endpoint {Method} {Path} completed in {ElapsedMs}ms",
            context.HttpContext.Request.Method,
            context.HttpContext.Request.Path,
            stopwatch.ElapsedMilliseconds);

        return result;
    });

Os filtros executam na ordem de registro, com o filtro mais interno mais próximo do handler do endpoint. Esta arquitetura permite separação limpa de validação, logging e lógica de autorização.

Padrões de Autenticação e Autorização

As Minimal APIs suportam os mesmos mecanismos de autenticação e autorização que os controllers MVC, com uma sintaxe de configuração mais declarativa.

Program.cs - JWT authentication setupcsharp
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
        };
    });

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("AdminOnly", policy =>
        policy.RequireRole("Admin"))
    .AddPolicy("PremiumUser", policy =>
        policy.RequireClaim("subscription", "premium", "enterprise"));

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

// Protected endpoints
app.MapGet("/admin/users", async (IUserService userService) =>
    Results.Ok(await userService.GetAllUsersAsync()))
    .RequireAuthorization("AdminOnly");

app.MapGet("/profile", async (ClaimsPrincipal user, IUserService userService) =>
{
    var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
    var profile = await userService.GetProfileAsync(userId!);
    return Results.Ok(profile);
})
.RequireAuthorization();

// Anonymous endpoint within protected group
var protectedGroup = app.MapGroup("/api/secure")
    .RequireAuthorization();

protectedGroup.MapGet("/public-info", () => Results.Ok("This is public"))
    .AllowAnonymous();

O método de extensão RequireAuthorization aceita nomes de políticas ou pode ser chamado sem argumentos para exigir qualquer usuário autenticado. Entender estes padrões é essencial para perguntas de entrevista sobre autenticação e autorização.

Perguntas de Entrevista: Padrões Comuns

Entrevistas técnicas frequentemente exploram as diferenças entre Minimal APIs e controllers tradicionais. A tabela abaixo resume as distinções chave:

| Aspecto | Minimal APIs | Controllers MVC | |---------|--------------|------------------| | Boilerplate | Baixo - delegates handlers diretos | Maior - classe + método + atributos | | Roteamento | Inline com MapGet, MapPost | Baseado em atributos ou convenções | | Model binding | Automático com atributos opcionais | Convenção + atributos | | Filtros | Filtros de endpoint | Action filters + middleware | | Suporte AOT | Completo desde .NET 8 | Limitado, pesado em reflexão | | Testabilidade | Baseada em funções, fácil de testar unitariamente | Requer instanciação do controller | | Ideal para | Microsserviços, APIs simples | Aplicações grandes, workflows complexos |

Dica de Entrevista

Quando perguntarem "Quando você escolheria controllers ao invés de Minimal APIs?", mencione aplicações complexas que requerem action filters, customização de model binding, ou bases de código existentes com padrões MVC estabelecidos. As Minimal APIs se destacam para microsserviços greenfield e funções serverless.

Testes de Endpoints Minimal API

Testes de integração com WebApplicationFactory fornecem verificação realista de endpoints sem fazer deploy da aplicação.

ProductEndpointsTests.cscsharp
public class ProductEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _client;
    private readonly WebApplicationFactory<Program> _factory;

    public ProductEndpointsTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureServices(services =>
            {
                // Replace real repository with mock
                services.RemoveAll<IProductRepository>();
                services.AddScoped<IProductRepository, MockProductRepository>();
            });
        });
        _client = _factory.CreateClient();
    }

    [Fact]
    public async Task GetProducts_ReturnsOkWithProductList()
    {
        // Act
        var response = await _client.GetAsync("/api/products");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var products = await response.Content
            .ReadFromJsonAsync<List<Product>>();

        products.Should().NotBeNull();
        products.Should().HaveCountGreaterThan(0);
    }

    [Fact]
    public async Task GetProductById_WithInvalidId_ReturnsNotFound()
    {
        // Act
        var response = await _client.GetAsync("/api/products/99999");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NotFound);
    }

    [Fact]
    public async Task CreateProduct_WithValidRequest_ReturnsCreated()
    {
        // Arrange
        var request = new CreateProductRequest("Test Product", 29.99m, "Test Description");

        // Act
        var response = await _client.PostAsJsonAsync("/api/products", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);
        response.Headers.Location.Should().NotBeNull();
    }
}

Para uma exploração mais profunda de padrões de clean architecture em aplicações .NET, a camada de serviços deve ser testada independentemente com testes unitários, enquanto testes de integração verificam o pipeline de requisições completo.

Conclusão

As Minimal APIs representam a abordagem moderna para construir serviços HTTP no ASP.NET Core:

  • Grupos de rotas e organização de endpoints escalam desde microsserviços simples até aplicações complexas
  • Filtros de endpoint fornecem separação limpa de preocupações transversais como validação e logging
  • TypedResults habilita verificação em tempo de compilação de tipos de resposta e documentação OpenAPI precisa
  • Compilação Native AOT entrega tempos de inicialização inferiores a um milissegundo para deploys serverless
  • Testes de integração com WebApplicationFactory garantem verificação realista de endpoints

Para preparação de entrevistas, foque em articular quando as Minimal APIs são apropriadas versus controllers tradicionais, e demonstre compreensão do pipeline de requisições, injeção de dependências e padrões de autenticação.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Compartilhar

Artigos relacionados