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

2

6

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.

Wong answered 26/4, 2015 at 2:58 Comment(0)
A
3

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.

Arlin answered 26/4, 2015 at 12:0 Comment(2)
I had a feeling my issue was stemming from a misunderstanding on how to use Hypermedia. So when creating the objects in which I want to marshal, how do I get the links? Do I create member variables using the Link class?Wong
Yes, that sounds like a reasonable approach. You could either re-use Spring HATEOAS's Link class or create your own.Arlin
B
0

You need extend RepositoryRestConfigurerAdapter to tell you need Id to be exported, like:

public class RepositoryConfig extends RepositoryRestConfigurerAdapter {
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(SignUpSheet.class);
 }
}
Bield answered 7/9, 2018 at 7:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.