Statically Typed Clients for Spring HATEOAS Resources

Recently I was experimenting with Spring HATEOAS.

My goal was to return a Resource from a Spring controller:

@RequestMapping(value = "/user/{id}", method = GET, produces = {"application/json"})
@PreAuthorize("isAuthenticated() and ( == #id or hasRole('ADMIN'))")
public @ResponseBody Resource<User> getUser(@PathVariable Long id) {
    User user = service.getUserById(id);
    return resourceAssembler.toResource(user);

… and be able to consume it from a Java client (not Javascript) using a RestTemplate:

String entityLink = ...
RestTemplate auth = ...
ResponseEntity<Resource> response = auth.getForEntity(entityLink, Resource.class);

(I’m using a Java client because my integration tests are in Java, and this also demonstrates how one might call the service from Android)

We’ve Got A Problem

However, on the first attempt I was getting deserialization errors. Upon observing the returned JSON, I found that the User attributes were being placed directly in the response body (along with the Resource’s links attribute) instead of in the Resource’s content attribute. The Resource class has a getContent() method, wasn’t the returned JSON supposed to have a content attribute?

Statically Typed Pain

The issue turned out to be with the out-of-the-box Resource class: upon further inspection it has @JsonUnwrapped on getContent(). This is convenient for dynamically typed clients like Javascript who might benefit from a shallower structure to navigate, and which do not have a problem with putting any attributes on any object. However, such a structure does not match any statically typed class defined on the server or the client. Resource does not have a “username” field, and User does not have a “links” field.

The Road Ahead?

One option could be for the client to retrieve it as string instead of as an object, and use the Jackson library direction to deserialize twice: each to two different classes (Resource and User) ignoring unknown fields. I don’t like this option so much because I feel like I shouldn’t have to write extra code (to do what RestTemplate should be doing for me anyway) every time I deserialize.

Another option (which is what I ended up doing) is to define my own Resource class as a copy of Spring HATEOAS’ version, but without @JsonUnwrapped on getContent(). This allows Jackson on the client to deserialize Resource into its own object, User into its own object inside the Resource, and all is well. It’s not a great option, as I’ll have to keep my copy of the Resource class up to date with Spring’s, but it’s the best I could do on short notice. 🙂 Additionally, my copy may end up morphing into a more customized DTO anyway.

What Do You Think?

What do you think of those two solutions? Are there better options?

Leave a comment

Filed under Software Engineering

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s