Part 3: Adding the REST Services

In a series of blogs we will look at how to create a REST service using the latest version of Spring. Our service will have validation so we can make sure the data stored meets the requirements and it will be secured so only the correct people can make updates.

In the previous entry we examined how to set up the basic application with configuration. In this entry we will set up the RESTful service layer on top of our normal services.

What are RESTful services?

REST stands for REpresentational State Transfer and it is an architectural style that among other things defines a way to create web services. REST is not a standard like SOAP is, it is a style, which means that it is a recommendation and implementations will vary.

The typical REST service is stateless and uses MIME-types to convey how to read the response. There are many more aspects to REST but let's keep it simple for the purpose of brevity. When creating web services with REST we rely heavily on HTTP's request methods, GET, POST, PATCH, PUT, DELETE, etc. Each resource should also have a separate URI. Resources typically return the content as XML or JSON but that is not strictly necessary.

REST and Request Methods

In REST, request methods are also called verbs. Each verb is mapped to a different type of call. Lets quickly go over what they are and how to typically use them.

GET This is a "view" operation. This should be repeatable and should never change the data it views.
POST Used to create a new entity in the database. Depending on the system this could be repeated as many times as needed or if not allowed by the data model would return a conflict.
PUT Used to update the whole of an entity.
Used to update parts of an entity.
DELETE Well…

HTTP Status Codes

The HTTP status codes are an important part of the communication between a REST service and it's clients. Let's look at some of the most common codes.

200 - OK This means everything went fine. Typically used with GET, PUT and PATCH.
201 - Created This means the object was created and a new entity is stored. Typically used with POST operations.
204 - No Content When there is nothing to return. Typically used with DELETE.
400 - Bad Request Any request that technically is working but something in the request data is not matching what our service expected.
409 - Conflict Typically is returned when you try to create a new object but the data model restricts duplicate values of some kind. Most often used with POST, PUT and PATCH.
500 - Internal Server Error A non-recoverable error has occurred.

There are myriad others as explained in the W3 RFC.

Spring MVC

Spring has long had an MVC component and most people might be familiar with it already but lets take a look at the basics so we can see the differences when using it for REST.

The @Controller Annotation

This is related to @Service, @Component and @Repository in that it makes Spring read and instantiate the class to be used as an endpoint in the application.

The @RequestMapping Annotation

This tells Spring which URI and which verb to match to which controller. This annotation also takes arguments to match against the MIME-types of the request. We'll look into this in more detail further down.

The @PathVariable Annotation

This is used to map parts of the URI to parameters for the method in your controller.

The @RequestParam Annotation

This binds a parameter from the HTTP request to a method parameter in your controller.

Let's look at a simple example of a Controller.

@Controller
@RequestMapping("/ticket")
public class SimpleController {
 @RequestMapping(value="/{id}", method=RequestMapping.GET)
 public ModelAndView display(@PathVariable String id) {
 ...
 }
 
 @RequestMapping(value="/{id}", method=RequestMapping.POST)
 public ModelAndView update(@PathVariable String id) {
 ...
 }
}

@RequestMappings are relative, meaning that they are scanned on both the class level and the method level. In this case the "/ticket" is added to each methods value "/{id}". The two methods are seemingly using the same value of "/{id}" but they are mapped differently because of the request methods, GET and POST. This is important to remember for later when we create our REST services.

Spring 4.0 and REST

Spring 4.0 doesn't change a lot when it comes to the MVC framework, it is more focused on Java 8 and JEE 7 compatibility and lot of internal upgrades. One new thing introduced in 4.0 was a new annotation we will be looking more at.

The @RestController Annotation

This new annotation extends the behaviour of the common MVC @Controller annotation. The difference is that the new annotation sets the expectation that all methods have an implicit @ResponseBody annotation. Let's explain these two annotations a little further.

The @ResponseBody and @RequestBody Annotations

These are two annotations that let's Spring know that your input (@RequestBody) and output (@ResponseBody) are to be transformed in some way.
Spring will automatically transform any incoming JSON (using Jackson) or XML (using JAXB) depending on the MIME-type, into the object specified and the same is true for any output. This works without you having to add a single line of code as long as the correct libraries are found on the classpath. In the case of JAXB you have to have the generated class files that map to your schema.
These annotations are not new, they were introduced in Spring 3.0 and you can use them with the normal @Controller. The difference in 4.0 is that you don't need to add the @ResponseBody, it is implicitly added to all methods.

The @ResponseStatus Annotation

This annotation is used in conjunction with a @RequestMapping to return a different status than the default 200 (OK) which is the default for any @Controller method. We will use this to send the status of our call to the client.
As an alternative to using @ResponseStatus a method can also return a ResponseEntity which wraps a status and a return object to be mapped to JSON or XML by Spring.

Creating Our REST Service

Let's go back to our Ticket system that we were starting to create in the first part of this blog. This is the implementation of our REST layer.

@Transactional
@RestController("TicketFacadeV1")
@RequestMapping(value = "/ticket", 
produces = "application/vnd.captech-v1.0+json", 
consumes ="application/vnd.captech-v1.0+json")
public class TicketFacade {
 
 @Autowired
 private TicketService ticketService;
 
 @Autowired
 private MappingService mappingService;
 
