Pular para conteúdo

Padrão de Erros - RFC 9457 (Problem Details for HTTP APIs)

Visão Geral

O PGCC-Service utiliza o padrão RFC 9457 (anteriormente RFC 7807) para respostas de erro em APIs HTTP. Este padrão define um formato estruturado e consistente para comunicar detalhes de erros aos consumidores da API.

O Spring Framework 6+ oferece suporte nativo a este padrão através da classe ProblemDetail, que é utilizada em toda a aplicação.


Estrutura da Resposta de Erro

Todas as respostas de erro seguem o formato application/problem+json:

{
  "type": "about:blank",
  "title": "Erro de Negócio",
  "status": 400,
  "detail": "Descrição detalhada do erro ocorrido",
  "instance": "/pgcc/api/v1/endpoint-chamado",
  "mnemonico": "CODIGO_DO_ERRO",
  "timestamp": "2026-07-01T10:30:00.000Z"
}

Campos Padrão RFC 9457

Campo Tipo Descrição
type string URI que identifica o tipo de problema (padrão: about:blank)
title string Resumo curto e legível do tipo de problema
status integer Código de status HTTP (ex: 400, 404, 500)
detail string Explicação específica sobre o erro ocorrido
instance string URI que identifica a ocorrência específica do problema

Campos Customizados PGCC

Campo Tipo Descrição
mnemonico string Código identificador único do erro no sistema (ex: VEICULO_NAO_ENCONTRADO)
timestamp string Data/hora ISO 8601 em que o erro ocorreu
violacoes array Lista de violações de validação (apenas para erros de validação)

Hierarquia de Exceções

A aplicação define uma hierarquia de exceções de negócio baseada em ErrorResponseException do Spring:

ErrorResponseException (Spring)
├── PgccBusinessException (abstrata)
│   ├── PgccErrorNegocioException      → HTTP 400 (Bad Request)
│   ├── PgccAccessDeniedException      → HTTP 403 (Forbidden)
│   ├── PgccRegistroNaoEncontradoException → HTTP 404 (Not Found)
│   └── PgccErrorValidacaoJwt          → HTTP 422 (Unprocessable Entity) - JWT e Hash
└── PgccErroNaoCobravelException       → HTTP 5xx (Erro não cobrável)

Tipos de Erro e Códigos HTTP

1. Erro de Negócio (400 Bad Request)

Utilizado quando a requisição viola regras de negócio.

Exceção: PgccErrorNegocioException

throw new PgccErrorNegocioException(Erro.VEICULO_NAO_ENCONTRADO);

Resposta:

{
  "type": "about:blank",
  "title": "Erro de Negócio",
  "status": 400,
  "detail": "Veículo não encontrado",
  "mnemonico": "VEICULO_NAO_ENCONTRADO",
  "timestamp": "2026-07-01T10:30:00.000Z"
}

2. Acesso Negado (403 Forbidden)

Utilizado quando o usuário não tem permissão para acessar o recurso.

Exceção: PgccAccessDeniedException

throw new PgccAccessDeniedException(Erro.NAO_AUTORIZADO, "Usuário sem permissão");

Resposta:

{
  "type": "about:blank",
  "title": "Erro de Negócio",
  "status": 403,
  "detail": "Usuário sem permissão",
  "mnemonico": "NAO_AUTORIZADO",
  "timestamp": "2026-07-01T10:30:00.000Z"
}

3. Registro Não Encontrado (404 Not Found)

Utilizado quando um recurso específico não é encontrado no sistema.

Exceção: PgccRegistroNaoEncontradoException

throw new PgccRegistroNaoEncontradoException(Erro.CONSENTIMENTO_NAO_ENCONTRADO);

Resposta:

{
  "type": "about:blank",
  "title": "Erro de Negócio",
  "status": 404,
  "detail": "Não existe consentimento associado aos parâmetros informados",
  "mnemonico": "CONSENTIMENTO_NAO_ENCONTRADO",
  "timestamp": "2026-07-01T10:30:00.000Z"
}

4. Erro de Validação JWT/Hash (422 Unprocessable Entity)

Utilizado quando há falha na validação do token JWT ou do Hash. A requisição é sintaticamente correta, mas não pode ser processada devido a erros semânticos (ex: token expirado, hash já consumido, hash expirado, hash excluído, etc.).

Exceção: PgccErrorValidacaoJwt

Exemplos de uso:

// Validação de JWT
throw new PgccErrorValidacaoJwt(Erro.ERRO_JWT, "Token expirado");

// Validação de Hash
throw new PgccErrorValidacaoJwt(Erro.ERRO_HASH, "Hash já foi consumido: " + hashId);

Resposta (JWT):

{
  "type": "about:blank",
  "title": "Erro de Negócio",
  "status": 422,
  "detail": "Token expirado",
  "mnemonico": "ERRO_JWT",
  "timestamp": "2026-07-01T10:30:00.000Z"
}

Resposta (Hash):

