@RestControllerAdvice Annotation in Spring
Tags: spring, webdev, todayilearned, java
Published: 2021-04-15 13:38:30

In this tutorial, we're gonna look at an Spring Boot exception handling example that uses <code>@RestControllerAdvice</code> annotation. I also show you the comparison between <code>@RestControllerAdvice</code> and <code>@ControllerAdvice</code> along with the use of <code>@ExceptionHandler</code> annotation.

Full Article: [https://bezkoder.com/spring-boot-restcontrolleradvice/](https://bezkoder.com/spring-boot-restcontrolleradvice/)

<h2>Rest API exception handling</h2>
We've created Rest Controller for CRUD Operations and finder method.
Let look at the code:
(step by step to build the Rest APIs is in:
- <a href="https://bezkoder.com/spring-boot-jpa-h2-example/">Spring Boot Data JPA + H2 CRUD example</a>
- <a href="https://bezkoder.com/spring-boot-jpa-crud-rest-api/">Spring Boot Data JPA + MySQL CRUD example</a>
- <a href="https://bezkoder.com/spring-boot-postgresql-example/">Spring Boot Data JPA + PostgreSQL CRUD example</a>
- <a href="https://bezkoder.com/spring-boot-mongodb-crud/">Spring Boot MongoDB CRUD example</a>
- <a href="https://bezkoder.com/spring-boot-cassandra-crud/">Spring Boot Cassandra CRUD example</a>)

```java
@RestController
public class TutorialController {

  @Autowired
  TutorialRepository tutorialRepository;

  @GetMapping("/tutorials")
  public ResponseEntity&lt;List&lt;Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
    try {
      ...
      return new ResponseEntity&lt;>(tutorials, HttpStatus.OK);
    } catch (Exception e) {
      return new ResponseEntity&lt;>(null, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
  
  @GetMapping("/tutorials/{id}")
  public ResponseEntity&lt;Tutorial> getTutorialById(@PathVariable("id") long id) {
    Optional&lt;Tutorial> tutorialData = tutorialRepository.findById(id);

    if (tutorialData.isPresent()) {
      return new ResponseEntity&lt;>(tutorialData.get(), HttpStatus.OK);
    } else {
      return new ResponseEntity&lt;>(HttpStatus.NOT_FOUND);
    }
  }
  
  @PutMapping("/tutorials/{id}")
  public ResponseEntity&lt;Tutorial> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
    Optional&lt;Tutorial> tutorialData = tutorialRepository.findById(id);

    if (tutorialData.isPresent()) {
      ...
      return new ResponseEntity&lt;>(tutorialRepository.save(_tutorial), HttpStatus.OK);
    } else {
      return new ResponseEntity&lt;>(HttpStatus.NOT_FOUND);
    }
  }

  ...

  @DeleteMapping("/tutorials/{id}")
  public ResponseEntity&lt;HttpStatus> deleteTutorial(@PathVariable("id") long id) {
    try {
      tutorialRepository.deleteById(id);
      return new ResponseEntity&lt;>(HttpStatus.NO_CONTENT);
    } catch (Exception e) {
      return new ResponseEntity&lt;>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @DeleteMapping("/tutorials")
  public ResponseEntity&lt;HttpStatus> deleteAllTutorials() {
    // try and catch
  }

  @GetMapping("/tutorials/published")
  public ResponseEntity&lt;List&lt;Tutorial>> findByPublished() {
    // try and catch
  }
}
```

You can see that we use try/catch many times for similar exception (<strong>INTERNAL_SERVER_ERROR</strong>), and there are also many cases that return <strong>NOT_FOUND</strong>.

Is there any way to keep them simple, any way to attach the error response message smartly and flexibility?
Let's solve the problem now.

<h2>@RestControllerAdvice annotation</h2>
Spring supports exception handling by a global Exception Handler (<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ExceptionHandler.html">@ExceptionHandler</a>) with Controller Advice (<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RestControllerAdvice.html">@RestControllerAdvice</a>).

```java
@RestControllerAdvice
public class ControllerExceptionHandler {
  
  @ExceptionHandler(value = {ResourceNotFoundException.class, CertainException.class})
  @ResponseStatus(value = HttpStatus.NOT_FOUND)
  public ErrorMessage resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ErrorMessage message = new ErrorMessage(
        status,
        date,
        ex.getMessage(),
        description);
    
    return message;
  }
}
```

The <code>@RestControllerAdvice</code> annotation is specialization of <code>@Component</code> annotation so that it is auto-detected via classpath scanning. It is a kind of interceptor that surrounds the logic in our Controllers and allows us to apply some common logic to them. 

Rest Controller Advice's methods (annotated with <code>@ExceptionHandler</code>) are shared globally across multiple <code>@Controller</code> components to capture exceptions and translate them to HTTP responses. The <code>@ExceptionHandler</code> annotation indicates which type of Exception we want to handle. The <code>exception</code> instance and the <code>request</code> will be injected via method arguments.

By using two annotations together, we can:
<ul>
	<li>control the body of the response along with status code</li>
	<li>handle several exceptions in the same method</li>
</ul>

How about <code>@ResponseStatus</code>?

<code>@RestControllerAdvice</code> annotation tells a controller that the object returned is automatically serialized into JSON and passed it to the <code>HttpResponse</code> object. You only need to return Java body object instead of <code>ResponseEntity</code> object. But the status could be always OK (200) although the data corresponds to exception signal (404 - Not Found for example). <code>@ResponseStatus</code> can help to set the HTTP status code for the response:

```java
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public ErrorMessage resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
  // ...    
  return message;
}
```

<h2>@RestControllerAdvice with @ResponseEntity</h2>
If you use <code>@RestControllerAdvice</code> without <code>@ResponseBody</code> and <code>@ResponseStatus</code>, you can return <code>ResponseEntity</code> object instead.

```java
@RestControllerAdvice
public class ControllerExceptionHandler {

  @ExceptionHandler(ResourceNotFoundException.class)
  @ResponseStatus(value = HttpStatus.NOT_FOUND)
  public ErrorMessage resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ErrorMessage message = new ErrorMessage(...);
    
    return message;
  }
}
```

<h2>@ControllerAdvice vs @RestControllerAdvice</h2>

For more details and implementing the example, please visit:
[https://bezkoder.com/spring-boot-restcontrolleradvice/](https://bezkoder.com/spring-boot-restcontrolleradvice/)

## Further Reading
If you want to add Pagination to this Spring project, you can find the instruction at:
<a href="https://bezkoder.com/spring-boot-pagination-filter-jpa-pageable/">Spring Boot Pagination & Filter example | Spring JPA, Pageable</a>

To sort/order by multiple fields:
<a href="https://bezkoder.com/spring-data-sort-multiple-columns/">Spring Data JPA Sort/Order by multiple Columns | Spring Boot</a>

Or way to write Unit Test for the JPA Repository:
<a href="https://bezkoder.com/spring-boot-unit-test-jpa-repo-datajpatest/">Spring Boot Unit Test for JPA Repositiory with @DataJpaTest</a>

More Practice:
- <a href="https://bezkoder.com/spring-boot-file-upload/">Spring Boot Multipart File upload example</a>
- <a href="https://bezkoder.com/spring-boot-jwt-authentication/">Spring Boot Token based Authentication with Spring Security & JWT</a>