Spring - Exception Handler
Tags: java, springboot, exception
Published: 2021-04-26 16:52:44

<!-- wp:paragraph -->
<p>Para começar, certifique que tem a dependência no seu POM ou Gradle.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;dependency>
   &lt;groupId>org.springframework.boot&lt;/groupId>
   &lt;artifactId>spring-boot-starter-web&lt;/artifactId>
&lt;/dependency></code></pre>
<!-- /wp:code -->

<!-- wp:heading -->
<h2>Tratamento Padrão</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>O tratamento de erro padrão do Spring oferece uma forma out-of-the-box que responde dependendo do <strong>Content-type</strong>.</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Caso o Content-Type seja text/html o tratamento de erro irá encaminhar o request para uma página de erro&nbsp;<em>White label error page</em>. Caso você adicione um mapping específico para "/error" então o Spring utilizará esse cara ao invés do White label error page.</li><li>Caso o Cotent-type seja JSON o tratamento de erro retornará um objeto com as seguintes propriedades:<em> error, message, path, status&nbsp;</em>e&nbsp;<em>timestamp</em>.</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>A página de erro pode ser desabilitada ao adicionar a linha abaixo no seu&nbsp;<strong>.properties</strong>.</p>
<!-- /wp:paragraph -->

<!-- wp:syntaxhighlighter/code {"language":"java"} -->
<pre class="wp-block-syntaxhighlighter-code">server.error.whitelabel.enabled=false</pre>
<!-- /wp:syntaxhighlighter/code -->

<!-- wp:heading -->
<h2>Tratamento Customizado</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Spring fornece uma maneira bem simples e limpa que nos permite tratar exceptions que ocorrem durante a execução de um método do nosso controller MVC/REST. Podemos tratar e retornar um JSON ou encaminhar para uma página especifica de erro.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Usando @ResponseStatus</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>A primeira forma nos permite tratar apenas o retorno do nosso controller. Na nossa exception adicionamos a anotação&nbsp;<strong>@ResponseStatus</strong> que permite customizar o HTTP status e o header&nbsp;<em>reason</em> do nosso response. O response ainda será retornado utilizando o tratamento padrão (error page ou JSON padrão) caso não tenha sido definido uma págian ou um handler.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "We can't handle your request =(")
public class CustomException extends RuntimeException {
}</code></pre>
<!-- /wp:code -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@RestController
@RequestMapping("/api")
class HelloController {
   @GetMapping("custom")
   public String customResponseFailure() {
      throw new CustomException();
   }
}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Quando uma exception do tipo CustomException for lançada o tratamento padrão de erro do Spring entrará em ação mas utilizará o nosso HTTP status e adicionará o header&nbsp;<em>reason</em> no response.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>Usando @ExceptionHandler</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>A segunda forma mais interessante é criar&nbsp; um método para tratar exceptions específicas. Esta forma nos permite cria um método para tratar a exception adequadamente e então encaminhar para uma página específica ou retornar uma entidade customizada.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Se anotarmos um método de uma controller então todos as exceptions daquela controller que não tenham sejam anotadas com @ResponseStatus serão tratadas por ela mas caso queiramos criar um handler para todas as controllers precisamos criar&nbsp; um Advice.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Temos dois tipos de Advice:&nbsp;<strong>ControllerAdvice</strong> e&nbsp;<strong>RestControllerAdvice</strong>. O ControllerAdvice segue a semântica do&nbsp;@Controller, então no fim do tratamento ele redireciona para a view definida. O RestControllerAdvice segue o @RestController, então retorna seu objeto serializado direto no response.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>Controller com @ExceptionHandler</h4>
<!-- /wp:heading -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@Controller
@RequestMapping("/web")
public class PageController {
   @GetMapping("customerrorpage")
   public String customErrorPage() {
      throw new NumberFormatException("We can't format your number");
   }

   @ExceptionHandler(NumberFormatException.class)
   public ModelAndView handleNumberFormatException(NumberFormatException nfex) {
      ModelAndView model = new ModelAndView("custom_error");
      model.addObject("errorMessage", nfex.getMessage());
      return model;
   }
}</code></pre>
<!-- /wp:code -->

<!-- wp:heading {"level":4} -->
<h4>Classe com ControllerAdvice e RestControllerAdvice</h4>
<!-- /wp:heading -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@ControllerAdvice
public class CustomPageExceptionHandler {
   @ExceptionHandler(NumberFormatException.class)
   public ModelAndView handleNumberFormatException(NumberFormatException nfex) {
      ModelAndView model = new ModelAndView("custom_error");
      model.addObject("errorMessage", nfex.getMessage());
      return model;
   }
}

@RestControllerAdvice
public class CustomJsonExceptionHandler {
   // podemos usar também @ResponseStatus
   @ResponseStatus(code =  HttpStatus.BAD_REQUEST)
   @ExceptionHandler(IllegalStateException.class)
   public String handleInvalidStateException(IllegalStateException rex, HttpServletResponse response) {
      System.err.println("Hey dev! We got an illegal state: " + rex.getMessage());
      response.addHeader("Reason", rex.getMessage());
      return "Sorry, we got some problem =(";
   }
}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Para maiores informações acessar links [2] e [3].</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Exemplo</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Projeto com exemplos:&nbsp;<a href="https://github.com/wesleyegberto/spring-study/tree/master/spring-exception-handler" target="_blank" rel="noopener">GitHub</a></p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Notas:</h2>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>Ao usar Spring Bott com custom error page lembre-se de empacotar com&nbsp;<strong>WAR</strong> e adicionar deps do tomcat (veja [1])</li></ul>
<!-- /wp:list -->

<!-- wp:heading -->
<h2>Links:</h2>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>[1] https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-jsp-limitations</li><li>[2] https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc</li><li>[3] https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling-custom-error-pages</li></ul>
<!-- /wp:list -->