【问题标题】:Canonical _links with Spring HATEOAS带有 Spring HATEOAS 的规范 _links
【发布时间】:2014-08-25 13:20:31
【问题描述】:

我们正在构建一个类似于 spring.io 指南“Accessing JPA Data with REST”的 RESTful Web 服务。要重现下面的示例输出,只需将 ManyToOne-Relation 添加到 Person,如下所示:

// ...

@Entity
public class Person {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long id;

  private String firstName;
  private String lastName;

  @ManyToOne
  private Person father;

  // getters and setters
}

添加一些示例数据后的 GET 请求产生:

{
  "firstName" : "Paul",
  "lastName" : "Mustermann",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/1"
    },
    "father" : {
      "href" : "http://localhost:8080/people/1/father"
    }
  }
}

但是,鉴于 Paul 的父亲存储的 ID 为 2,我们想要的结果将是其关系的规范 url:

// ...
    "father" : {
      "href" : "http://localhost:8080/people/2"
    }
// ...

如果 father 对某些人来说,这当然会导致问题(好吧,这在这里没有多大意义......;)),但在这种情况下,我们不想渲染JSON 格式的链接。

我们已经尝试实现一个 ResourceProcessor 来实现这一点,但似乎在调用处理器时链接还没有填充。我们设法添加了指向所需规范 url 的附加链接,但未能修改后来添加的链接。

问题:是否有通用的方法来自定义所有资源的链接生成?

澄清我们对规范 URL 的需求:我们使用 SproutCore Javascript 框架来访问 RESTful Web 服务。它使用“类似 ORM”的数据源抽象,我们已经为其实现了 Spring 生成的 JSON 输出的通用处理程序。当查询所有人时,我们需要向与其他人有 q 关系的 n 个人发送 n*(1+q) 个请求(而不仅仅是 n 个),以将它们同步到客户端数据源。这是因为默认的“非规范”链接绝对不包含有关正在设置的父亲或父亲 ID 的信息。似乎这会导致大量不必要的请求,如果最初的响应首先包含更多信息,则可以轻松避免这些请求。

另一种解决方案是将父亲的 id 添加到关系中,例如:

"father" : {
  "href" : "http://localhost:8080/people/1/father",
  "id" : 2
}

【问题讨论】:

    标签: spring spring-data-jpa spring-data-rest spring-hateoas


    【解决方案1】:

    Spring Data Rest 团队在某处进行了讨论,解释了为什么以这种方式将属性呈现为链接。话虽如此,您仍然可以通过抑制 SDR 生成的链接并实施ResourceProcessor 来实现您喜欢的目标。所以你的 Person 类如下所示。注意注解@RestResource(exported = false) 禁止链接

    @Entity
    public class Person {
    
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private long id;
    
      private String firstName;
      private String lastName;
    
      @ManyToOne
      @RestResource(exported = false)
      private Person father;
    
      // getters and setters
    }
    

    你的 ResourceProcessor 类看起来像

    public class EmployeeResourceProcessor implements
            ResourceProcessor<Resource<Person>> {
    
        @Autowired
        private EntityLinks entityLinks;
    
        @Override
        public Resource<Person> process(Resource<Person> resource) {
            Person person = resource.getContent();
            if (person.getFather() != null) {
                resource.add(entityLinks.linkForSingleResour(Person.class, person.getFather().getId())
                    .withRel("father"));
            }
            return resource;
        }
    
    }
    

    只有当father 值与Person 一起被急切地获取时,上述解决方案才有效。否则,您需要拥有属性 fatherId 并使用它而不是 father 属性。不要忘记使用 Jackson @ignore... 隐藏 fatherId 以响应 JSON。

    注意:我自己没有对此进行测试,但猜测它会起作用

    【讨论】:

    • 如果不实现控制器,我不知道该怎么做……这很有趣。是否必须将 EmployeeResourceProcessor 注册为 bean?
    • 是的,ResourceProcessor 需要注册为一个bean。我已经开始工作了。不幸的是,@RestResource 注释会导致 StackOverflowError(无限递归) - 它似乎不适用于单个属性,但适用于存储库?
    • 是的,您需要注册 EmployeeResourceProcessor。还要确保最新版本的 SDR (2.1.1)
    • 这有效,除了@RestResource(exported = false) 部分。这不符合您(我)的期望。它不是从链接中隐藏字段,而是将非导出字段的所有字段直接包含到序列化对象中。我认为您需要抑制 ResourceProcessor 中的链接
    【解决方案2】:

    由于我遇到了同样的问题,我在 spring-data-rest 创建了一个 Jira 问题: https://jira.spring.io/browse/DATAREST-682

    如果有足够多的人投票支持它,也许我们可以说服一些开发人员实施它:-)。

    【讨论】:

    • 我对这个问题添加了一些解释:还有另一种方法可以强制 SDR 使用额外的投影来包含相关资源的规范链接:jira.spring.io/browse/…
    【解决方案3】:

    您正在推动显示规范链接,这很奇怪。一旦检索到 /father 处的资源,self 链接应该是规范的……但实际上没有充分的理由强制父亲关系规范……也许是一些缓存方案?

    对于您的具体问题...您依赖于自动生成的控制器,因此您放弃了对很多链接做出决定的权利。如果您拥有自己的 PersonController,那么您将更多地负责链接结构。如果您创建了自己的控制器,则可以使用 EntityLinks https://github.com/spring-projects/spring-hateoas#entitylinks 和父亲的 ID..IE

    @Controller
    @ExposesResourceFor(Person.class)
    @RequestMapping("/people")
    class PersonController {
    
      @RequestMapping
      ResponseEntity people(…) { … }
    
      @RequestMapping("/{id}")
      ResponseEntity person(@PathVariable("id") … ) {
        PersonResource p = ....
        if(p.father){
          p.addLink(entityLinks.linkToSingleResource(Person.class, orderId).withRel("father");
        }
      }
    }
    

    但是仅仅更改 URL 似乎需要做很多工作

    【讨论】:

    • 感谢您的回答!澄清我们对规范 URL 的需求:我们使用 SproutCore Javascript 框架来访问 RESTful Web 服务。它使用“类似 ORM”的数据源抽象,我们已经为其实现了 Spring 生成的 JSON 输出的通用处理程序。
    • 好的,我对 StackOverlow 还不是很熟悉 ;)。上面的评论是按回车键意外保存的,我错过了 5 分钟的时间范围来编辑它。我将更新问题以阐明我们对规范 URL(或类似解决方案)的需求,并且我们将考虑为每个实体编写控制器,尽管这对于此任务来说似乎很麻烦。
    猜你喜欢
    • 2016-10-15
    • 1970-01-01
    • 2017-08-19
    • 2020-05-20
    • 1970-01-01
    • 2022-09-30
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多