Common HTTP Error Handling in Spring Boot

Software Engineer
Talk with our tech expert & find initial solutions the challenge, that is currently crucial for your business

Error handling – for me this phrase associates with angry frontend developers accusing backend team of throwing exceptions without any unified response format into ether.

Table of contents:

Let’s imagine someone has failed during the development or planning process, and now we need to fix the things – how to unify the error responses with Spring Boot? My favourite is usage of @ControllerAdvice. It is annotation used to define class that contains @ExceptionHandler annotated methods which define how to handle exceptions.

Custom error responses

To unify the logic, we need to prepare:

  1. Response body class – used in each error response.
  2. Common method to return the same format for each handled exception.
  3. Exception handler classes to define how to handle given exceptions.

Preparation

Assuming we are working on errors for employee manager, and we have employees/{id} endpoint which throws EmployeeNotFoundException when employee doesn’t exist, let’s create/add spock integration test in EmployeesControllerTest:

    def "should respond with NOT_FOUND Http status code when user with given firstName doesn't exist"() {
        when:
        def resultingStatusCode
        def resultingBody
        try {
            restTemplate.getForEntity("http://localhost:$runningServerPort/employees/test-id", EmployeesDto)
        } catch (HttpStatusCodeException e) {
            resultingStatusCode = e.statusCode
            resultingBody = new JsonSlurper().parseText(e.getResponseBodyAsString())
        }

        then:
        resultingStatusCode == HttpStatus.NOT_FOUND
        with(resultingBody) {
            message == "Employee (id = 'test-id') doesn't exist."
            code == "employee_not_found"
            details == [[employeeId: "test-id"]]
        }
    }

Of course test will fail as the response is completely different than what we expect:

response before spring

spring boot error handling

Response body

First of all we need to prepare body of exceptional response. Let’s name it ErrorResponse and put in the root of the project:

class ErrorResponse {
    private final String message;
    private final String code;          // code for frontend to tell which message it needs to show for user 
    private final List<Object> details; // ie. details which field in input failed
    
    // ... constructors, builders, getters, setters, equals, hashcode, toString ... but I prefer to use Lombok instead ;)
}

Don’t you think it would be nice to define enum with error codes, just in case if there will be other developers working with that code? Let’s create it:

enum ErrorCode {
   EMPLOYEE_NOT_FOUND("employee_not_found");

   private final String code;

   ErrorCode(String code) {
       this.code = code;
   }

   public String getCode() {
       return this.code;
   }
}

Common response

Then we need to create abstract class with a method that will define a response. Where? It depends on the project, but probably on the project’s root as we need to access it from each feature’s package.

public abstract class HttpResponseExceptionHandler {
    protected ResponseEntity<ErrorResponse> getErrorResponseEntity(
            Exception e,
            String errorCode,
            List<Object> details,
            HttpStatus status) {
        ErrorResponse errorResponse = new ErrorResponse(e.getMessage(), errorCode, details);
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.set("Content-type", MediaType.APPLICATION_JSON_VALUE);
        return new ResponseEntity<>(errorResponse, headers, status);
    }
}

Exception handler

Next thing is to prepare @ControllerAdvice annotated classes for each functionality package. It is our place to define exception handlers for functionality errors:

@ControllerAdvice
class EmployeeExceptionHandler extends HttpResponseExceptionHandler {
    
    @ExceptionHandler(value = {EmployeeNotFoundException.class})
    ResponseEntity<ErrorResponse> handleCustomerAlreadyExists(EmployeeNotFoundException e) {
        Map<String, String> detailsMap = Collections.singletonMap("employeeId", e.getEmployeeId());
        return getErrorResponseEntity(
                e,
                ErrorCode.EMPLOYEE_NOT_FOUND.getCode(),
                Collections.singletonList(detailsMap),
                HttpStatus.NOT_FOUND);
    }
}

Of course, in @ExceptionHandler’s value argument you can define more Exception classes if they fit the same mapping.

Verification

Run previously prepared test:

error handling spring boot

Now check the response:

handling error in spring boot

It works!

Overriding default Spring error responses

You’ve probably noticed that there are some default spring exception handlers. They were created to simplify responses and catch possible exceptions like HttpRequestMethodNotSupportedException. Let’s override mentioned one.

Traditionally, create test:

    def "should override default spring response for NotSupportedMethodException"() {
        when:
        def resultingStatusCode
        def resultingBody
        try {
            restTemplate.put("http://localhost:$runningServerPort/employees/test-id", EmployeesDto)
        } catch (HttpStatusCodeException e) {
            resultingStatusCode = e.statusCode
            resultingBody = new JsonSlurper().parseText(e.getResponseBodyAsString())
        }

        then:
        resultingStatusCode == HttpStatus.METHOD_NOT_ALLOWED
        with(resultingBody) {
            message == "Not supported HTTP method. Available methods are: [GET]"
            code == "not_supported_http_method"
        }
    }

http error handling in spring boot

Test is failing, so we need to override handleHttpRequestMethodNotSupported method from ResponseEntityExceptionHandler:

@ControllerAdvice
class SpringRESTExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
            HttpRequestMethodNotSupportedException ex,
            HttpHeaders headers,
            HttpStatus status,
            WebRequest request) {

        pageNotFoundLogger.warn(ex.getMessage());

        Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
        if (!CollectionUtils.isEmpty(supportedMethods)) {
            headers.setAllow(supportedMethods);
        }
        ErrorResponse errorResponse = new ErrorResponse(
                String.format("Not supported HTTP method. Available methods are: %s", supportedMethods),
                "not_supported_http_method",
                null);
        return handleExceptionInternal(ex, errorResponse, headers, HttpStatus.METHOD_NOT_ALLOWED, request);
    }
}

Of course, you should replace "not_supported_http_method" with some enum or constant

Run the test:

common http error handling in spring boot

Let’s check the request now:

second after test spring

Conclusion

As you can see, it’s really straightforward solution. You can simply replace default Spring error responses as well as your custom exception error responses. Once the response format and @ControllerAdvice classes are defined, the only thing you have to do is to prepare new handler methods.

Would you like to talk with our experts about technology solutions for your business?

5/5 - (7 votes)
Software Engineer
Talk with our tech expert & find initial solutions the challenge, that is currently crucial for your business
Share on facebook
Share on linkedin

Share the article

Piotr Ostapczuk

Software consultation

Fill in the form

The more information you can provide us, the better understanding we have of who will be perfect match for your chat.

Get a reply

Get a reply in only 2 hours and schedule your 15min call.

Receive recommendations

Receive an e-mail with recommendations crafted to your need & business.

Share on facebook
Share on linkedin

We use cookies to ensure you get the best experience on our website.