当前位置: 首页 > 知识库问答 >
问题:

Spring HATEOAS嵌入式资源支持

微生乐
2023-03-14

我想使用REST API的HAL格式来包含嵌入式资源。我在API中使用Spring HATEOAS,Spring HATEOAS似乎支持嵌入式资源;但是,没有关于如何使用此功能的文档或示例。

有人能提供一个如何使用Spring HATEOAS包含嵌入式资源的例子吗?

共有3个答案

谯翔
2023-03-14

下面是我们发现的一个小例子。首先,我们使用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;
}

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

Resource primary = new Resource(new Email("neo@matrix.net", "primary"));
Resource home = new Resource(new Email("t.anderson@matrix.net", "home"));

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

import org.springframework.hateoas.core.EmbeddedWrappers
EmbeddedWrappers wrappers = new EmbeddedWrappers(true);

wrappers的帮助下,我们可以为每封电子邮件创建EmbeddedWrapper实例,并将它们放入列表中。

List<EmbeddedWrapper> embeddeds = Arrays.asList(wrappers.wrap(primary), wrappers.wrap(home))

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

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

请记住嵌入字段上的注释@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()));
}

现在在我们的回应中

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

使用资源的有趣部分

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

林俊英
2023-03-14

前HATEOAS 1.0.0M1:我找不到一个正式的方法来做这个。。。这就是我们所做的

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);
    }  
}

然后让我们的资源扩展到我们的资源

更新:在HATEOAS 1.0.0M1中,只要嵌入式资源通过getContent公开(或者无论如何让jackson序列化内容属性),实体模型(以及任何扩展的表示模型)就会得到本地支持。

    public class Result extends RepresentationalModel<Result> {
        private final List<Object> content;

        public Result(

            List<Object> content
        ){

            this.content = content;
        }

        public List<Object> getContent() {
            return content;
        }
    };

    EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
    List<Object> elements = new ArrayList<>();

    elements.add(wrappers.wrap(new Product("Product1a"), LinkRelation.of("all")));
    elements.add(wrappers.wrap(new Product("Product2a"), LinkRelation.of("purchased")));
    elements.add(wrappers.wrap(new Product("Product1b"), LinkRelation.of("all")));

    return new Result(elements);

你会得到

{
 _embedded: {
   purchased: {
    name: "Product2a"
   },
  all: [
   {
    name: "Product1a"
   },
   {
    name: "Product1b"
   }
  ]
 }
}
鱼安然
2023-03-14

一定要阅读Spring关于HATEOAS的文档,这有助于了解基本知识。

在这个回答中,核心开发人员指出了资源资源页面资源的概念,这是文档中未涉及的基本概念。

我花了一些时间来理解它是如何工作的,所以让我们通过一些例子来明确它。

资源

import org.springframework.hateoas.ResourceSupport;


public class ProductResource extends ResourceSupport{
    final String name;

    public ProductResource(String name) {
        this.name = name;
    }
}

控制器

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);
    }
}

反应

{
    "name": "Apfelstrudel",
    "_links": {
        "self": { "href": "http://example.com/products/1" }
    }
}

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);
    }

反应

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

如果要更改键productResources,需要为资源添加注释:

@Relation(collectionRelation = "items")
class ProductResource ...

这时你需要开始拉皮条了。@chris damour在另一个答案中介绍的HALResource非常适合。

public class OrderResource extends HalResource {
    final float totalPrice;

    public OrderResource(float totalPrice) {
        this.totalPrice = totalPrice;
    }
}

控制器

    @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);
    }

反应

{
    "_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" }
                    }
                }]
            }
        }
    }
}
 类似资料:
  • 嵌入资源 嵌入资源即内容嵌入,可以为工程师提供诸如图片base64嵌入到css、js里,前端模板编译到js文件中,将js、css、html拆分成几个文件最后合并到一起的能力。有了这项能力,可以有效的减少http请求数,提升工程的可维护性。 fis不建议用户使用内容嵌入能力作为组件化拆分的手段,因为声明依赖能力会更适合组件化开发。 在html中嵌入资源 在html中可以嵌入其他文件内容或者base6

  • 6.10 嵌入式资源 6.10.1 简介 在Web应用程序中,ABP提供了一个简单的方法来使用嵌入式的 Razor视图(.cshtml文件)和 其它资源(css,js,img等文件)。你可以使用该功能来创建包含UI功能的插件/模块。 6.10.2 创建嵌入式文件 首先,我们应该创建一个文件并且标记它为 嵌入式资源。任何程序集都可以包含嵌入式资源文件。至于如何标记它,这要看你的项目是什么格式的。 项

  • 下面是一个我正在与SDR斗争的用例- 这是用户表和参考问题表 这是用户表和用户好友表 当我转到/users/{id}/userFriends时,默认情况下应该显示UserProjection中的firstname、lastname等 因此,我在UserRepository中启用了摘录投影,效果很好。我预计这里会有大约100个结果,所以如果这个结果没有分页的话就可以了。 但是,既然RefSecQue

  • 案例 Basic css 我们绑定 styled-jsx 来生成独立作用域的 CSS. 目标是支持 "shadow CSS",但是 不支持独立模块作用域的 JS. export default () => <div> Hello world <p>scoped!</p> <style jsx>{` p { color: blue;

  • 我正在使用HTTP采样器下载嵌入式资源。下面是来自Python.org的示例。我希望嵌入的资源的名称与实际的请求相匹配。但它们的名称与父示例相同。可能是HTTPS的产物吗?我使用的是JMeter5.3。

  • 资源嵌入 使用 go.rice server.go package main import ( "net/http" rice "github.com/GeertJohan/go.rice" "github.com/labstack/echo" ) func main() { e := echo.New() // the file server for ri