 @RequestMapping(value = "/{ticketId}", method = RequestMethod.GET)
 public TicketVO read(@PathVariable long ticketId) {
 Ticket ticket = ticketService.get(ticketId);
 return mappingService.map(ticket, TicketVO.class);
 }
 
 @RequestMapping(method = RequestMethod.POST)
 @ResponseStatus(HttpStatus.CREATED)
 public TicketVO create(@RequestBody TicketVO ticket) {
 Ticket mappedTicket = mappingService.map(ticket, Ticket.class);
 Ticket persisted = ticketService.store(mappedTicket);
 return mappingService.map(persisted, TicketVO.class);
 }
 
 @RequestMapping(value = "/{ticketId}", method = RequestMethod.DELETE)
 @ResponseStatus(HttpStatus.NO_CONTENT)
 public void remove(@PathVariable long ticketId) {
 Ticket ticket = ticketService.get(ticketId);
 ticketService.delete(ticket);
 }
 
}

Lets examine this class in some more detail. The class is annotated with a @RequestMapping with some extra arguments besides a normal URI.
The consumes part means that only requests that have a "Content-Type" header matching the "application/vnd.captech-v1.0+json" is matched. The same thing goes for the produces matching the "Accept" header.
I'll explain the value of the headers later but for now what is important is that the header tells Spring to map this using the Jackson parser since we have a "application/json" request.
The @RequestMapping is exactly the same as in a normal MVC application. The method annotations build on the class ones so all methods require the same MIME-type.
Any request that doesn't have a Content-Type header will be getting a response of 415 (Unsupported Media Type) because we only serve people we know at this establishment.

Error Handling in our REST service

When things go wrong we still want to return a REST response and not the default html page. Lets look at our exception handler.

@ControllerAdvice
public class DefaultExceptionHandler {
 
 @RequestMapping(produces = {"application/vnd.captech-v1.0+json", "application/vnd.captech-v2.0+json"})
 @ExceptionHandler({MissingServletRequestParameterException.class,
 UnsatisfiedServletRequestParameterException.class,
 HttpRequestMethodNotSupportedException.class,
 ServletRequestBindingException.class
 })
 @ResponseStatus(value = HttpStatus.BAD_REQUEST)
 public @ResponseBody MapString, Object> handleRequestException(Exception ex) {
 MapString, Object> map = Maps.newHashMap();
 map.put("error", "Request Error");
 map.put("cause", ex.getMessage());
 return map;
 }
 
@RequestMapping(produces = {"application/vnd.captech-v1.0+json", "application/vnd.captech-v2.0+json"})
 @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
 @ResponseStatus(value = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
 public @ResponseBody MapString, Object> handleUnsupportedMediaTypeException(HttpMediaTypeNotSupportedException ex) throws IOException {
 MapString, Object> map = Maps.newHashMap();
 map.put("error", "Unsupported Media Type");
 map.put("cause", ex.getLocalizedMessage());
 map.put("supported", ex.getSupportedMediaTypes());
 return map;
 }
 
 @RequestMapping(produces = {"application/vnd.captech-v1.0+json", "application/vnd.captech-v2.0+json"})
 @ExceptionHandler(Exception.class)
 @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
 public @ResponseBody MapString, Object> handleUncaughtException(Exception ex) throws IOException {
 MapString, Object> map = Maps.newHashMap();
 map.put("error", "Unknown Error");
 if (ex.getCause() != null) {
 map.put("cause", ex.getCause().getMessage());
 } else {
 map.put("cause", ex.getMessage());
 }
 return map;
 }
}

The @RequestMapping annotation is once again used to make sure that we only serve the clients with correctly specified headers. In the future we might have a version 3 that changes the response structure and that is when we add another method to our exception handler with an appropriate @RequestMapping. For now both our versions return the same error response structure.

The @ControllerAdvice Annotation

This annotation is similar to @Component and most often used to add exception handlers or init binders.

The @ExceptionHandler Annotation

This maps a list of exceptions to the handler method. In our example we return a Map of values and the @ResponseBody turns this into a JSON response.

Versioning the REST Service

A lot of times REST services will be versioned by adding a /v1/ or a /v2/ to the URI part of the resource. There is nothing wrong with this approach but there is another way. The IETF suggest a best practice of adding to the MIME-type instead. There are a couple of options here.

application/vnd.captech-v1.0+json

and

application/vnd.captech+json; version=1.0

The "vnd.captech" part is called Vendor MIME-Type. This vendor MIME-Type along with our versioning gives us the opportunity to match every version to a specific method.
As far as Spring is concerned either choice is allowed, they are both correct according to the MIME-type standard.
Create a new version of our facade is as simple as creating a new class that overrides one or all methods. As long as the consumes and produces annotation attributes differ from the previous version it will be routed correctly.

Conclusion

In the previous part we configured our simple application and now we have added a REST layer on top. We have a uniform URI pattern independent of the version and the versioning is controlled by MIME-Type header. Some people will argue that this service is not technically completely RESTful because we don't use HATEOAS and they would be right.

Each part of this blog will add functionality to our application.
Part 1: Creating the Basic Application
Part 2: Configuring The Application
Part 3: Adding the REST Services
Part 4: Adding Validation
Part 5: Adding Security to the Services
Part 6: Customising the Security for REST

Next we will look at validation to our application to make sure that our data model is consistent.

The code examples are all heavily edited excerpts but the complete source code can be found on GitHub.