22 pontos por lemonmint 2025-06-02 | 5 comentários | Compartilhar no WhatsApp

Ao desenvolver APIs HTTP, o tratamento de erros costuma ser uma parte trabalhosa. À medida que o número de APIs cresce e a lógica interna fica mais complexa, surgem dificuldades em três aspectos.

  • Retornar códigos de erro apropriados: para desenvolvedores com menos experiência, é difícil usar códigos de status HTTP consistentes em lógicas complexas.
  • Escrever uma grande quantidade de logs de resultado: registrar logs em todos os pontos de saída esperados quando um erro ocorre aumenta o volume de código e torna a manutenção mais complexa.
  • Enviar mensagens de erro claras: apenas repassar uma mensagem de erro ao cliente não é suficiente para entender e tratar o erro com clareza.

Melhorando o retorno de códigos de erro apropriados

Para resolver o problema de consistência no uso de códigos de erro, propõe-se implementar uma interface ou struct HttpError que inclua StatusCode e Message.

  • Solução:
    • Definir o tipo HttpError: encapsula o código de status HTTP e a mensagem.
    • Fornecer funções helper: usar helpers que retornam códigos de erro específicos, como httperror.BadRequest("wrong format"), para criar objetos de erro com facilidade.
  • Vantagens:
    • Aproveitar o autocompletar da IDE para inserir códigos de erro e mensagens de forma prática e segura.
    • Redução da possibilidade de erro em comparação com digitar códigos numéricos manualmente.
    • Menos incômodo ao ter de consultar repetidamente documentos de design preparados com antecedência.

Centralização da escrita de logs

Para reduzir a escrita repetitiva de logs e gerenciar a lógica de tratamento de erros em um só lugar, é apresentado um método de encapsular o handler HTTP.

  • Solução:
    • Implementar um roteador customizado (chiwrap.Router): inclui internamente um roteador existente, como chi.Router, e adiciona lógica de tratamento de erros.
    • Encapsular handlers: métodos como Get do roteador customizado recebem HandlerFunc, executam internamente e, se ocorrer um erro, o encaminham para a lógica central de tratamento.
    • Função de callback de erro: ao criar NewRouter, recebe-se uma função errCallback; quando ocorre um erro, essa callback é chamada para registrar logs de forma centralizada ou executar processamento adicional.
  • Vantagens:
    • Quando ocorre um erro na lógica da API, o código de erro e a mensagem apropriados são retornados automaticamente na resposta.
    • Fica fácil gerenciar logs ao registrar funções de callback para gravar logs adequados a cada serviço.
    • Redução de duplicação de código e melhoria da manutenibilidade.

Envio de mensagens de erro claras (uso do RFC7807)

Para que o cliente possa entender e tratar erros com mais clareza, propõe-se o envio de mensagens de erro estruturadas com base no padrão RFC7807.

  • Principais elementos do RFC7807:
    • type: URI que identifica o tipo de erro (ex.: https://example.com/errors/validation).
    • title: descrição curta do erro em uma linha.
    • status: igual ao código de status HTTP.
    • detail: descrição detalhada do erro, legível por humanos.
    • instance: URI específica em que o erro ocorreu (ex.: /api/users/abc).
    • extensions: objeto JSON que contém informações adicionais (ex.: invalid_field, expected_format).
  • Implementação:
    • Criar uma struct RFC7807Error e incluir os principais elementos.

    • Criar facilmente objetos de erro estruturados por meio do padrão de method chaining (WithType(), WithInstance(), WithExtension()).

    • Com o método ToHttpError(), converter RFC7807Error em HttpError, permitindo integração com o roteador centralizado.

    • O cliente pode identificar com clareza o tipo, a causa e o local de ocorrência do erro.

    • Aumenta a consistência e a utilidade das respostas da API, melhorando a eficiência do desenvolvimento no cliente.

5 comentários

 
aer0700 2025-06-02

Obrigado pelo ótimo artigo.

 
beoks 2025-06-02

Ótimo artigo, obrigado!
Só como referência, no Spring existe uma implementação em org.springframework.http.ProblemDetail, dentro da biblioteca spring-web!

 
honglu 2025-06-02

Obrigado pela ótima introdução!
Fui pesquisar e vi que foi substituído pelo RFC 9457.

https://datatracker.ietf.org/doc/html/rfc9457
(documento 7807 anterior: https://datatracker.ietf.org/doc/html/rfc7807)

 
findnamo 2025-06-02

Principais diferenças entre a RFC 7807 e a RFC 9457

  • Gerenciamento de tipos de problema: a 7807 só permite URI personalizada; a 9457 introduz um registro compartilhado da IANA
  • Tratamento de múltiplos erros: a 7807 recomenda o uso do código de status HTTP 207; a 9457 agrupa erros relacionados usando um array errors dentro de um único tipo de problema
  • Campos de extensão: a 7807 permite adicionar campos arbitrários; a 9457 associa explicitamente os campos esperados a cada tipo de problema
  • Recomendações de segurança: ausentes na 7807; a 9457 adiciona diretrizes explícitas para prevenir vulnerabilidades de segurança
  • JSON Pointer: não suportado na 7807; a 9457 oferece suporte oficial ao campo pointer

Para novos projetos desde julho de 2023, recomenda-se aplicar a RFC 9457

 
honglu 2025-06-02

Parece que é recomendado definir o campo type como uma URI que possa ser dereferenciada.

Em serviços internos, provavelmente não há problema em substituí-la por um link para a documentação do Swagger UI.