Class UnifiedErrorResponseProcessor

java.lang.Object
br.com.xadm.comum.web.UnifiedErrorResponseProcessor
All Implemented Interfaces:
io.micronaut.http.server.exceptions.response.ErrorResponseProcessor<ProblemDetail>

@Singleton @Replaces(io.micronaut.http.server.exceptions.response.HateoasErrorResponseProcessor.class) public class UnifiedErrorResponseProcessor extends Object implements io.micronaut.http.server.exceptions.response.ErrorResponseProcessor<ProblemDetail>
Renderiza todo corpo de erro da API no formato RFC 7807 (ProblemDetail, application/problem+json), substituindo o HateoasErrorResponseProcessor default (que emitia {message, _links, _embedded}). É o padrão de erro da casa.

Atua no ponto único por onde o Micronaut renderiza erros gerados pelo framework: HttpStatusException (e a hierarquia de exceção desta lib), falhas de validação (Bean Validation), 404 de rota e erros de parse de corpo.

Honra a taxonomia de domínio da causa-raiz. Lê context.getRootCause(): quando a exceção é uma PortadoraDeProblema, carimba o code de máquina e o title de tipo que ela carrega; na ausência, cai no genérico (code = status.name(), title = status.getReason()) — byte-idêntico ao comportamento anterior. O title é do tipo do problema (estável, RFC 9457 §3.1.4); o específico da ocorrência fica no detail.

Falha de Bean Validation (ConstraintViolationException) recebe code uniforme (status.name(), ex. BAD_REQUEST — o mesmo de BadRequestException, para o consumidor não distinguir borda×service) mais a lista de campos rejeitados em invalid-params. Ordem de decisão: portadora → validação → genérico.

O status HTTP já vem definido na resposta quando este processor roda — apenas formatamos o corpo, sem alterar o status (preserva os contratos testados de cada app).

Log estruturado (policy da casa)

Ponto único por onde todo erro renderizado da API passa — logo, o lugar da policy de log da casa (constituição engenharia/java-micronaut §Erros: "mensagem ao cliente × log interno"). Antes cada app reimplementava o log num @ExceptionHandler dedicado e, ao migrar para o seam PortadoraDeProblema, esse log — junto do 5xx→Sentry — se perdia. Aqui o log é incondicional (não é opt-in): emite uma linha por erro — método caminho -> status code lendo context.getRequest():

  • 5xx em nível ERROR com a causa-raiz (stacktrace). É o nível que o SentryAppender do logback captura — restaura o 5xx→Bugsink sem código no app.
  • 404 numa requisição autenticada em nível ERROR (glitcha). Cliente que manda credencial acreditava que a rota existe — 404 nele é bug, não ruído. Nasceu de um incidente de produção: o native-image não registrou um controller (reachability/AOT) e a rota, que funciona na JVM, deu 404 em DEBUG — erro real, invisível no GlitchTip. Ver autenticado(HttpRequest) para o proxy de "cliente legítimo".
  • Demais 4xx — e o 404 anônimo (scanner batendo /.env, /wp/...) — em nível DEBUG, sem stacktrace: erro de cliente, esperado e ruidoso (validação, 401/403, varredura); não polui o log de produção nem o GlitchTip.

A linha não carrega corpo, query-string, headers nem o detail — só verbo, caminho, status e o code de máquina, minimizando exposição de PII no log.

  • Constructor Details

    • UnifiedErrorResponseProcessor

      public UnifiedErrorResponseProcessor()
  • Method Details

    • processResponse

      public io.micronaut.http.MutableHttpResponse<ProblemDetail> processResponse(io.micronaut.http.server.exceptions.response.ErrorContext context, io.micronaut.http.MutableHttpResponse<?> response)
      Specified by:
      processResponse in interface io.micronaut.http.server.exceptions.response.ErrorResponseProcessor<ProblemDetail>