Reputation: 4940
I am developing a Rest API in spring boot. Which of the following is the best way to handle when an instance of resource not found ?
@GetMapping(value="/book/{id}")
public ResponseEntity<Book> getBook(@PathVariable String id){
Book book = bookService.getBook();
// Which is best Approach for resource instance not found ?
if(book == null) {
// This one
return new ResponseEntity<>(book, HttpStatus.NO_CONTENT);
//OR
return new ResponseEntity<>(book, HttpStatus.NOT_FOUND);
//OR
throw new DataNotFoundException("Book with id " + id + " Does not exist");
}
return new ResponseEntity<>(book , HttpStatus.OK);
}
I am clear about that when a collection of resource not found in Db then to pass an empty collection instead of null but I am not clear what to do with an instance of resource.
I have also read on StackOverflow that HttpStatus.NOT_FOUND
should be used when a Resource under the criteria cannot exist instead of do not exist in the Db.
What is best approach to handle this ?
Upvotes: 7
Views: 18070
Reputation: 4639
Someone could argue that difference between 400 and 204 is fuzzy and better always return 204. Indeed, difference may be fuzzy, but from monitoring perspective I would like to know when everything is ok (no book found by title) and when something smells (no book found by id).
I know that my answer does not comply REST directives (or maybe does not comply). I don't care it too much. I simply think that 404 should be reserved for application server and should not be used by application. Reason is already explained in other answer here.
Summary:
Upvotes: 3
Reputation: 44715
When working with Spring MVC, you usually have two choices when returning your result, either you work with plain objects, or you work with the ResponseEntity
class. Neither of those is better than the other. Additionally, you can decide whether or not you separate your error handling using exceptions or not.
Given that, your third scenario by throwing an exception is essentially the same as one of your first two options. By default, throwing an exception will result into a 500 Internal Server Error, but it can be changed by using the @ResponseStatus
annotation, for example:
@ResponseStatus(HttpStatus.NOT_FOUND) // Or @ResponseStatus(HttpStatus.NO_CONTENT)
public class DataNotFoundException extends RuntimeException {
}
Alternatively, you can also define an exception handler. Again, this can be done by either working with plain objects or ResponseEntity
, for example:
@ResponseStatus(HttpStatus.NOT_FOUND) // Or @ResponseStatus(HttpStatus.NO_CONTENT)
@ExceptionHandler(DataNotFoundException.class)
public Book handleNotFound(DataNotFoundException ex) {
return null;
}
Or:
@ExceptionHandler(DataNotFoundException.class)
public ResponseEntity<Book> handleNotFound(DataNotFoundException ex) {
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND); // Or HttpStatus.NO_CONTENT
}
Again, neither is better than the other and what you choose is mostly based upon personal preference. However, you should probably use one consistently.
Now, that means that there are still two choices left, either by choosing HttpStatus.NOT_FOUND
(404) or HttpStatus.NO_CONTENT
(204). While you can technically use either status, they have a different meaning:
Now, if you request /book/123
and there's no book with ID 123, it could be seen as a resource that doesn't exist, and thus, HttpStatus.NOT_FOUND
makes most sense.
Upvotes: 10
Reputation: 2817
First of all I think that you mean @PathVariable
and not @RequestParam
for your method parameter (see difference between PathVariable and RequestParam here ).
Secondly, it will be ambiguous for the client that receives the 404 not found response as this means that :
The server has not found anything matching the requested address (URI) ( not found ). This means the URL you have typed is wrong or obsolete and does not match any document existing on the server (you may try to gradualy remove the URL components from the right to the left to eventualy retrieve an existing path).
Knowing that your return type is a ResponsEntity
, it will be more appropriate to have this :
@GetMapping(value="/book/{id}")
public ResponseEntity getBook(@PathVariable String id){
Optional<Book> book = bookService.getBook();
if(book.isPresent()) {
return ResponseEntity.status(HttpStatus.OK).body(book.get());
}
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
Upvotes: 2
Reputation: 2863
Whenever a resource cannot be found, you should indicate that to the client, most commonly using the HTTP Status Code 404 Not Found, as you already mentioned.
For collections, simply return an empty array in the response body (alongside with response code 200 OK, this is my opinion tough), do not return 404 Not Found since the resource actually exists.
Please note that 202 No Content is a bad choice here, since the server has not successfully fulfilled the request. Instead, use this return code, for example, for a successful PUT request (you have changed internal data but return no content in the response body).
In most APIs you will encounter additional information in the response body:
{"messages":{"error":[{"code":404,"message":"Resource not found."}]}}
You will find list of all errors and their response codes with informative descriptions. One thing is important tough: Stick to one format, otherwise it will be a pain for clients. Most APIs also only use about 6-8 HTTP response codes.
Also, Spring has a number of utilities to help you out:
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order")
public class OrderNotFoundException extends RuntimeException {
// ...
}
Or, the following annotation to create a custom response format:
@ExceptionHandler({ YourException.class })
Upvotes: 0
Reputation: 21
just return 404 HttpStatus to client ,do not waste time on it.No one will request id that not exist in db normally. usually client request like model/{id} come from against your Collection [model1,model2,.....]
Upvotes: 0