title: Spring Boot - Global Custom Exception Handling Mechanism using RestControllerAdvice
body: <p>Am using Spring Boot for Restful Web Services. </p>
  
  <p>Trying to setup a global custom exception handling mechanism which relies on @RestControllerAdvice which can handle exceptions that are known but also not known.</p>
  
  <p>pom.xml</p>
  
  <pre><code>&lt;parent&gt;
      &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
      &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
      &lt;version&gt;1.5.4.RELEASE&lt;/version&gt;
  &lt;/parent&gt;
  
  &lt;properties&gt;
      &lt;java.version&gt;1.8&lt;/java.version&gt;
  &lt;/properties&gt;
  
  &lt;repositories&gt;
      &lt;repository&gt;
          &lt;id&gt;spring-releases&lt;/id&gt;
          &lt;url&gt;https://repo.spring.io/libs-release&lt;/url&gt;
      &lt;/repository&gt;
  &lt;/repositories&gt;
  
  &lt;pluginRepositories&gt;
      &lt;pluginRepository&gt;
          &lt;id&gt;spring-releases&lt;/id&gt;
          &lt;url&gt;https://repo.spring.io/libs-release&lt;/url&gt;
      &lt;/pluginRepository&gt;
  &lt;/pluginRepositories&gt;
  
  &lt;dependencies&gt;
      &lt;!-- Spring --&gt;
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
      &lt;/dependency&gt;
  
      &lt;dependency&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
          &lt;scope&gt;test&lt;/scope&gt;
      &lt;/dependency&gt;
  &lt;/dependencies&gt;
  </code></pre>
  
  <p>GlobalControllerExceptionHandler:</p>
  
  <pre><code>@RestControllerAdvice
  public class GlobalControllerExceptionHandler {
  
      private static final Logger LOG = Logger.getLogger(GlobalControllerExceptionHandler.class);
  
      @ExceptionHandler(value = { ConstraintViolationException.class })
      @ResponseStatus(HttpStatus.BAD_REQUEST)
      public ApiErrorResponse constraintViolationException(ConstraintViolationException ex) {
          LOG.error(ex.getCause().toString());
          return new ApiErrorResponse(400, "Bad Request");
      }
  
      @ExceptionHandler(value = { NoHandlerFoundException.class })
      @ResponseStatus(HttpStatus.NOT_FOUND)
      public ApiErrorResponse noHandlerFoundException(Exception ex) {
          LOG.error(ex.getCause().toString());
          return new ApiErrorResponse(404, "Resource Not Found");
      }
  
      @ExceptionHandler(value = { Exception.class })
      @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
      public ApiErrorResponse unknownException(Exception ex) {
          LOG.error(ex.getCause().toString());
          return new ApiErrorResponse(500, "Internal Server Error");
      }
  }
  </code></pre>
  
  <p>ApiErrorResponse:</p>
  
  <pre><code>public class ApiErrorResponse {
  
      private int status;
      private String message;
  
      public ApiErrorResponse(int status, String message) {
          this.status = status;
          this.message = message;
      }
  
      public int getStatus() {
          return status;
      }
  
      public String getMessage() {
          return message;
      }
  
      @Override
      public String toString() {
          return new ToStringBuilder(this).append(status)
                                          .append(message)
                                          .toString();
      }
  }
  </code></pre>
  
  <p>The problem with this is when I use a 3rd party library to do something, 
  the unknown exception might be a 404 but is returned as a 500!</p>
  
  <p>e.g. Using ElasticSearch with an unknown index (deliberately to see type of Exception):</p>
  
  <pre><code>{
    "timestamp": 1501236796640,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "org.elasticsearch.client.ResponseException",
    "message": "POST http://localhost:9200/fn3r4343/_search?pretty=true: HTTP/1.1 404 Not Found"
          {
          "error": {
              "root_cause": [
                  {
                      "type": "index_not_found_exception",
                      "reason": "no such index",
                      "resource.type": "index_or_alias",
                      "resource.id": "fn3r4343",
                      "index_uuid": "_na_",
                      "index": "fn3r4343"
                  }
              ],
              "type": "index_not_found_exception",
              "reason": "nosuchindex",
              "resource.type": "index_or_alias",
              "resource.id": "fn3r4343",
              "index_uuid": "_na_",
              "index": "fn3r4343"
          }
          {  "root_cause" : 
              [ 
                  { 
                     "type" :"index_not_found_exception", 
                     "reason" : no such index", "resource.type" : "index_or_alias", 
                     "resource.id" : "fn3r4343",
                     "index_uuid" : "_na_",
                     "index" : "fn3r4343"
                  }
              ],
              [  
                      {
                    "type" : "index_not_found_exception",
                    "reason" : "no such index", 
                    "resource.type" : "index_or_alias", 
                    "resource.id" : "fn3r4343", 
                    "index_uuid" : "_na_", 
                    "index" : "fn3r4343"  
                  }, 
                "status": 404
                ]
          }
          "path": "/myapp/search"
  }
  </code></pre>
  
  <p>As one can see, this returns as a HTTP 500 Status but in the payload its really HTTP 404!</p>
  
  <p>What I am seeking is to return is this:</p>
  
  <pre><code>{ 
      "message" : "Index Not Found Exception",
      "status"  : "HTTP 404"
  }
  </code></pre>
  
  <p>And for known HTTP 404 Exceptions:</p>
  
  <pre><code>{ 
      "message" : "Not Found",
      "status"  : "HTTP 404"
  }
  </code></pre>
  
  <p>Is there a good practice / mechanism to use RestControllerAdvice to catch any type of Exception and customize the response into a JSON format which is readable / useful for a client using the REST API? </p>
  
  <p>This post isn't really specific to Elastic Search but is seeking how by trying to use @RestControllerAdvice to handle any type of Exception put the proper response for a client app...</p>