I want to use the HAL format for my REST API to include embedded resources. I\'m using Spring HATEOAS for my APIs and Spring HATEOAS seems to support embedded resources; how
Make sure to read Spring's documentation about HATEOAS, it helps to get the basics.
In this answer a core developer points out the concept of Resource, Resources and PagedResources, something essential which is is not covered by the documentation.
It took me some time to understand how it works, so let's step through some examples to make it crystal-clear.
the resource
import org.springframework.hateoas.ResourceSupport;
public class ProductResource extends ResourceSupport{
final String name;
public ProductResource(String name) {
this.name = name;
}
}
the controller
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping("products/{id}", method = RequestMethod.GET)
ResponseEntity> get(@PathVariable Long id) {
ProductResource productResource = new ProductResource("Apfelstrudel");
Resource resource = new Resource<>(productResource, new Link("http://example.com/products/1"));
return ResponseEntity.ok(resource);
}
}
the response
{
"name": "Apfelstrudel",
"_links": {
"self": { "href": "http://example.com/products/1" }
}
}
Spring HATEOAS comes with embedded support, which is used by Resources to reflect a response with multiple resources.
@RequestMapping("products/", method = RequestMethod.GET)
ResponseEntity>> getAll() {
ProductResource p1 = new ProductResource("Apfelstrudel");
ProductResource p2 = new ProductResource("Schnitzel");
Resource r1 = new Resource<>(p1, new Link("http://example.com/products/1"));
Resource r2 = new Resource<>(p2, new Link("http://example.com/products/2"));
Link link = new Link("http://example.com/products/");
Resources> resources = new Resources<>(Arrays.asList(r1, r2), link);
return ResponseEntity.ok(resources);
}
the response
{
"_links": {
"self": { "href": "http://example.com/products/" }
},
"_embedded": {
"productResources": [{
"name": "Apfelstrudel",
"_links": {
"self": { "href": "http://example.com/products/1" }
}, {
"name": "Schnitzel",
"_links": {
"self": { "href": "http://example.com/products/2" }
}
}]
}
}
If you want to change the key productResources you need to annotate your resource:
@Relation(collectionRelation = "items")
class ProductResource ...
This is when you need to start to pimp Spring. The HALResource introduced by @chris-damour in another answer suits perfectly.
public class OrderResource extends HalResource {
final float totalPrice;
public OrderResource(float totalPrice) {
this.totalPrice = totalPrice;
}
}
the controller
@RequestMapping(name = "orders/{id}", method = RequestMethod.GET)
ResponseEntity getOrder(@PathVariable Long id) {
ProductResource p1 = new ProductResource("Apfelstrudel");
ProductResource p2 = new ProductResource("Schnitzel");
Resource r1 = new Resource<>(p1, new Link("http://example.com/products/1"));
Resource r2 = new Resource<>(p2, new Link("http://example.com/products/2"));
Link link = new Link("http://example.com/order/1/products/");
OrderResource resource = new OrderResource(12.34f);
resource.add(new Link("http://example.com/orders/1"));
resource.embed("products", new Resources<>(Arrays.asList(r1, r2), link));
return ResponseEntity.ok(resource);
}
the response
{
"_links": {
"self": { "href": "http://example.com/products/1" }
},
"totalPrice": 12.34,
"_embedded": {
"products": {
"_links": {
"self": { "href": "http://example.com/orders/1/products/" }
},
"_embedded": {
"items": [{
"name": "Apfelstrudel",
"_links": {
"self": { "href": "http://example.com/products/1" }
}, {
"name": "Schnitzel",
"_links": {
"self": { "href": "http://example.com/products/2" }
}
}]
}
}
}
}