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.

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.
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.
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.
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.
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.
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.
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.
// 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.
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.
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 |
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.
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

.NET 10 em 2026: Novos Recursos, Native AOT e Perguntas de Entrevista
Descubra os novos recursos do .NET 10 em 2026: Native AOT com binários de ~1MB, C# 14 com extension members e field keyword, ASP.NET Core 10 e perguntas de entrevista para desenvolvedores.

.NET MAUI em 2026: Desenvolvimento Multiplataforma e Perguntas de Entrevista
Tutorial de .NET MAUI para 2026: construir aplicativos multiplataforma com .NET 10, handlers, MVVM, HybridWebView. Inclui perguntas de entrevista com respostas detalhadas.

Top 25 perguntas de entrevista de ASP.NET Core: middleware, DI e minimal APIs
As perguntas de entrevista de ASP.NET Core mais comuns sobre o pipeline de middleware, os tempos de vida da injeção de dependências e as minimal APIs. Cobre as novidades do .NET 9 e do .NET 10 com exemplos de código.