Spring HATEOAS嵌入式资源支持

Gli*_*ide 45 java spring spring-hateoas

我想使用REST API的HAL格式来包含嵌入式资源.我正在使用Spring HATEOAS作为我的API,Spring HATEOAS似乎支持嵌入式资源; 但是,没有关于如何使用它的文档或示例.

有人可以举例说明如何使用Spring HATEOAS来包含嵌入式资源吗?

lin*_*nqu 39

请务必阅读Spring 关于HATEOAS文档,它有助于获得基础知识.

在这个答案中,核心开发人员指出了文档未涵盖的概念Resource,Resources以及PagedResources必不可少的内容.

我花了一些时间来了解它是如何工作的,所以让我们通过一些例子让它变得清晰.

返回单一资源

资源

import org.springframework.hateoas.ResourceSupport;


public class ProductResource extends ResourceSupport{
    final String name;

    public ProductResource(String name) {
        this.name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器

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<Resource<ProductResource>> get(@PathVariable Long id) {
        ProductResource productResource = new ProductResource("Apfelstrudel");
        Resource<ProductResource> resource = new Resource<>(productResource, new Link("http://example.com/products/1"));
        return ResponseEntity.ok(resource);
    }
}
Run Code Online (Sandbox Code Playgroud)

响应

{
    "name": "Apfelstrudel",
    "_links": {
        "self": { "href": "http://example.com/products/1" }
    }
}
Run Code Online (Sandbox Code Playgroud)

返回多个资源

Spring HATEOAS附带嵌入式支持,用于Resources反映具有多种资源的响应.

    @RequestMapping("products/", method = RequestMethod.GET)
    ResponseEntity<Resources<Resource<ProductResource>>> getAll() {
        ProductResource p1 = new ProductResource("Apfelstrudel");
        ProductResource p2 = new ProductResource("Schnitzel");

        Resource<ProductResource> r1 = new Resource<>(p1, new Link("http://example.com/products/1"));
        Resource<ProductResource> r2 = new Resource<>(p2, new Link("http://example.com/products/2"));

        Link link = new Link("http://example.com/products/");
        Resources<Resource<ProductResource>> resources = new Resources<>(Arrays.asList(r1, r2), link);

        return ResponseEntity.ok(resources);
    }
Run Code Online (Sandbox Code Playgroud)

响应

{
    "_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" }
            }
        }]
    }
}
Run Code Online (Sandbox Code Playgroud)

如果要更改密钥productResources,则需要注释资源:

@Relation(collectionRelation = "items")
class ProductResource ...
Run Code Online (Sandbox Code Playgroud)

使用嵌入式资源返回资源

这是你需要开始皮条客弹簧的时候.在HALResource通过@克里斯-达摩在推出另一个答案完全适合.

public class OrderResource extends HalResource {
    final float totalPrice;

    public OrderResource(float totalPrice) {
        this.totalPrice = totalPrice;
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器

    @RequestMapping(name = "orders/{id}", method = RequestMethod.GET)
    ResponseEntity<OrderResource> getOrder(@PathVariable Long id) {
        ProductResource p1 = new ProductResource("Apfelstrudel");
        ProductResource p2 = new ProductResource("Schnitzel");

        Resource<ProductResource> r1 = new Resource<>(p1, new Link("http://example.com/products/1"));
        Resource<ProductResource> 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);
    }
Run Code Online (Sandbox Code Playgroud)

响应

{
    "_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" }
                    }
                }]
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*our 31

我找不到正式的方法来做到这一点......这就是我们所做的

public abstract class HALResource extends ResourceSupport {

    private final Map<String, ResourceSupport> embedded = new HashMap<String, ResourceSupport>();

    @JsonInclude(Include.NON_EMPTY)
    @JsonProperty("_embedded")
    public Map<String, ResourceSupport> getEmbeddedResources() {
        return embedded;
    }

    public void embedResource(String relationship, ResourceSupport resource) {

        embedded.put(relationship, resource);
    }  
}
Run Code Online (Sandbox Code Playgroud)

然后我们的资源扩展了HALResource

  • 请告诉我,多年后不再是这种情况,为最基本的标准HAL做定制的东西!为什么春天HATEOAS假装它可以管理HAL并使其默认,如果它甚至不能自动执行此操作? (3认同)
  • 这样做的一个问题是您必须为要返回的每个实体创建一个资源类. (2认同)

小智 26

这是我们发现的一个小例子.首先我们使用spring-hateoas-0.16

