0

I am trying to build my first spring boot based rest api and I'm trying to understand what are the common practices used to return 404 in case a resource was not found.

First of all I don't know if I should consider not finding a resource an "exceptional event" or just something that happens usually and my application should handle often.

Most of the solutions I found suggest the use of an annotated exception that tells the handlers to return a 404 in the case of a resource not found.

Eg:

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found")
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException() {
        super();
    }

    public ResourceNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public ResourceNotFoundException(String message) {
        super(message);
    }

    public ResourceNotFoundException(Throwable cause) {
        super(cause);
    }
}

Now I am wondering, considering the fact that I use the simplest structure for my code:

  1. Controller
  2. Service
  3. Repository

where am I supposed to throw it? at the repository level? or just return null and throw the exception at the Controller level? would that be more efficient or it's just a bad practice?

Otrebor
  • 416
  • 4
  • 12

5 Answers5

1

The way you return errors is tightly coupled to your API, whereas your service code should be API agnostic. In other words, if you needed to add a SOAP API next to the REST API for some reason, the service code should be able to service both APIs. Therefore, anything that is tightly coupled to the API should be handled in the layer which implements the API, in this case your controller layer.

At my current place of employment, we check for existence of the resource being acted on in the Controller. Since we use Hibernate, once the entity is retrieved from the database, it remains in the session cache for the duration of the session and does not incur additional cost to retrieve a second time in the service layer should you choose not to pass the entity down to the service.

In SpringBoot, the org.springframework.data.rest.webmvc.ResourceNotFoundException is bound to 404 NOT_FOUND. Therefore you would not need to implement any kind of exception handler for the API to return a response with a 404 http status code.

John Camerin
  • 677
  • 5
  • 15
  • I don't really understand why throwing an exception makes my service code tightly coupled with my API. Why an handler for the SOAP API won't be able to handle the exception? I thought that having my own exception instead of using the one included in spring libraries was avoiding this tight coupling. Can you elaborate on this please? Thank you in advance – Otrebor Nov 24 '18 at 17:47
  • 1
    Its not that throwing an exception creates a tight coupling... coupling is turning an exception into something API specific. For us, this was one consideration among others. One of the things I like about checking the request at the controller layer is the service methods do not need to check for existence of the request parameters, i.e. they can consider them safe. This then allows our service methods to focus on business logic. These arent black and white choices... its perfectly valid to check at the service layer. – John Camerin Nov 24 '18 at 17:59
  • So using the same exception and a specific handler would solve the problem? then I could use the exception at any level in the code. Is that what you mean? – Otrebor Nov 24 '18 at 18:03
  • Yes, you could do that if you like – John Camerin Nov 24 '18 at 18:04
1

The best practice is to throw the Exception from service to controller and handle the exception with proper HttpStatus in @RestControllerAdvice class.

For example

Exception thrown from service class to controller

@Service
public class ResourceServiceImpl implements ResourceService {

    @Override
    public void findById(String id) throws ResourceNotFoundException{
        throw new ResourceNotFoundException("your_error_code", "msg");
    }

}

ControllerAdvice class example to handle the exception and send the response for your rest API with your HTTP Status and your defined ErrorResponse class object as JSON.

@RestControllerAdvice
public class ErrorHandler {

    @ExceptionHandler(value = { ResourceNotFoundException.class })
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
        logger.error("Handling resource not found exception ", ex);
        return new ErrorResponse(ex.getCode(), ex.getMessage());
    } 

}
Nawal Sah
  • 126
  • 6
  • I have two questions: what is the ErrorResponse you use in your example? Why do you think an handler is necessary and the annotated Exception I use is not? With both methods I obtain a 404 not found. I'm probably missing something here :) – Otrebor Nov 24 '18 at 17:59
  • 1
    Is is best practice to use a handler to handle all exception raised in the code, so that it can send proper error response to the API call. ErrorResponse is model class here to create object with error_code, message, timestamp etc to send as JSON to describe the API response. Check the https://stackoverflow.com/questions/28902374/spring-boot-rest-service-exception-handling?rq=1 for details. – Nawal Sah Nov 24 '18 at 18:34
0

You should throw your exception in the controller.

  • I'm more interested about the reasons behind this choice. can you explain why you suggest that? as you can see there are different opinions :) – Otrebor Nov 24 '18 at 17:52
0

Assuming that you are using last version of spring boot so the best practice is throws the exception in Service because you can find an entity or not.

@Service
public class EntityService {

    @Autowired
    private EntityRepository repository;

    public Entity findById (Long id) {
       return repository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException(String.format("Entity not found with id %d", id)));
    }
}
Jonathan JOhx
  • 5,784
  • 2
  • 17
  • 33
0
ResponseEntityExceptionHandler is the default implementation of Spring for handling of various error .In order to customize the error, override the method.     

 @ControllerAdvice
            @Slf4j
            public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

         @ExceptionHandler(value = { ResourceNotFoundException.class })
            @ResponseStatus(value = HttpStatus.NOT_FOUND)
            public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
                logger.error("Handling resource not found exception ", ex);
                return new ErrorResponse(ex.getCode(), ex.getMessage());
            } 
            }
Roshan Oswal
  • 111
  • 1
  • 2