Marcel Overdijk
Marcel Overdijk

Reputation: 11467

Jackson and unmarshalling Spring HATEOAS references

I have a Spring Boot REST application which also include dependencies to spring-boot-starter-data-jpa and spring-hateoas.

My issue is want to convert a JSON payload being posted to my server.

The payload contains data to create a new Product with a relation to a existing VAT Rate. This relation is modeled using a JPA @ManyToOne like:

@NotNull
@ManyToOne(fetch = FetchType.EAGER)
private VatRate vatRate; 

When I post something like:

{ 
  "description": "bla",
  "price": 99.99,
  "vat_rate": {
    "id": 1
}

Jackson automatically converts I can persist a new Product with a relation to the given VAT Rate.

However as I'm using HATEOAS I think I should use something like:

{ 
  "description": "bla",
  "price": 99.99,
  "vat_rate": {
    "id": "http://somehost/vat-rates/1"
}

But this one fails as Jackson cannot convert it.

Is there a easy solution for this? As HATEOAS is not a solid requirement in my case, if this is not possible easily it will put a to high burden to use HATEOAS.

Next to that if in first example I provide a unknown vat_rate id Boot gives me back a 500. Reason is that Jackson references/creates a VAT Rate which does not exists and when trying to store the Product a FK violation happens resulting in the 500 due to a org.hibernate.exception.ConstraintViolationException. I think it would be better to return a 400 pointing out that vat_rate does not exists. But probably I have to check the existence of the VAT Rate manually before saving the Product.

Upvotes: 1

Views: 651

Answers (1)

Andy Wilkinson
Andy Wilkinson

Reputation: 116171

In this situation, I like to use a separate DTO that's passed into my controller methods, rather than trying to reuse the JPA entity. In this scenario I'd create a ProductInput class with three properties:

class ProductInput {
    private String description;
    private String price;
    private URI vatRate;
}

The vatRate property is a URI as it's referencing an existing VAT rate resource and, when you're using hypermedia, a URI is a resource's id.

In your controller you then need to use this URI to look up a VatRate instance. You can use UriTemplate to extract the id from the URI and then use the id to look it up. Something like this:

String idString = new UriTemplate(/vat-rates/{id})
        .match(productInput.getVateRate().toASCIIString()).get("id");
long id = Long.valueOf(idString);
VatRate vatRate = vatRateRepository.findById(id);
if (vatRate == null) {
    // Return an appropriate error response, probably a 400
    // …
} else {
    // Create and save the product
    // …
}

I've got a very basic app that uses Spring HATEOAS on Github that might provide some useful further reading.

Upvotes: 1

Related Questions