Spring MVC, REST, and HATEOAS
Asked Answered
S

4

24

I'm struggling with the correct way to implement Spring MVC 3.x RESTful services with HATEOAS. Consider the following constraints:

  • I don't want my domain entities polluted with web/rest constructs.
  • I don't want my controllers polluted with view constructs.
  • I want to support multiple views.

Currently I have a nicely put together MVC app without HATEOAS. Domain entities are pure POJOs without any view or web/rest concepts embedded. For example:

class User {
   public String getName() {...}
   public String setName(String name) {...}
   ...
}

My controllers are also simple. They provide routing and status, and delegate to Spring's view resolution framework. Note my application supports JSON, XML, and HTML, yet no domain entities or controllers have embedded view information:

@Controller
@RequestMapping("/users")
class UserController {

  @RequestMapping
  public ModelAndView getAllUsers() {
    List<User> users = userRepository.findAll();
    return new ModelAndView("users/index", "users", users);
  }

  @RequestMapping("/{id}")
  public ModelAndView getUser(@PathVariable Long id) {
    User user = userRepository.findById(id);
    return new ModelAndView("users/show", "user", user);
  }
}

So, now my issue - I'm not sure of a clean way to support HATEOAS. Here's an example. Let's say when the client asks for a User in JSON format, it comes out like this:

{
  firstName: "John",
  lastName: "Smith"
}

Let's also say that when I support HATEOAS, I want the JSON to contain a simple "self" link that the client can then use to refresh the object, delete it, or something else. It might also have a "friends" link indicating how to get the user's list of friends:

{
  firstName: "John",
  lastName: "Smith",
  links: [
    {
      rel: "self",
      ref: "http://myserver/users/1"
    },
    {
      rel: "friends",
      ref: "http://myserver/users/1/friends"
    }
  ]
}

Somehow I want to attach links to my object. I feel the right place to do this is in the controller layer as the controllers all know the correct URLs. Additionally, since I support multiple views, I feel like the right thing to do is somehow decorate my domain entities in the controller before they are converted to JSON/XML/whatever in Spring's view resolution framework. One way to do this might be to wrap the POJO in question with a generic Resource class that contains a list of links. Some view tweaking would be required to crunch it into the format I want, but its doable. Unfortunately nested resources could not be wrapped in this way. Other things that come to mind include adding links to the ModelAndView, and then customizing each of Spring's out-of-the-box view resolvers to stuff links into the generated JSON/XML/etc. What I don't want is to be constantly hand-crafting JSON/XML/etc. to accommodate various links as they come and go during the course of development.

Thoughts?

Soccer answered 23/8, 2011 at 20:54 Comment(2)
There is now a Spring Data project called Spring Data - Rest that supports HATEOAS in some form.Soccer
or you can use spring-hateoas standalone module directly.Ziwot
W
10

There is a useful project called Spring HATEOAS on GitHub which has the following description:

"This project provides some APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring and especially Spring MVC"

If the resource class you are returning extends 'ResourceSupport' you can easily add links to it, and you can build links using 'ControllerLinkBuilder', for example to add a self link:

import static org.sfw.hateoas.mvc.ControllerLinkBuilder.*;

Link link = linkTo(YourController.class).slash(resource.getName()).withSelfRel();
resource.add(link);

It is quite a new project but it is available from the public Maven repo if required:

<dependency>
    <groupId>org.springframework.hateoas</groupId>
    <artifactId>spring-hateoas</artifactId>
    <version>0.3.0.RELEASE</version>
</dependency>

If you use the maven artifact:

org.sfw.hateoas.mvc.ControllerLinkBuilder

becomes:

org.springframework.hateoas.mvc.ControllerLinkBuilder
Wachter answered 30/11, 2012 at 11:2 Comment(3)
Interesting. I prefer imports of Spring and other infrastructural code being at the periphery of my application but in this case all resource classes extending ResourceSupport seems like a decent tradeoff. Thanks!Soccer
Oh, and its from Oliver Gierke who is the main Spring Data guy. Cool.Soccer
I wrote a blog posts related to HATEOAS using Spring FrameworkTwinned
I
1

My Thoughts:

  • using some kind of naming convention so that for example the self reference url can be constructed out of the class name of the object.
  • I do not think that adding the links stuff should be added by the controller (btw you self wrote "I don't want my controllers polluted with view constructs". I would try to search for an way to extend the JSON serialization so that it automatically add the extra stuff. May you need to add some annotations to your entities, even if this would pollute them a bit.
Indecorum answered 24/8, 2011 at 5:54 Comment(3)
Thanks. The naming convention could work if all I wanted was a "self" link, however, in the real application there would be multiple workflow-specific links on objects (similar to the somewhat famous "how to get a cup of coffee" article) that could not be created by convention.Soccer
Regarding generating the links - I need to come up with something somewhat generic so when I add a new link I don't have to go edit my 3 different view resolvers (JSON/XML/HTML). Hmmm. Maybe the nature of a RESTful architecture with this technology stack requires the domain entities to be "polluted" with links, and maybe that's not bad...Soccer
The "naming convention" is a bad idea. Specifically, it seems totally anti-HATEOAS. The point of HATEOAS is that the client doesn't need to "know" your API, but that it can simply follow the links to get to another entity or state transition.Providing
A
1

Just stumbled across this while looking for something else and thought you should be considering using the Link header instead of content in the JSON body which is really just polluting the representations of your resources.

Check out the IETFs memo on web linking and also the IANA registry of link relations.

Aureaaureate answered 15/8, 2012 at 11:35 Comment(0)
D
0

TO create the link in your REST api you can use HAETOAS project of Spring framework.

org.springframework.hateoas.mvc.ControllerLinkBuilder class have set of method which you can use to build a link like -

Link link=linkTo(PersonControllerImpl.class).slash(null).withSelfRel();

Also if you have a controller method having @RequestMapping annotation with some URI value -

@RequestMapping(value = "/customer", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<?> getCustomer() {

//...
}

then you can create link using method URI value as -

linkTo(methodOn(PersonControllerImpl.class).getCustomer()).toUri().toString()

it will return String value (http://www.urhost.com/customer) which you can set in your entity Object.

Doughnut answered 19/9, 2016 at 11:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.