Reputation: 508
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:
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?
Upvotes: 0
Views: 816
Reputation: 111
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());
}
}
Upvotes: 0
Reputation: 126
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());
}
}
Upvotes: 1
Reputation: 722
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.
Upvotes: 1
Reputation: 5968
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)));
}
}
Upvotes: 0