Módulo 4 · APIs e sua Arquitetura — Capítulo 02

Web API — Controllers e Minimal APIs

Duas formas de implementar os mesmos princípios REST no ASP.NET Core: Controllers (o estilo clássico, parecido com o Módulo 3) e Minimal APIs (mais direto, popular em projetos novos).

1. API Controller — igual ao MVC, devolvendo dados em vez de HTML

Se você entendeu o Módulo 3, já sabe 90% disto — a diferença é que em vez de Controller (que devolve Views), uma Web API herda de ControllerBase e devolve dados diretamente:

C# ClienteController.cs — API
[ApiController]
[Route("api/clientes")]
public class ClienteController : ControllerBase
{
    private readonly IClienteRepository _repositorio;

    public ClienteController(IClienteRepository repositorio) => _repositorio = repositorio;

    [HttpGet]
    public async Task<ActionResult<List<ClienteDto>>> Listar([FromQuery] string? nome, [FromQuery] bool? ativo)
    {
        var clientes = await _repositorio.ListarAsync(nome, ativo);
        return Ok(clientes.Select(ClienteMapper.ParaDto).ToList());
    }

    [HttpGet("{id:int}")]
    public async Task<ActionResult<ClienteDto>> ObterPorId(int id)
    {
        var cliente = await _repositorio.ObterPorIdAsync(id);
        return cliente is null ? NotFound() : Ok(ClienteMapper.ParaDto(cliente));
    }

    [HttpPost]
    public async Task<ActionResult<ClienteDto>> Criar(ClienteCadastroDto dto)
    {
        var novoId = await _repositorio.InserirAsync(ClienteMapper.ParaEntidade(dto));
        var criado = await _repositorio.ObterPorIdAsync(novoId);
        return CreatedAtAction(nameof(ObterPorId), new { id = novoId }, ClienteMapper.ParaDto(criado!));
    }

    [HttpDelete("{id:int}")]
    public async Task<IActionResult> Excluir(int id)
    {
        await _repositorio.ExcluirAsync(id);
        return NoContent();
    }
}
MétodoStatus devolvido
Ok(dado)200, com o dado no corpo
NotFound()404
CreatedAtAction(...)201, com header Location apontando para ObterPorId
NoContent()204, sem corpo
O atributo [ApiController]

Habilita comportamentos automáticos específicos de API: validação de ModelState automática (devolve 400 sozinho, sem você checar ModelState.IsValid manualmente), inferência de origem dos parâmetros, e respostas de erro em formato padronizado.

2. Minimal APIs — a alternativa mais direta

Introduzidas no .NET 6, Minimal APIs dispensam a classe Controller inteira para casos simples — tudo é declarado direto no Program.cs:

C# Program.cs — os mesmos endpoints, como Minimal API
var app = builder.Build();

var clientes = app.MapGroup("/api/clientes");

clientes.MapGet("/", async (IClienteRepository repo, string? nome, bool? ativo) =>
{
    var lista = await repo.ListarAsync(nome, ativo);
    return Results.Ok(lista.Select(ClienteMapper.ParaDto));
});

clientes.MapGet("/{id:int}", async (IClienteRepository repo, int id) =>
{
    var cliente = await repo.ObterPorIdAsync(id);
    return cliente is null ? Results.NotFound() : Results.Ok(ClienteMapper.ParaDto(cliente));
});

clientes.MapPost("/", async (IClienteRepository repo, ClienteCadastroDto dto) =>
{
    var novoId = await repo.InserirAsync(ClienteMapper.ParaEntidade(dto));
    return Results.Created($"/api/clientes/{novoId}", new { id = novoId });
});

clientes.MapDelete("/{id:int}", async (IClienteRepository repo, int id) =>
{
    await repo.ExcluirAsync(id);
    return Results.NoContent();
});

app.Run();

Repare que a dependência (IClienteRepository repo) é recebida diretamente como parâmetro da função — o container de DI (Módulo 3, capítulo 6) resolve isso automaticamente, sem precisar de um construtor de classe.

3. Controllers vs. Minimal APIs — quando usar cada um

ControllersMinimal APIs
OrganizaçãoUma classe por recurso, actions agrupadasEndpoints declarados individualmente (ou agrupados com MapGroup)
Melhor paraAPIs grandes, muitos endpoints relacionados, filters/binding complexoAPIs pequenas/médias, microsserviços, quando simplicidade é prioridade
Curva de aprendizadoMais convenções para entenderMais direto para quem já conhece Express/Node (rota + handler)
Comparando com o que você já conhece

Minimal APIs devem parecer bem próximas de Express: app.MapGet(rota, handler) lê quase como app.get(rota, handler). A diferença central continua sendo a tipagem — o handler C# recebe parâmetros já tipados e validados, sem parsing manual do corpo da requisição.

4. Onde isso te leva

Com os dois estilos de implementação cobertos, o próximo capítulo aprofunda DTOs (que você já usou nos exemplos acima) e como controlar exatamente o formato JSON que entra e sai da API — incluindo versionamento, para quando a API precisa evoluir sem quebrar clientes existentes.

📌 Resumo do capítulo

  • API Controllers (ControllerBase + [ApiController]) seguem o mesmo modelo do Módulo 3, devolvendo dados em vez de Views.
  • [ApiController] habilita validação automática de ModelState e outras convenções de API.
  • Minimal APIs declaram rota + handler diretamente, com dependências injetadas como parâmetros da função.
  • Escolha Controllers para APIs grandes/complexas; Minimal APIs para simplicidade em projetos menores.

✏️ Praticando

  1. Implemente ClienteController completo como API, usando as procedures do Módulo 2 através do repositório.
  2. Reimplemente os mesmos quatro endpoints como Minimal API, num projeto separado, e compare a quantidade de código.
  3. Teste os endpoints com um cliente HTTP (curl, Postman, ou a extensão REST Client do VS Code) confirmando os códigos de status corretos.