Tratamento de Exceções

Estratégia Utilizada

A ideia principal do Framework é que todas as Exceções que sobem para serem mostradas para o usuário final sejam tratadas por um único ponto e que ele possa ser sobrescrito pelo desenvolvedor de acordo com as suas necessidades.

O primeiro passo foi definir a estrutura a ser utilizada no retorno padrão dos erros do Framework Demoiselle, e para isso pegamos como inspiração a RFC 6749 que define o OAuth 2.0, pois dentro dela existe um padrão definido no item 5.2 (Error Response) que descreve o seguinte formato:

[
  {
    "error": "descrição_curta",
    "error_description": "descrição longa, legível por humanos",
    "error_uri": "URI que leva a uma descrição detalhada do erro"
  },
  {
    "error": "descrição_curta",
    "error_description": "descrição longa, legível por humanos",
    "error_uri": "URI que leva a uma descrição detalhada do erro"
  }
]

Por padrão usamos uma coleção contendo a estrutura do erro com o objetivo de retornar vários erros de uma só vez (isso pode ser útil no caso de uma validação de formulário server-side, por exemplo).

RFC 6749 (The OAuth 2.0 Authorization Framework) - https://tools.ietf.org/html/rfc6749\#page-45

Código HTTP de Resposta

É recomendada a utilização de código de resposta HTTP que semânticamente mais se adequem a resposta que será dada. O ideal é que sejam utilizados os códigos de resposta que estão definidos no Java na enum javax.ws.rs.core.Status que contém os códigos de retorno HTTP disponíveis no Java.

O Framework Demoiselle tem como padrão retornar os seguintes códigos de erro que serão melhor detalhados mais a frente.

  • Código de erro 400 (Bad Request)
  • Código de erro 412 (Precondition Failed)
  • Código de erro 500 (Internal Server Error)

Exception Mappers

O ideal é que no log de inicialização do servidor de aplicação somente existam as seguintes classes de mapeamento:

  1. org.demoiselle.jee.rest.exception.mapper.ValidationExceptionMapper: sobrescreve o mapeamento de Exception do Beans Validation
  2. org.demoiselle.jee.rest.exception.mapper.AnyOtherExceptionMapper: captura todas as outras Exceptions do sistema

Caso existam outros Mappers provavelmente ele não irá retornar no formato padrão como os mapeadores do Framework faz, pois o funcionamento padrão segue a seguinte sequência:

  1. Ao lançar uma exceção não tratada o mecanismo de tratamento irá tentar encontrar um Mapper que seja idêntico a exceção lançada
  2. Caso não encontre um Mapper de exceção idêntico tentará encontrar um Mapper para a super classe da exceção
  3. O processo de encontrar continuará procurando na super classe da exceção até encontrar um Mapper, que no caso do Framework Demoiselle irá terminar no Mapper AnyOtherExceptionMapper que mapeia Throwable

Os Mappers padrões do Framework tratam as seguintes Exceções:

  • ConstraintViolationException: Código de erro 412 (Precondition Failed)
  • SQLException: Código de erro 500 (Internal Server Error)
  • DemoiselleRestException: Código de erro 412 (Precondition Failed) ou o Status Code informado pelo desenvolvedor
  • InvalidFormatException: Código de erro 400 (Bad Request)
  • ClientErrorException: Código de erro da Exception

Para todas as outras que não estão tratadas ele irá retornar o código de erro 500 (Internal Server Error).

Configurações

A propriedade demoiselle.rest.showErrorDetails (que por padrão vem verdadeira) permite desabilitar o retorno dos detalhes do erro, por exemplo no cadastro de um usuário no qual tenha dado um erro de Unique Constraint e o propriedade esteja true:

[
  {
    "error":"Não foi possível salvar",
    "error_description":
    { 
      "error_message":"Duplicate entry 'nome@dominio.com' for key 'UK_e6gkqunxajvyxl5uctpl2vl2p'",
      "sql_state":"23000",
      "error_code":1062
    }  
  }
]

Agora com a propriedade demoiselle.rest.showErrorDetails com valor false:

[
  {
    "error":"Não foi possível salvar",
  }
]

Usando mensagens de erro customizadas para erros de banco

Utilizando a propriedade demoiselle.rest.sqlError.'code' = 'mensagem' é possível mapear e customizar as mensagens para os erros provenientes do banco de dados utilizado (Mysql, Postgres, etc...). Para isso acontecer é necessário somente adicionar as mensagens (de acordo com o codigo de erro do banco ) no demoiselle.properties da aplicação:

demoiselle.rest.sqlError.1062=Entidade já existe no banco
demoiselle.rest.sqlError.1063=....

*Verificar número dos codigos de erros que podem ser diferentes de acordo com o fornecedor do banco de dados.

Sobrescrita do Formato Padrão

Para sobrescrever a classe que faz o tratamento das Exceções que ocorrem na aplicação basta estender da Classe org.demoiselle.jee.rest.exception.treatment.ExceptionTreatmentImpl com sua implementação do método getFormatedError e adicionar a anotação @Specializes, a partir disso todas as Exceptions que ocorrerem irão cair neste método.

É importante notar que caso existam outros ExceptionMappers encontrados no Classpath do Java é possível que nem todas as Exceções sejam tratadas por este método.

...
import org.demoiselle.jee.rest.exception.treatment.ExceptionTreatmentImpl;

@Specializes
public class ExceptionTreatmentApp extends ExceptionTreatmentImpl {

    @Override
    public Response getFormatedError(Throwable exception, HttpServletRequest request) {        
        // Put here your implementation of exception treatment    

        return super.getFormatedError(exception, request);
    }

}

results matching ""

    No results matching ""