{
  "type": "about:blank",
  "title": "Erro de Negócio",
  "status": 422,
  "detail": "Hash já foi consumido: 06cbf962-9921-44dd-b92e-6a5b0939972e",
  "mnemonico": "ERRO_HASH",
  "mensagemTecnica": "Hash já foi consumido: 06cbf962-9921-44dd-b92e-6a5b0939972e",
  "timestamp": "2026-07-02T10:30:00.000Z",
  "parametros": ["Hash já foi consumido: 06cbf962-9921-44dd-b92e-6a5b0939972e"]
}

5. Erro de Validação de Parâmetros (400 Bad Request)

Utilizado quando há violação de constraints de validação (Bean Validation).

Exceção: ConstraintViolationException

Resposta:

{
  "type": "about:blank",
  "title": "Validação Falhou",
  "status": 400,
  "detail": "executarCadastroConsentimento.cpf: CPF inválido",
  "violacoes": [
    {
      "campo": "executarCadastroConsentimento.cpf",
      "mensagem": "CPF inválido",
      "valor": "123"
    }
  ],
  "timestamp": "2026-07-01T10:30:00.000Z"
}

6. Erro Não Cobrável (5xx)

Utilizado para erros de infraestrutura onde o hash de consulta não deve ser consumido (não cobrar do usuário).

Exceção: PgccErroNaoCobravelException

throw new PgccErroNaoCobravelException(502, "Serviço RENAVAM indisponível");

Resposta:

{
  "type": "about:blank",
  "title": "Erro Não Cobrável",
  "status": 502,
  "detail": "Serviço RENAVAM indisponível",
  "timestamp": "2026-07-01T10:30:00.000Z"
}

7. Erro Interno do Servidor (500 Internal Server Error)

Utilizado para exceções não tratadas (fallback).

Resposta:

{
  "type": "about:blank",
  "title": "Erro Interno do Servidor",
  "status": 500,
  "detail": "Mensagem da exceção",
  "timestamp": "2026-07-01T10:30:00.000Z"
}


Catálogo de Erros (Mnemônicos)

O enum Erro define todos os códigos de erro disponíveis no sistema:

Mnemônico Descrição
PLACA_INVALIDA_AUSENTE Placa inválida ou ausente
VEICULO_NAO_ENCONTRADO Veículo não encontrado
AUTORIZACAO_NAO_ENCONTRADA Autorização não encontrada
CONSENTIMENTO_PENDENTE Consentimento pendente de autorização do Titular
CONSENTIMENTO_NEGADO_PELO_TITULAR Acesso negado pelo titular, o último consentimento foi negado
CONSENTIMENTO_NAO_ENCONTRADO Não existe consentimento associado aos parâmetros informados
CONSENTIMENTO_REVOGADO Acesso negado pelo titular, o último consentimento foi revogado
TEMPLATE_NAO_ENCONTRADO Template não encontrado
CONDUTOR_NAO_ENCONTRADO Condutor não encontrado
INFRACAO_NAO_ENCONTRADA Infração não encontrada
CERTIFICADO_NAO_ENCONTRADO Certificado Público não encontrado
CNPJ_USUARIO_OBRIGATORIO CNPJ do Usuário não encontrado na requisição
CNPJ_GCC_OBRIGATORIO CNPJ do GCC não encontrado na requisição
GCC_NAO_ENCONTRADO GCC não encontrado para o CNPJ informado
CONTROLADOR_NAO_ENCONTRADO Controlador não encontrado para o CNPJ informado
ERRO_VALIDACAO_CERTIFICADO Não foi possível autorizar o certificado no momento
ERRO_INESPERADO Ocorreu um erro inesperado

Para a lista completa, consulte o enum br.gov.serpro.denatran.pgcc.exception.Erro.


Como Lançar Exceções

Erro Simples (sem parâmetros)

throw new PgccErrorNegocioException(Erro.VEICULO_NAO_ENCONTRADO);

Erro com Parâmetros

throw new PgccErrorNegocioException(Erro.GCC_NAO_ENCONTRADO, cnpj);
// Resultado: "GCC não encontrado para o CNPJ informado: 12345678000199"

Escolhendo a Exceção Correta

Situação Exceção HTTP Status
Violação de regra de negócio PgccErrorNegocioException 400
Usuário sem permissão para o recurso PgccAccessDeniedException 403
Recurso não encontrado no banco/sistema PgccRegistroNaoEncontradoException 404
Token JWT inválido ou expirado PgccErrorValidacaoJwt 422
Hash consumido, expirado, excluído ou inválido PgccErrorValidacaoJwt 422
Erro em serviço externo (não cobrar consulta) PgccErroNaoCobravelException 5xx

Tratamento Global de Exceções

O RestExceptionHandler é responsável por capturar todas as exceções e convertê-las para o formato RFC 9457:

@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(PgccErrorNegocioException.class)
    public ResponseEntity<ProblemDetail> handlePgccErrorNegocioException(PgccErrorNegocioException ex) {
        log.warn("Erro de negócio: {}", ex.getBody().getDetail());
        return ResponseEntity.status(ex.getStatusCode()).body(ex.getBody());
    }

    // ... outros handlers
}

Referências