DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Frameworks
  4. REST Service Discoverability with Spring, part 5

REST Service Discoverability with Spring, part 5

Eugen Paraschiv user avatar by
Eugen Paraschiv
·
Dec. 10, 11 · Interview
Like (1)
Save
Tweet
Share
7.65K Views

Join the DZone community and get the full member experience.

Join For Free

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.

The REST with Spring series:

  • Part 1 – Bootstrapping a web application with Spring 3.1 and Java based Configuration
  • Part 2 – Building a RESTful Web Service with Spring 3.1 and Java based Configuration
  • Part 3 – Securing a RESTful Web Service with Spring Security 3.1
  • Part 4 – RESTful Web Service Discoverability

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.

If you read this far, you should follow me on twitter here.

From RESTful Web Service Discoverability from the REST with Spring series.

REST Web Protocols Discoverability Web Service Spring Framework

Published at DZone with permission of Eugen Paraschiv, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Authenticate With OpenID Connect and Apache APISIX
  • gRPC on the Client Side
  • Top 10 Best Practices for Web Application Testing
  • Important Data Structures and Algorithms for Data Engineers

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: