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();
}
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.
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 })
Related
I have a simple question regarding how do I link an endpoint through HATEOAS if that endpoint requires a valid #RequestBody? I've seen another question regarding this but with no straight answers and so I wonder if the question was not clear enough.
Take a look at the code below:
#GetMapping(path = "/notification/{id}")
#ResponseStatus(HttpStatus.OK)
public NotificationItemResponse getNotification(#PathVariable final String id) {
return notificationItemMapper.toResponse(findUseCase.findNotification(id))
.add(linkTo(methodOn(NotificationItemController.class).getNotification(id)).withSelfRel())
.add(linkTo(methodOn(NotificationItemController.class).saveNotification()).withRel("save")) <- error here, saveNotification expects a valid request body
.add(linkTo(methodOn(NotificationItemController.class).revokeNotification(id)).withRel("revoke"))
.add(linkTo(methodOn(NotificationItemController.class).markNotificationAsSeen(id, "{userName}")).withRel("visualize"));
}
saveNotification() is a method on this controller that requires a request body. That request body is a long json containing a notification message, target users, etc.
It doesn't seem right to create a dummy body just to pass down here, and it hardly seem right to pass a null value. What is the correct approach here? How do I correctly link a method that requires a request body? More specifically, what do I pass down as that request body?
What is the best practice here, other than passing a null or dummy body, as I stated before as a non-optimal solution?
The question is pretty old, but I faced the same issue today, and it looks pretty hard to find the correct answer.
After some research, I found this example in Spring HATEOAS Docs: 3.3 Affordances
#GetMapping("/employees/{id}")
public EntityModel<Employee> findOne(#PathVariable Integer id) {
Class<EmployeeController> controllerClass = EmployeeController.class;
// Start the affordance with the "self" link, i.e. this method.
Link findOneLink = linkTo(methodOn(controllerClass).findOne(id)).withSelfRel();
// Return the affordance + a link back to the entire collection resource.
return EntityModel.of(EMPLOYEES.get(id), //
findOneLink //
.andAffordance(afford(methodOn(controllerClass).updateEmployee(null, id)))
.andAffordance(afford(methodOn(controllerClass).partiallyUpdateEmployee(null, id))));
}
In this case, they use a method afford(...), which works pretty similar to linkTo(...). Looks like passing a null object is a best practice, or at least it is encouraged by the Spring team. So in your case it would look like this:
.add(linkTo(methodOn(NotificationItemController.class).saveNotification(null)).withRel("save"))
I have developed a REST end-point in Springboot that takes a String ID and responds with a ModelAndView. This end-point is marked with #Cacheable annotation. Now there are two things that can happen at the given end-point.
Case 1: The request ID exists in the DB and yields a URL to which redirection needs to happen. In this case, response should be cached so that upon consecutive requests of the same ID, the result can be served from Cache
Case 2: The requested ID doesn't exist in the DB and thus redirection should happen to a specific URL and no caching should be done in this scenario.
So below is my method
#GetMapping("{id}")
#Cacheable(value = "url-single", key = "#id", unless = "#result.view!=\"redirect:/notfound\"")
public ModelAndView redirect(#PathVariable("id") String id, ServletRequest servletRequest,
ServletResponse servletResponse) {
HttpServletRequest request = HttpServletRequest.class.cast(servletRequest);
LOG.info("Redirection request from: {} for Short URL Key: {}", request.getRemoteAddr(), id);
try {
Optional<String> originalUrlOptional = urlManagerService.retrieveOriginalUrl(id);
if (originalUrlOptional.isPresent() && !StringUtils.isEmpty(originalUrlOptional.get())) {
LOG.info("Found Original URL: {} for Short URL Key: {}", originalUrlOptional.get(), id);
return new ModelAndView("redirect:https://" + originalUrlOptional.get());
}
} catch (NoSuchElementException e) {
LOG.error("Error while redirecting: {}", e.getMessage(), e);
}
return new ModelAndView("redirect:/notfound");
}
If I understand it correctly from here, the keyword unless in #Cacheable applies to the return type and in order to access any particular member variable of the return type object, we have to refer to it as #result.attributeName <comparison> <value>.
So why isn't anything being stored in my Redis cache? If I remove the unless condition, everything gets stored. Is the condition not correct?
I have been looking at your unless statement:
unless = "#result.view!=\"redirect:/notfound\""
so this means that it WONT be cached if the result.view IS NOT redirect:/notfound.
My assumption is that you want to cache it, except when redirect:/notfound.
There is a "double negative" here.
So you probably should use:
unless = "#result.view==\"redirect:/notfound\""
(so use '==' instead of '!=')
Let me know if this works!
I found where I was going wrong after playing with the unless conditional keyword for awhile. The #result inside the double quotes (") of the unless keyword is literally the object to returned. What that means is that, you can call whatever method can be called on that object.
The statement "#result.view==\"redirect:/notfound\"" was failing because the object didn't expose any member variable called view. Using the expression unless = "#result.getViewName().equals(\"redirect:/notfound\")" actually did the trick because there was a method getViewName() on the object that returned String and calling equals() on it did the actual comparison.
I guess I got stuck here because I wasn't not familiar with Spring Expression Language or SpEL
I have to following endpoint structure in Jersey:
/objects/
/objects/:id
/objects/:id/:field
/objects/:id/:field/:subfield
The IDs I'm using have a very specific format, so I first check if the format is valid before making a request to the database.
Right now I have to put this code in each of the POST, PUT, GET, DELETE functions for each of the functions that has :id as a parameter. So this just means an early return.
if (!isIdValid(id)){
return Response.status(Response.StatusType.BAD_REQUEST)
.entity("The ID you've provided is invalid")
.build();
}
(In reality the error entity is an object containing more information about the error)
And then for each function using the :field or :subfield parameters the code is similar. This checking and error-handling behavior has to be copied every time. And when I start copy-pasting stuff, I start thinking: there should be a better way?
I would like to place the :id checking code at the the /objects/:id level, and then all further nested levels are assumed have a valid ID. The same for the other parameters further nesting down.
I've been looking into using subresource locators, but then you create a function returning a new instance of the subresource. I can't put a conditional return of a Response-object at that level for if the validation fails.
#Path("{id}")
function Class<ObjectFieldResource> getObjectById(#PathParam("id") String id){
return ObjectFieldResource.class;
}
I could start throwing exceptions, but I would rather avoid that, since I don't really consider invalid input to be an exception.
How would such a structure best be implemented? I've looked at bean validation but that doesn't seem to allow me to define validation for my specific format + custom error responses.
Am I missing something in the way subresources should be implemented?
Solution 1
If you can use regexp checks instead of your isIdValid method it's possible to define your resources like this
#POST
#Path("objects/{id:\\d+}")
public Response doSmth(#PathParam("id") String id) {
...
}
In a case of invalid id format caller will have 'Not Found' response status without even reaching your doSmth method.
Obviously, you can use String constants for all equal path values.
final static String ID_RES = "objects/{id:\\d+}";
#POST
#Path(ID_RES)
public Response postSmth(#PathParam("id") String id) {
...
}
...
#GET
#Path(ID_RES)
public Object getSmth(#PathParam("id") String id) {
...
}
The can also read full description of Path#value parameter
Solution 2
Create and register at your REST server javax.ws.rs.container.ContainerRequestFilter implementation with filter method having needed URI checks.
The single filter parameter has ContainerRequestContext type from witch you can call getUriInfo for getting URI and method abortWith(Response response) which can be used for aborting caller request if your resource ids validation was failed.
See Chapter 10. Filters and Interceptors chapter of Jersey Manual.
I've developed REST services, but now I realized that I'm doing something wrong.
For example, I have a service which retrieves information about a specific device. Each device has an address: sector.room.group.id.
The URI I did for this GET method was: (...)/services_devices/{sector}/{room}/{group}/{id} But now I realized that I should not have used the '/' to separate the device address, right?
How should I pass the address to this method? Using ';' ?
My GET method is:
#GET
#Path("{sector}/{room}/{group}/{id}")
#Produces("application/json")
public String getDeviceName(#PathParam("sector") int sector, #PathParam("room") int room, #PathParam("group") int group, #PathParam("id") int id) throws Exception
{
String name = null;
try {
name = new DevicesManager().getDeviceName(sector, room, group, id);
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
There is a simple way of change this, to have a correct URI? I have this "error" in many methods.
If there is a hierarchy in your resources path variables are appropriate.
It seems in your case there is a hierarchy between devices and address, but first comes the address and after the deviceName. "deviceName" can be considered a one more hierarchy step.
The best way to reflect the above relations would be the following url:
(...)/sector/room/group/id/deviceName
You can then have another attribute of the device mapped like this:
(...)/sector/room/group/id/deviceOwner
The JAX-RS mapping would be:
#GET
#Path("{sector}/{room}/{group}/{id}/deviceName")
#Produces("application/json")
public String getDeviceName(#PathParam ...) {
//impl.
}
And yes, if the deviceName is the only relevant attribute of the resource, then you can leave out "deviceName" and your orignal mapping is correct.
If the resource at /sector/room/group/id has many attributes you should consider returning a composed object for the path:
#GET
#Path("{sector}/{room}/{group}/{id}")
#Produces("application/json")
public Device getDeviceName(#PathParam...) {
}
REST architectural style introduces HATEOAS, which means that client and server are loosely coupled. Simply the client is not aware of how the URLs look like and gets them from previous responses. (it's similar like surfing thru HTML pages). Of course there will be at least one URL, an entry point, that is known to the client. From this point of view, your need to have correct URIs is irrelevant. What's correct URI? The URI is correct when its form is aligned with RFC.
You are probably introducing URL patterns, that are not RESTful, because it implicates tight coupling between client and server (the client must be aware of URL patterns and have ability to construct URLs from them; fill up sector/room/ etc. in your case)
See also this post:
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
My advice is; don't waste your time on URL patterns, make URLs simple as is possible, flat hierarchy has also many benefits, and follow HATEOAS principle.
My service:
#POST
public String setData(#QueryParam("id") Long is, MyObject payload) {
...
}
or
#POST
public String setData(#PathParam("id") Long is, MyObject payload) {
...
}
My interceptor on the server:
Object read(MessageBodyReaderContext context) throws IOException, WebApplicationException {
Class mypayloadtype = context.getType;
InputStream mypayloadinpustream = context.getInputStream();
Long myidparam = ???????? // how to get the query or path param here?
}
EDIT: To be a bit more concrete:
What I'd like to do is to grab the XML and store it based on the parameters in a separate audit system. Maybe PreProcessInterceptor / PostProcessInterceptor are the better choices?
Any hints or alternative ways to get the param when the xml is still available for preprocessing?
Miguel
I just stumbled over the same problem today. I needed the #PathParams and #QueryParams in the read() method and ended up with something like this:
public class MyInterceptor implements PreProcessInterceptor, MessageBodyReaderInterceptor
{
private static ThreadLocal<UriInfo> uri = new ThreadLocal<UriInfo>();
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
{
uri.set(request.getUri);
...
}
public Object read(MessageBodyReaderContext context)
{
String param = uri.get().getPathParameters().getFirst("myidparam");
...
}
}
Although when thinking about it now - I'm not quite sure, if just using PreProcessInterceptor/PostProcessInterceptor will also do the trick for my (and maybe your) problem. I'll have another look tomorrow.
I am not an expert on the topic but to me it seems as if the MessageBodyReaderContext interface does not really know if it is on the server or the client side, so it cannot expose the request or its parameters / path parts etc.
So as far as I know this is not possible.
If your code knows that it lives on the server side of the rest
communication, maybe you can use a servlet filter to store the request
in a ThreadLocal and then access it from there while the request is
handled, somewhat similar to RequestContextFilter / RequestContextHolder from the spring framework? (Then the request object does not know anything about the annotations of your service, but instead one has to extract the information manually from the request. This means to have the same information in two places, so there has to be a better solution ...)
Edit: after looking at some examples I get the vague feeling that if you want to read the input stream to create an object and add path parameters to it, MessageBodyReaderInterceptor is simply not the way to go. Instead set up a MessageBodyReader which constructs the object from the request body data, and this then will be passed into the public String setData(#PathParam("id") Long is, MyObject payload), assuming that this method is annotated with a #Consumes which matches the #ConsumeMime annotation for the MessageBodyReader. There you might be able in the setData to set the missing id on the object read from the request body. Some examples related to this seem to be here: How to get full REST request body using Jersey? (but for Jersey, not jBoss :-/)
However I am not sure if that works for you, and I also feel I completely overestimated my ability to answer this question appropriately, so I hope someone more knowledgeable comes in with a better solution.