REST Service Discoverability with Spring, part 5

This is the fifth of a series of articles about setting up a secure RESTful Web Service using Spring 3.1 and Spring Security 3.1 with Java based configuration. The previous article introduced the concept of Discoverability for the RESTful service, HATEOAS and followed with some practical scenarios driven by tests. This article will focus on the actual implementation of discoverability and satisfying the HATEOAS constraint in the REST Service using Spring 3.1.

Decouple Discoverability through events

Discoverability as a separate aspect or concern of the web layer should be decoupled from the controller handling the HTTP request. In order to do so, the Controller will fire off events for all the actions that require additional manipulation of the HTTP response:

@RequestMapping( value = "admin/foo/{id}",method = RequestMethod.GET )
@ResponseBody
public Foo get( @PathVariable( "id" ) Long id, 
    HttpServletRequest request, HttpServletResponse response ){
   Foo resourceById = RestPreconditions.checkNotNull( this.service.getById( id ) );
   
   this.eventPublisher.publishEvent
    ( new SingleResourceRetrieved( this, request, response ) );
   return resourceById;
}
@RequestMapping( value = "admin/foo",method = RequestMethod.POST )
@ResponseStatus( HttpStatus.CREATED )
public void create( @RequestBody Foo resource, 
    HttpServletRequest request, HttpServletResponse response ){
   RestPreconditions.checkNotNullFromRequest( resource );
   Long idOfCreatedResource = this.service.create( resource );
   
   this.eventPublisher.publishEvent
   ( new ResourceCreated( this, request, response, idOfCreatedResource ) );
}

These events can then be handled by any number of decoupled listeners, each focusing on it’s own particular case and each moving towards satisfying the overall HATEOAS constraint.

Also, the listeners should be the last objects in the call stack and no direct access to them is necessary; as such they are not public.

Make the URI of a newly created resource discoverable

As discussed in the previous post, the operation of creating a new resource should return the URI of that resource in the Location HTTP header of the response. :

@Component
class ResourceCreatedDiscoverabilityListener 
    implements ApplicationListener< ResourceCreated >{
   
   @Override
   public void onApplicationEvent( ResourceCreated resourceCreatedEvent ){
      Preconditions.checkNotNull( resourceCreatedEvent );
      
      HttpServletRequest request = resourceCreatedEvent.getRequest();
      HttpServletResponse response = resourceCreatedEvent.getResponse();
      long idOfNewResource = resourceCreatedEvent.getIdOfNewResource();
      
      this.addLinkHeaderOnResourceCreation( request, response, idOfNewResource );
   }
   void addLinkHeaderOnResourceCreation
    ( HttpServletRequest request, HttpServletResponse response, long idOfNewResource ){
      String requestUrl = request.getRequestURL().toString();
      URI uri = new UriTemplate( "{requestUrl}/{idOfNewResource}" )
       .expand( requestUrl, idOfNewResource );
      response.setHeader( HttpHeaders.LOCATION, uri.toASCIIString() );
   }
}

Unfortunately, dealing with the low level request and response objects is inevitable even in Spring 3.1, because first class support for specifying the Location is still in the works.

Get of single resource

Retrieving a single resource should allow the client to discover the URI to get all resources of that particular type:

@Component
class SingleResourceRetrievedDiscoverabilityListener 
 implements ApplicationListener< SingleResourceRetrieved >{
   
   @Override
   public void onApplicationEvent( SingleResourceRetrieved resourceRetrievedEvent ){
      Preconditions.checkNotNull( resourceRetrievedEvent );
      
      HttpServletRequest request = resourceRetrievedEvent.getRequest();
      HttpServletResponse response = resourceRetrievedEvent.getResponse();
      this.addLinkHeaderOnSingleResourceRetrieval( request, response );
   }
   void addLinkHeaderOnSingleResourceRetrieval
    ( HttpServletRequest request, HttpServletResponse response ){
      StringBuffer requestURL = request.getRequestURL();
      int positionOfLastSlash = requestURL.lastIndexOf( "/" );
      String uriForResourceCreation = requestURL.substring( 0, positionOfLastSlash );

      String linkHeaderValue = RESTURLUtil
       .createLinkHeader( uriForResourceCreation, "collection" );
      response.addHeader( LINK_HEADER, linkHeaderValue );
   }
}

Note that the semantics of the link relation make use of the “collection” relation type, specified and used in several microformats, but not yet standardized.

The Link header is one of the most used HTTP header for the purposes of discoverability. Because of this, some simple utilities are needed to ease the creation of it’s values on the server and to avoid introducing a third party library.

Discoverability at the root

The root is the entry point in the RESTful web service – it is what the client comes into contact with when consuming the API for the first time. If the HATEOAS constraint is to be considered and implemented throughout, then this is the place to start. The fact that most of the main URIs of the system have to be discoverable from the root shouldn’t come as much of a surprise by this point.

This is a sample controller method to provide discoverability at the root:

@RequestMapping( value = "admin",method = RequestMethod.GET )
@ResponseStatus( value = HttpStatus.NO_CONTENT )
public void adminRoot( HttpServletRequest request, final response ){
   String rootUri = request.getRequestURL().toString();
   
   URI fooUri = new UriTemplate( "{rootUri}/{resource}" ).expand( rootUri, "foo" );
   String linkToFoo = RESTURIUtil.createLinkHeader
    ( fooUri.toASCIIString(), REL_COLLECTION );
   response.addHeader( HttpConstants.LINK_HEADER, linkToFoo );
}

This is of course an illustration of the concept, to be read in the context of the proof of concept RESTful service of the series. In a more complex system there would be many more links, each with it’s own semantics defined by the type of link relation.

Discoverability is not about changing URIs

One of the more common pitfalls related to discoverability is the misunderstanding that, since the URIs are now discoverable, then they can be subject to change. This is however simply not the case, and for good reason: first, this is not how the web works – clients will bookmark the URIs and will expect them to work in the future. Second, the client shouldn’t have to navigate through the API to get to a certain state that could have been reached directly.

Instead, all URIs of the RESTful web service should be considered cool URIs, and cool URIs don’t change. Instead, versioning of the API can be used to solve the problem of a URI reorganization.

Caveats of Discoverability

As some of the discussions around the previous articles state, the first goal of discoverability is to make minimal or no use of documentation and have the client learn and understand how to use the API via the responses it gets. In fact, this shouldn’t be regarded as such a far fetched ideal – it is how we consume every new web page – without any documentation. So, if the concept is more problematic in the context of REST, then it must be a matter of technical implementation, not of a question of whether or not it’s possible.

That being said, technically, we are still far from the a fully working solution – the specification and framework support are still evolving, and because of that, some compromises may have to be made; these are nevertheless compromises and should be regarded as such.

Conclusion

This article covered the implementation of some of the traits of discoverability in the context of a RESTful Service with Spring MVC and touched on the concept of discoverability at the root. In the next articles I will focus on custom link relations and the Atom Publishing Protocol. In the meantime, check out the github project.

Reference: REST Service Discoverability with Spring, part 5 from our JCG partner Eugen Paraschiv at the baeldung blog.

Related Articles :

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


− three = 2



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books