Error Handling in your Spring boot RESTful api

In this post we would be adding Exception handling in our ItemAPI from previous post.
Spring provides us with na easy to integrate component to handle global exceptions in our application.

Assuming a user tries to access an Item that does not exit. By RESTful specification, we are suppose to response with HTTP code 404 but by default all exceptions in spring boot web application fails with HTTP 500 code. To make this modification such that appropriate HTTP code are send for corresponding exception in our application we follow these steps

Create a new exception package and add a general ErrorResponse class

/itemApi/src/main/java/com/eddytnk/itemApi/exception/ErrorResponse.java

package com.eddytnk.itemApi.exception;

import lombok.Data;

@Data
public class ErrorResponse {

    private String code;
    private String message;
    private String path;

}

Now Create exception classes to be thrown within your application e.g
BusinessValidationException for failed validation logics, ResourceNotFoundException for when resource can not be found in Database

/itemApi/src/main/java/com/eddytnk/itemApi/exception/BusinessValidationException.java

package com.eddytnk.itemApi.exception;

import lombok.Data;

@Data
public class BusinessValidationException extends RuntimeException {
    public BusinessValidationException(String message) {
        super(message);
    }
}

/itemApi/src/main/java/com/eddytnk/itemApi/exception/ResourceNotFoundException.java

package com.eddytnk.itemApi.exception;

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

Then Create a GlobalExceptionHandler class to bind exception thrown within the application and the HTTP response our of the application. Annotate this class with @ControllerAdvice

package com.eddytnk.itemApi.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = {ResourceNotFoundException.class}) // 404
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setCode("RESOURCE_NOT_FOUND");
        errorResponse.setMessage(ex.getMessage());
        errorResponse.setPath(request.getRequestURI());
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(value = {BusinessValidationException.class}) // 422
    public ResponseEntity<ErrorResponse> handleBusinessValidationException(BusinessValidationException ex, HttpServletRequest request) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setCode("VALIDATION_ERROR");
        errorResponse.setMessage(ex.getMessage());
        errorResponse.setPath(request.getRequestURI());
        return new ResponseEntity<>(errorResponse, HttpStatus.UNPROCESSABLE_ENTITY);
    }
}

Now We will modify the ItemService class to Throw these newly created Exceptions instead of the RuntimeException as before.

/itemApi/src/main/java/com/eddytnk/itemApi/service/ItemService.java

package com.eddytnk.itemApi.service;

import com.eddytnk.itemApi.exception.BusinessValidationException;
import com.eddytnk.itemApi.exception.ResourceNotFoundException;
import com.eddytnk.itemApi.model.Item;
import com.eddytnk.itemApi.repository.ItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class ItemService {

    private ItemRepository itemRepository;

    @Autowired
    public ItemService(ItemRepository itemRepository) {
        this.itemRepository = itemRepository;
    }

    public List<Item> getAllItems() {
        return itemRepository.findAll();
    }

    public Item getItem(Long id) {
        Optional<Item> itemOptional = itemRepository.findById(id);
        if (!itemOptional.isPresent()) {
            throw new ResourceNotFoundException("Item not found.");
        }
        return itemOptional.get();
    }

    public Item addNewItem(Item item) {
        return itemRepository.save(item);
    }

    public void updateItem(Long id, Item item) {
        Optional<Item> itemOptional = itemRepository.findById(id);
        if (!itemOptional.isPresent()) {
            throw new BusinessValidationException("Item to be updated those not exit found.");
        }
        Item newItem = itemOptional.get();
        newItem.setId(item.getId());
        newItem.setName(item.getName());
        newItem.setPrice(item.getPrice());
        newItem.setQuantity(item.getQuantity());
        itemRepository.save(newItem);
    }

    public void removeItem(Long id) {
        itemRepository.deleteById(id);
    }


}

Download or Clone source code on GitHub

One Response
  1. Ngouleu Tertullien

Leave a Reply

Your email address will not be published.