我们GET /profile应该使用嵌入式电子邮件列表返回用户配置文件.

我们有电子邮件资源.

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@Relation(value = "email", collectionRelation = "emails")
public class EmailResource {
    private final String email;
    private final String type;
}
Run Code Online (Sandbox Code Playgroud)

我们想要嵌入到个人资料响应中的两封电子邮件

Resource primary = new Resource(new Email("neo@matrix.net", "primary"));
Resource home = new Resource(new Email("t.anderson@matrix.net", "home"));
Run Code Online (Sandbox Code Playgroud)

为了表明嵌入了这些资源,我们需要一个EmbeddedWrappers实例:

import org.springframework.hateoas.core.EmbeddedWrappers
EmbeddedWrappers wrappers = new EmbeddedWrappers(true);
Run Code Online (Sandbox Code Playgroud)

借助于wrappers我们可以EmbeddedWrapper为每封电子邮件创建实例并将其放入列表中.

List<EmbeddedWrapper> embeddeds = Arrays.asList(wrappers.wrap(primary), wrappers.wrap(home))
Run Code Online (Sandbox Code Playgroud)

剩下要做的就是用这些嵌入式构建我们的配置文件资源.在下面的示例中,我使用lombok来缩短代码.

@Data
@Relation(value = "profile")
public class ProfileResource {
    private final String firstName;
    private final String lastName;
    @JsonUnwrapped
    private final Resources<EmbeddedWrapper> embeddeds;
}
Run Code Online (Sandbox Code Playgroud)

请记住@JsonUnwrapped嵌入式字段中的注释

我们准备从控制器返回所有这些

...
Resources<EmbeddedWrapper> embeddedEmails = new Resources(embeddeds, linkTo(EmailAddressController.class).withSelfRel());
return ResponseEntity.ok(new Resource(new ProfileResource("Thomas", "Anderson", embeddedEmails), linkTo(ProfileController.class).withSelfRel()));
}
Run Code Online (Sandbox Code Playgroud)

现在在我们的回应中

{
"firstName": "Thomas",
"lastName": "Anderson",
"_links": {
    "self": {
        "href": "http://localhost:8080/profile"
    }
},
"_embedded": {
    "emails": [
        {
            "email": "neo@matrix.net",
            "type": "primary"
        },
        {
            "email": "t.anderson@matrix.net",
            "type": "home"
        }
    ]
}
}
Run Code Online (Sandbox Code Playgroud)

使用中有趣的部分Resources<EmbeddedWrapper> embeddeds是你可以在其中放入不同的资源,它会自动按关系对它们进行分组.为此,我们使用注解@Relationorg.springframework.hateoas.core包.

还有一篇关于HAL中嵌入式资源的好文章


Pet*_*nto 5

通常,HATEOAS需要创建一个代表REST输出的POJO并扩展HATEOAS提供的ResourceSupport.可以在不创建额外POJ的情况下直接使用Resource,Resources和Link类,如下面的代码所示:

@RestController
class CustomerController {

    List<Customer> customers;

    public CustomerController() {
        customers = new LinkedList<>();
        customers.add(new Customer(1, "Peter", "Test"));
        customers.add(new Customer(2, "Peter", "Test2"));
    }

    @RequestMapping(value = "/customers", method = RequestMethod.GET, produces = "application/hal+json")
    public Resources<Resource> getCustomers() {

        List<Link> links = new LinkedList<>();
        links.add(linkTo(methodOn(CustomerController.class).getCustomers()).withSelfRel());
        List<Resource> resources = customerToResource(customers.toArray(new Customer[0]));

        return new Resources<>(resources, links);

    }

    @RequestMapping(value = "/customer/{id}", method = RequestMethod.GET, produces = "application/hal+json")
    public Resources<Resource> getCustomer(@PathVariable int id) {

        Link link = linkTo(methodOn(CustomerController.class).getCustomer(id)).withSelfRel();

        Optional<Customer> customer = customers.stream().filter(customer1 -> customer1.getId() == id).findFirst();

        List<Resource> resources = customerToResource(customer.get());

        return new Resources<Resource>(resources, link);

    }

    private List<Resource> customerToResource(Customer... customers) {

        List<Resource> resources = new ArrayList<>(customers.length);

        for (Customer customer : customers) {
            Link selfLink = linkTo(methodOn(CustomerController.class).getCustomer(customer.getId())).withSelfRel();
            resources.add(new Resource<Customer>(customer, selfLink));
        }

        return resources;
    }
}
Run Code Online (Sandbox Code Playgroud)