Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot rest service, how to get it to marshal the links as properties?

first question here so be gentle :)

I've got a JPA project which I want to expose as REST. I've done this so far:

My entity:

@Entity
public class SignUpSheet {
    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    @Column
    private String description;

    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date dateTime;

    @ManyToOne
    private User parent;

    @OneToMany
    private List<Volunteer> volunteers;

    //getter and setters
}

All well and good, I call added spring-boot-starter-data-rest to my pom and now I get a service. Here's the JSON I get back.

http://localhost:8080/api-0.1.0/signUpSheets/1

{
  "name": "Auction",
  "description": "My First Sign Up Sheet",
  "dateTime": "2015-04-22T03:47:12.000+0000",
  "volunteers": [
   {
    "role": "Bringing stuff",
    "comments": "I have comments!"
   }
   ], 
  "endpoint": "/signUpSheets",
  "_links": {
    "self": {
      "href": "http://localhost:8080/api-0.1.0/signUpSheets/1"
    },
    "parent": {
      "href": "http://localhost:8080/api-0.1.0/signUpSheets/1/parent"
    },
    "user": {
      "href": "http://localhost:8080/api-0.1.0/signUpSheets/1/user"
    } 
  }
}

Super! Pretty much what I expected. Now I call my service using Spring's RestTemplate and here's where I'm stuck. When it marshals back into the SignUpSheet object it pulls in most of the object, but the ID field is null (which makes sense, because there is no ID field in the Json, just a self reference) and all the OneToMany and ManyToOne object are null (I assume for the same reason).

My question is: How do I tell either Spring Hateoas to add the ID to the json or tell Jackson how to marshal it the ID into the ID field? Furthermore how do I get the links? Should I not be marshaling back into the JPA entity and instead create another POJO for SignUpSheet (something I'd like to avoid for duplication purposes but could be talked into if it's necessary/desirable for some reason I'm missing). I have the Jackson2HalModule added to my ObjectMapper but that seems to make no difference whether it's there or not.

@Bean
@Primary
public ObjectMapper objectMapper() {
    ObjectMapper o = new ObjectMapper();
    o.registerModule(new Jackson2HalModule());
    return o;
}

Thanks in advance for the help!

=======================================================

Solution:

First step, read the manual :)

So I found out I need to extend ResourceSupport on my newly created DTOs. Done and done. But I was getting no links back! It seems like I needed to add the Jackson2HalModule to the object mapper on the RestTemplate like this:

    ObjectMapper o = new ObjectMapper();
    o.registerModule(new Jackson2HalModule());
    MappingJackson2HttpMessageConverter c = new MappingJackson2HttpMessageConverter();
    c.setObjectMapper(o);
    restTemplate.getMessageConverters().add(0, c);

So I figure I'll extend off of RestTemplate and @Component it and I should be good for any HATEOAS resource.

like image 306
Doug Busley Avatar asked Apr 26 '15 02:04

Doug Busley


1 Answers

I don't think you should be trying to deserialise the JSON back into your JPA entity. The JPA entity is very closely tied to your application's database and should be considered an implementation detail of the server. Instead, I'd recommend mapping to a type that's specifically modelled on the REST API, not on the structure of your database and your usage of JPA.

You're using Spring Data REST which strongly embraces hypermedia. That means that clients should use URIs to identify resources and links to navigate between them. For example, on the client side, a sign up sheet already has an ID; it's the href of the self link in the response. Therefore, there's no need to expose the ID from your JPA entity. Indeed, doing so would expose an implementation detail of your application that clients need not know about.

Rather than trying to populate all of the properties in the response, Spring Data REST provides links instead. For example, to access a sign up sheet's parent, you should extract the href of the parent link from the response and perform a GET request on the URI.

like image 188
Andy Wilkinson Avatar answered Oct 23 '22 02:10

Andy Wilkinson