简体   繁体   中英

Handle Resource not found in Rest API

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 ?

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:

  • 204 = The request was succesful, but there's nothing.
  • 404 = The request was not succesful, the resource does not exist

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.

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();
    }
  1. If your endpoint gets book by id and the book does not exists, return 400. Do not return 404. 404 is protocol error: it should be reserved for wrong URL. Now URL is correct but id is wrong. Id almost often is not guessed, but returned by previous query. It cannot disappear suddenly: if id is wrong, the request is wrong.
  2. If your endpoint gets book by title and the book does not exists, return 204. That is absolutely normal that book does not exists in such case and client should be prepared to handle 204.

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:

  • 404: wrong URL
  • 400: wrong id
  • 204: not found and that is OK

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,.....]

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 })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM