Exceções customizadas
ResumoExceções próprias tornam erros de domínio explícitos e fáceis de tratar. Uma
ProdutoNaoEncontradoExceptioncomunica muito mais que umaExceptiongenérica — e permite mapear o erro para o status HTTP certo na API.
1. Objetivos de Aprendizagem#
- Criar exceções derivadas de
Exception. - Modelar erros de domínio com hierarquia própria.
- Preservar a exceção original com
innerException.
2. Pré-requisitos#
- try/catch/finally (tópico 10.1).
3. Conceito#
Você cria uma exceção herdando de Exception (ou de uma base sua). O porquê: dar nome e semântica aos erros do seu domínio, permitindo que camadas superiores (ex.: um middleware de erros na API) decidam como respondê-los (404, 422, etc.).
Boas práticas de design:
- Sufixo
Exceptionno nome. - Uma base comum de domínio (ex.:
DominioException) e derivadas específicas. - Construtores que aceitam mensagem e, opcionalmente, a exceção interna.
4. Mão na Massa#
4.1. Setup#
dotnet new console -n Catalog.CustomEx
cd Catalog.CustomEx
4.2. Implementação Passo a Passo#
- Base + derivadas:
public abstract class DominioException : Exception
{
protected DominioException(string mensagem) : base(mensagem) { }
}
public sealed class ProdutoNaoEncontradoException : DominioException
{
public ProdutoNaoEncontradoException(Guid id)
: base($"Produto {id} não encontrado.") { }
}
public sealed class PrecoInvalidoException : DominioException
{
public PrecoInvalidoException(decimal valor)
: base($"Preço inválido: {valor}.") { }
}
- Uso e captura pela base:
Produto Buscar(Guid id) =>
throw new ProdutoNaoEncontradoException(id);
try
{
Buscar(Guid.NewGuid());
}
catch (DominioException ex) // captura qualquer erro de domínio
{
Console.WriteLine($"Erro de domínio: {ex.Message}");
}
record Produto(Guid Id);
- Preservando a origem (
innerException):
try { /* chamada externa */ }
catch (Exception original)
{
throw new DominioException2("Falha ao integrar", original);
}
public class DominioException2 : Exception
{
public DominioException2(string m, Exception inner) : base(m, inner) { }
}
4.3. Executando#
dotnet run
5. Exemplo Completo#
Com uma base DominioException, o middleware de erros da API pode mapear ProdutoNaoEncontradoException → 404 e PrecoInvalidoException → 422 num só lugar — exatamente o padrão do Módulo 05 do curso de Web API.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Criar uma base de domínio e derivadas específicas | Lançar Exception genérica para tudo |
| Preservar a exceção interna ao "re-embrulhar" | Descartar o innerException e perder o rastro |
| Usar exceções para erros de regra, não para fluxo comum | Criar dezenas de exceções triviais sem necessidade |
Atençãoao capturar e relançar, use
throw;(sem o objeto) para preservar o stack trace.throw ex;reinicia o rastro e esconde a origem.
7. Segurança e Produção#
- Não exponha detalhes internos (stack trace, SQL) nas mensagens que chegam ao cliente. Mensagens de exceção são para logs; a resposta ao usuário deve ser sóbria.
8. Exercícios#
- Fácil: crie uma
CategoriaNaoEncontradaException. - Médio: organize suas exceções sob uma base comum e capture pela base.
- Desafio: relance uma exceção externa embrulhada numa de domínio, preservando a original.
9. Resumo#
Exceções customizadas dão semântica aos erros de domínio e permitem mapear respostas HTTP num só lugar. Use uma base comum, preserve innerException e relance com throw;.
10. Próximos Passos#
A seguir, as boas práticas gerais de tratamento de exceções.
11. Referências#
- Microsoft Learn — Criando e lançando exceções
- Microsoft Learn — Exceções personalizadas
- Microsoft Learn — Melhores práticas com exceções