Skip to main content
Bumped by Community user
Bumped by Community user
edited title
Link
Eoin
  • 263
  • 1
  • 3
  • 6

Unclear on whether my exception should be checked or unchecked in spring Spring boot shop application with error handling

Source Link
Eoin
  • 263
  • 1
  • 3
  • 6

Unclear on whether my exception should be checked or unchecked in spring boot application

I'm writing a simple online shop using spring boot, for learning purposes. Right now I have purchasing-service and a product-service. The purchasing-service makes requests to the product-service via REST APIs for querying for product information. All of this is working, but I'm unclear on if I'm using exceptions correctly within the product-service.

I have an endpoint in my controller called retrieveProductById that retrieves information about a product given the product id. This id is passed to a service, which then uses a repository to look up the product in the database and return the product information. If the product id does not exist in the database, it throws a ProductDoesNotExistException. This is then handled by an ErrorHandlerControllerAdvice class that simply returns a 400 bad request response with the erorr message to the caller.

My question is: should my ProductDoesNotExistException be checked, or unchecked? To me, it makes sense for it to be checked, because as a developer or even a consumer of the API, you can clearly infer that if you give an invalid product id to the API, it will throw a ProductDoesNotExistException. If I change the exception to be unchecked, this is not as clear, as the IDE does not enforce me to explicitly add a throws ProductDoesNotExistException to the method signature.

So what would be the correct approach here? I do feel like it almost doesn't matter which I choose, as the controller advice class will handle the exception regardless of the type when it is thrown, so is the decision just a matter of preference on the developer part? See code below:

Controller

@RestController
@RequestMapping("/products")
@Slf4j
public class ProductController {

    private final ProductService productService;
    private final RestApiResponseFactory restApiResponseFactory;

    public ProductController(final ProductService productService,
            final RestApiResponseFactory restApiResponseFactory) {
        this.productService = productService;
        this.restApiResponseFactory = restApiResponseFactory;
    }


    @GetMapping("/{productId}")
    public ResponseEntity<RestApiResponse<ProductVO>> retrieveProductById(
            @PathVariable final String productId) throws ProductDoesNotExistException {
        log.info("Request made to find product {}", productId);
        final ProductVO productVO = this.productService.retrieveProductById(
                Integer.parseInt(productId));
        return ResponseEntity.ok(this.restApiResponseFactory.createSuccessResponse(productVO));
    }
}

Service

@Service
@Slf4j
public class ProductService {

    private final ProductRepository productRepository;

    public ProductService(final ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    public ProductVO retrieveProductById(final int productId) throws ProductDoesNotExistException {
        final Product product = findProductById(productId).orElseThrow(
                () -> new ProductDoesNotExistException(productId));
        return new ProductVO(product);
    }

    private Optional<Product> findProductById(final int productId) {
        return this.productRepository.findById(productId);
    }

}

Exception

public class ProductDoesNotExistException extends Exception {

    public ProductDoesNotExistException(final int productId) {
        super("No product with Product ID " + productId + " exists");
    }
}

Error handler

@ControllerAdvice
public class ErrorHandlerControllerAdvice {

    private final RestApiResponseFactory restApiResponseFactory;

    public ErrorHandlerControllerAdvice(final RestApiResponseFactory restApiResponseFactory) {
        this.restApiResponseFactory = restApiResponseFactory;
    }

    /**
     * Return 400 if a product does not exist
     */
    @ExceptionHandler(ProductDoesNotExistException.class)
    public ResponseEntity<RestApiResponse> handleProductDoesNotExistException(
            final ProductDoesNotExistException e) {
        return ResponseEntity.badRequest()
                .body(restApiResponseFactory.createErrorResponse(e.getMessage()));
    }

    /**
     * Catch all exception handler. In case any exceptions slip through the cracks, we want to
     * return a 500
     *
     * @param e the exception
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<RestApiResponse> handleGeneralException(final Exception e) {
        return ResponseEntity.internalServerError()
                .body(restApiResponseFactory.createErrorResponse(e.getMessage()));
    }

}