Exception Handling com Spring
Exceptions ocorrem em todo projeto e lidar com elas é necessário e inevitável. Contudo, nem sempre essas exceptions são algo ruim. No geral, precisamos conseguir prever os possíveis erros e, com isso, utilizá-los para prover respostas indicativas e legíveis para os clients que requisitam em nossa API.
Para quem utiliza o Spring, tem a sorte de o próprio framework prover uma forma de lidar de forma simples com essa situação, e, neste artigo, focaremos em exemplificar como conseguimos lidar com exceptions utilizando o Spring e suas ferramentas.
Criando o DTO padrão do projeto para mapear as exceptions
Para podermos tornar nosso json de retorno em caso de exceptions legível e utilizável para o client que requisita, precisamos padronizar e modelar um objeto com as informações sobre a nossa exception. Aqui pro artigo, criei um DTO bem semelhante ao que usamos aqui na empresa, mas um pouco mais simplificado:
Este DTO contém as informações principais que serão necessárias ao client:
- statusCode: o http code resultante.
- message: uma mensagem que pode ser usada para exibir para o usuário.
- debugMessage: uma mensagem para auxiliar desenvolvedores a entender mais a fundo qual o problema. Por aqui, utilizamos a mensagem que vem nas exceptions das libs que usamos.
Por padrão, este será sempre o objeto que retornaremos para o client. Dessa forma ele já sabe o que esperar e quando receber, vai saber como usar. Além disso, caso venha a receber algo diferente disso, ele conseguirá identificar que algum problema não previsto está ocorrendo.
Usando exceptions customizadas
Todas as RuntimeExpcetion’s não precisam de um try catch e nem um throws na declaração do método. Dessa forma, conseguimos criar exceptions customizadas que estendem a RuntimeException e utilizá-las para lançar erros durante o fluxo do nosso código.
Para demonstração, foi criada uma exception de exemplo que será mapeada e lançada durante o fluxo de código para demonstração:
Tudo pronto, vamos ao mapeamento!
Já temos nossa exception customizada e também já temos um DTO de retorno. Agora precisamos apenas mapear o que ocorrerá quando a exception ExampleErrorException for lançada e pronto!
Para tanto, criaremos uma classe anotada com @RequestControllerAdvice, do Spring. Abaixo da classe explicarei o uso de cada anotação.
- @RestControllerAdvice: a anotação indica que esta é uma classe de mapeamento de erros.
- @ExceptionHandler: a anotação indica com qual exception o método em questão irá lidar. No parâmetro da declaração dessa anotação sempre informaremos a classe em si.
- @ResponseStatus: aqui indicamos qual será o http status code que será retornada ao client.
O retorno de cada método de mapeamento deverá ser a classe que será transformada em Json. No nosso caso, criamos uma padrão (ApiError). Então, quando a exception ExampleErrorException for lançada, cairemos diretamente no método handleExampleErrorException e o retorno dele será transformado em json e enviado ao client.
Na nossa classe, mapeamos apenas a ExampleErrorException, mas inúmeras exceptions podem ser mapeadas, bastando apenas declarar mais um método com as anotações necessárias.
Testando e vendo o resultado
Para testar o mapeamento realizado, foi criado um controller e um service de exemplo:
Service:
Fazendo um GET usando o postman na url http://localhost:8080/medium-article/example, obtemos o seguinte json no retorno:
Por fim…
Essa é apenas uma das formas de lidar com exceptions. Como é feito na sua empresa? Por quais situações já acabou passando? Conta pra gente que com certeza temos algo a aprender juntos!
Neste link é possível acessar o projeto de exemplo completo e funcional.