We're building a RESTful web service similiar to the spring.io guide "Accessing JPA Data with REST". To reproduce the sample outputs below, it suffices to add a ManyToOne-Relation to Person as follows:
// ...
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
@ManyToOne
private Person father;
// getters and setters
}
A GET request after adding some sample data yields:
{
"firstName" : "Paul",
"lastName" : "Mustermann",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/1"
},
"father" : {
"href" : "http://localhost:8080/people/1/father"
}
}
}
But, given Paul's father is stored with ID 2, our desired result would be the canonical url for its relation:
// ...
"father" : {
"href" : "http://localhost:8080/people/2"
}
// ...
This of course will cause problems if father is null on some persons (OK, this does not make much sense here... ;)), but in this case we would like to not render the link in JSON at all.
We already tried to implement a ResourceProcessor to achieve this, but it seems that by the time the processor is called the links are not populated yet. We managed to add additional links pointing to the desired canonical url, but failed to modify the somehow later added links.
Question: Is there a generic approach to customize the link generation for all resources?
To clarify our need for canonical URLs: We use the SproutCore Javascript framework to access the RESTful web service. It uses an "ORM-like" abstraction of data sources for which we've implemented a generic handler of the JSON output Spring produces. When querying for all persons, we would need to send n*(1+q) requests (instead of just n) for n persons with q relations to other persons to sync them to the client side data source. This is because the default "non-canonical" link contains absolutely no information about a father being set or the father's id. It just seems that this causes a huge amount of unnecessary requests which could be easily avoided if the initial response would contain a little more information in the first place.
Another solution would be to add the father's id to the relation, e.g.:
"father" : {
"href" : "http://localhost:8080/people/1/father",
"id" : 2
}
There is a discussion somewhere Spring Data Rest team explained why properties are rendered as links that way. Having said you could still achieve what you like by suppressing link generated by SDR and implementing ResourceProcessor
. So your Person class would look like below. Note the annotation @RestResource(exported = false)
to suppress link
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
@ManyToOne
@RestResource(exported = false)
private Person father;
// getters and setters
}
Your ResourceProcessor class would look like
public class EmployeeResourceProcessor implements
ResourceProcessor<Resource<Person>> {
@Autowired
private EntityLinks entityLinks;
@Override
public Resource<Person> process(Resource<Person> resource) {
Person person = resource.getContent();
if (person.getFather() != null) {
resource.add(entityLinks.linkForSingleResour(Person.class, person.getFather().getId())
.withRel("father"));
}
return resource;
}
}
The above solution works only if father
value is eagerly fetched along with Person
. Otherwise you need to have property fatherId
and use it instead of father
property. Don't forget to use Jackson @ignore...
to hide fatherId
in response JSON.
Note: I haven't tested this myself but guessing it would work
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With