Pular para o conteúdo
.NETRecurso único e requisições inválidas
Módulo 03Construindo a Web API

Recurso único e requisições inválidas

intermediario 30 min de leitura·Atualizado · .NET 9
Resumo

Buscamos um recurso por id, retornando 404 Not Found quando ele não existe. A verificação de existência fica na Service Layer, mantendo o controller limpo.

1. Objetivos de Aprendizagem#

  • Implementar GET /recurso/{id} com resposta 404 adequada.
  • Centralizar a validação de existência na Service Layer.
  • Usar exceções de domínio integradas ao handler global (Módulo 19).

2. Pré-requisitos#

  • Endpoints GET e tratamento global de erros (Módulos 04–05).

3. Conceito#

Buscar um único recurso exige responder corretamente quando ele não existe: 404 Not Found, não 200 com corpo nulo nem 500. O porquê: o status code é parte do contrato — clientes automatizam decisões com base nele.

Colocar a checagem de existência na Service Layer evita duplicar if (x is null) return NotFound() em cada ação; lançamos uma NotFoundException que o handler global converte em 404.

4. Mão na Massa#

4.1. Setup#

Reuse o GlobalExceptionHandler do Módulo 19.

4.2. Implementação Passo a Passo#

  1. Exceção de domínio:
C#
public abstract class NotFoundException : Exception
{
    protected NotFoundException(string message) : base(message) { }
}

public sealed class CategoriaNotFoundException : NotFoundException
{
    public CategoriaNotFoundException(Guid id)
        : base($"A categoria com id {id} não foi encontrada.") { }
}
  1. Serviço com verificação:
C#
public async Task<CategoriaDto> GetByIdAsync(Guid id, bool trackChanges)
{
    var categoria = await _repo.Categoria.GetByIdAsync(id, trackChanges)
        ?? throw new CategoriaNotFoundException(id);
    return _mapper.Map<CategoriaDto>(categoria);
}
  1. Controller enxuto:
C#
[HttpGet("{id:guid}", Name = "CategoriaById")]
public async Task<IActionResult> GetCategoria(Guid id)
{
    var categoria = await _service.Categoria.GetByIdAsync(id, trackChanges: false);
    return Ok(categoria);
}

4.3. Executando#

Shell
curl -i http://localhost:5000/api/categorias/00000000-0000-0000-0000-000000000000
# HTTP/1.1 404 Not Found  (ProblemDetails no corpo)

5. Exemplo Completo#

Fluxo: controller → serviço (lança CategoriaNotFoundException) → handler global → resposta 404 ProblemDetails.

6. Boas Práticas e Armadilhas#

FaçaEvite
Retornar 404 para recurso inexistenteRetornar 200 com corpo null
Centralizar a checagem de existência no serviçoRepetir if null em todo controller
Nomear a rota para uso em CreatedAtRouteConstruir a URL do recurso manualmente
Atenção

exceções são para fluxos excepcionais. Se "não encontrado" for um caminho extremamente frequente e esperado, considere um result pattern (Módulo 44) para evitar o custo de exceções.

7. Segurança e Produção#

  • A mensagem de erro não deve revelar se o id existe para outro usuário (evite enumeration attacks em recursos sensíveis).

8. Exercícios#

  • Fácil: crie ProdutoNotFoundException e use-a no serviço de produtos.
  • Médio: retorne 400 Bad Request quando o id não for um GUID válido (dica: route constraint).
  • Desafio: compare o custo de exceções vs. result pattern num benchmark simples.

9. Resumo#

Buscamos recurso único retornando 404 via exceção de domínio tratada globalmente, mantendo o controller limpo e o comportamento HTTP correto.

10. Próximos Passos#

A seguir, relacionamentos pai/filho: obter os produtos de uma categoria.

11. Referências#

20-01 — Recurso único e requisições inválidas | Curso ASP.NET Core