Add Exception Handling

Out of the box, our starter service maps every thrown exception to a 500 Internal Server error. In order to provide better feedback when exceptions are thrown, we can create custom error handlers in our service.

Begin by adding a folder named exception at the same level as your resource and health source directories. Here you will create the file GlobalExceptionHandler.java that will handle any defined exceptions as specified.

In this example, we just return a response with the proper HTTP status for HttpClientErrorException. Any unlisted exceptions will still be thrown as 500 errors.

The example below will serve as your base handler; it can be modified to include other exceptions as needed.

package gov.va.mobile.starter.v1.service.exception;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpClientErrorException;


@ControllerAdvice
@Slf4j
@RequiredArgsConstructor
class GlobalExceptionHandler {

  @ExceptionHandler(HttpClientErrorException.class)
  public ResponseEntity<Object> handleClientConflict(final HttpClientErrorException hcee) {
      log.info("Error requesting data from secondary service", hcee);
      return ResponseEntity.status(hcee.getStatusCode()).build();
  }
}

Add a Custom Exception

In the services that we write, we often want more data than what’s present in a stock exception, but we don’t want to re-write what Spring does out of the box. To facilitate this, let’s create a couple of custom exceptions that take advantage of Spring’s ResponseStatusException. These will get automatically handled by Spring, so we won’t need to add them to our exception handler class.

First, add TokenExistsException to your exception package, extending ResponseStatusException.

package gov.va.mobile.starter.v1.service.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;

import java.io.Serial;

public class TokenExistsException extends ResponseStatusException {

  @Serial
  private static final long serialVersionUID = -1L;

  public TokenExistsException(final Throwable throwable, final String message) {
    super(HttpStatus.BAD_REQUEST, throwable.getMessage() + ": " + message);
  }
}

Similarly, create another exception called TokenRequiredException, but only include the message in its constructor. We will use these in the next section.

As mentioned earlier, now that you have added exception handling, your UserSessionResourceITCase.testNotFoundKey() test will no longer work. Update the test to now test for a 404 Not Found Error. Once you have updated your test, run mvn clean install -Pwith-skaffold to verify all tests have successfully executed.