Spring HATEOAS embedded resource support

前端 未结 8 900
别那么骄傲
别那么骄傲 2020-12-07 14:12

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

8条回答
  •  夕颜
    夕颜 (楼主)
    2020-12-07 14:25

    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.

    Returning a Single Resource

    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" }
        }
    }
    

    Returning Multiple Resources

    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 ...
    

    Returning a Resource with Embedded Resources

    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" }
                        }
                    }]
                }
            }
        }
    }
    

提交回复
热议问题