🏠RESTFulResponse codes for RESTful Services

Response codes for RESTful Services

RESTFul Spring Boot

In our previous installment, We have made sure our application follow hyper-media format called HAL. However, At most of the controller methods we have been throwing RuntimeException. These by design would cause 500 Error codes. But REST principles dictates that proper message or indication must be given back to client when possible.

GET http://localhost:8080/carts/4 HTTP/1.1 500 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 12 Nov 2020 23:01:24 GMT Keep-Alive: timeout=60 Connection: keep-alive { "timestamp": "2020-11-12T23:01:24.911+00:00", "status": 500, "error": "Internal Server Error", "message": "", "path": "/carts/4" }
Code language: JavaScript (javascript)

For example if a resource was not available, then a 404 not found would be an appropriate response. Let’s try to achieve this behaviour. I will take the CartController for this example. Let’s try to respond back with a 404 if a cart with given id does not exist.

Here is attempt number 1 using the spring-mvc’s ResponseEntity wrapper.

@GetMapping("/{cartId}") ResponseEntity<EntityModel<Cart>> get(@PathVariable Integer cartId) { Optional<Cart> byId = cartRepository.findById(cartId); if (!byId.isPresent()) { return ResponseEntity.notFound().build(); } EntityModel<Cart> entityModel = EntityModel.of(byId.get(), linkTo(methodOn(CartController.class).get(cartId)).withSelfRel()); return ResponseEntity.ok().body(entityModel); }
Code language: JavaScript (javascript)

Even though this would work, There are couple of problems with this approach. Even before this change, the response code was already for records that exist. This is additional code debt. Also, we can’t keep checking the records for every possible scenario.

Here is an attempt with cleaner approach. First create a new Exception for scenarios where resources that doesn’t exist. Make sure this exception is extending RuntimeException. Annotate this Exception with @ResponseStatus.

package com.springhow.examples.springboot.rest.exceptions; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(HttpStatus.NOT_FOUND) public class ResourceNotFoundException extends RuntimeException { }
Code language: CSS (css)

Here, This @ResponseStatus bind annotation is part of SpringMVC and it contains an already existing auto-configuration that will take care of the Response’s status code. Now throw this exception wherever you want to have 404 response code if the resource doesn’t exist. So the attempt 1 can be re-written as

@GetMapping("/{cartId}") EntityModel<Cart> get(@PathVariable Integer cartId) { Cart cart = cartRepository.findById(cartId).orElseThrow(ResourceNotFoundException::new); return EntityModel.of(cart, linkTo(methodOn(CartController.class).get(cartId)).withSelfRel()); }
Code language: JavaScript (javascript)

let’s try calling this API for an invalid Cart id.

GET http://localhost:8080/carts/4 HTTP/1.1 404 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 12 Nov 2020 23:05:33 GMT Keep-Alive: timeout=60 Connection: keep-alive { "timestamp": "2020-11-12T23:05:33.911+00:00", "status": 404, "error": "Not Found", "message": "", "path": "/carts/4" }
Code language: JavaScript (javascript)

Notice how the response HTTP code as well as the status error message has changed. In the same regard, Some of the responses may need positive varients of HTTP response codes. For example a DELETE method’s appropriate response code would be 204 No Content. And a create method would return 201 Created or 202 Accepted as a response. As these are success cases, there are no exceptions to use. But there is also an easier way to do this. Just annotate the method with @ResponseStatus as shown below.

@PostMapping("/") @ResponseStatus(HttpStatus.CREATED) EntityModel<Cart> create(@RequestBody Cart cart) { cart.setStatus(CartStatus.NEW); Cart created = cartRepository.save(cart); return EntityModel.of(created, linkTo(methodOn(CartController.class).get(created.getId())).withSelfRel()); }
Code language: JavaScript (javascript)

Summary

To conclude, We are at the end of this series and I would like to recollect what we have learned. In the beginning of this series we learned about Restful services and its basics. Once we knew that we were in the right track we made sure the service follows all RESTful principles by implementing hateoas and appropriate messages and error codes.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *