Spring Data REST - PUT 请求自 v.2.5.7 版本以来存在问题。

17
自从版本2.5.7起,Spring Data REST在更新涉及到关联资源的资源时无法正常执行PUT请求。与正常工作的PATCH请求不同!
例如,PersonAddres有多对一的关联。如果我们使用SDR v.2.5.6(Spring Boot v.1.4.3)进行PUT请求,则一切正常。但是,如果我们切换到版本2.5.7(即Spring Boot v.1.4.4),则会出现错误:

无法构造Address的实例:没有String-argument构造函数/工厂方法可用于从String值反序列化

与其他类型的关联(例如单向和双向的一对多关联)也会发生相同的情况-请参见我的示例应用程序代码和测试。
这个问题存在于自Spring Boot 1.4.4以来的所有版本中,包括最新稳定的1.5.6版本,以及最新的2.0.0-SNAPSHOT版本!
为了解决这个问题,我们可以切换到SDR v.2.5.6(Spring Boot v.1.4.3)。

我已经准备好了一个Postman请求集合,以帮助您解决问题:SDR PUT Issue

更新 2017-08-14

我找到了如何避免错误Can not construct instance of Address: no String-argument constructor/factory method to deserialize from String value

由于我在这个项目中使用了Lombok, 只需要告诉Lombok抑制使用@ConstructorProperties注释 generated constructors。 因此,我在“lombok.config”文件中设置了lombok.anyConstructor.suppressConstructorProperties=true,错误就消失了。

不幸的是,发现了一个新问题 - PUT请求根本不会更新关联对象

下面的示例演示了这一点。当我们尝试通过将其地址从addresses/1(初始值)更改为addresses/2来更新Person时 - 它仍然是相同的:addresses/1!与前一个问题一样,这个问题存在于所有版本的Spring Boot自1.4.4(SDR - 自v.2.5.7)以来。

我调试了我的项目并发现问题的原因隐藏在方法DomainObjectReader#mergeForPut中(请参见其源代码)-它从不用新资源替换关联的资源。
在我将此问题发布到Spring JIRA之前,请在此报告您的项目中是否存在此问题以及您对此的看法
您可以在此处获取我的测试,并在您的项目中进行检查-测试是“独立的”,不依赖于其他类/模块(仅排除H2,希望如此)。
@Entity
public class Person {

    private String name;

    @ManyToOne
    private Address address;

    // other stuff
}

@Entity    
public class Address {

    private String street;

    // other stuff
}

尝试更新个人信息:
PUT http://localhost:8080/api/persons/1

{
    "name": "person1u",
    "address": "http://localhost:8080/api/addresses/2"
}

获得正确的响应:
{
    "name": "person1u",
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/persons/1"
        },
        "person": {
            "href": "http://localhost:8080/api/persons/1"
        },
        "address": {
            "href": "http://localhost:8080/api/persons/1/address"
        }
    }
}

检查 Person 的“新”地址 - 地址未更新:

GET http://localhost:8080/api/persons/1/address

{
    "street": "address1",
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/addresses/1"
        },
        "address": {
            "href": "http://localhost:8080/api/addresses/1"
        }
    }
}

更新于2017年8月24日

感谢Scott C.的answer,发现了SDR存在一个错误,具体描述在两个工单中:DATAREST-1001DATAREST-1012


链接 http://localhost:8080/api/persons/1/address (v.2.5.6) 的对象是什么? - Andrew Tobilko
@AndrewTobilko 地址1: { "街道": "地址1", "_links": { "自身": { "href": "http://localhost:8080/api/addresses/1" }, "地址": { "href": "http://localhost:8080/api/addresses/1" } } } - Cepr0
我不明白为什么第一个版本可以工作。相同的异常应该被抛出,因为无法从单个String构造Address实例。什么是BaseEntity - Andrew Tobilko
大家好。我们社区中的一些女性有时会说,每当她们看到关于软件工程师的性别假设时,她们会感到有点被排斥。我想知道,你能否尝试避免在你的帖子中添加男性导向的问候语和代词,以营造更加友好的环境?谢谢。 - halfer
1
@halfer 好的。谢谢。 - Cepr0
显示剩余8条评论
2个回答

8

看起来问题已经被报告为一个bug了:请确认。据我所知,这就是你上面报告的问题。

请注意,我正在修改之前的答案以包含这个bug报告。


谢谢您的回复!我知道PUT和PATCH之间的区别。您是完全正确的 - PUT必须用新的实体完全替换原有的实体,包括简单字段和对其他对象的引用。正如RFC2616所说:应将封闭实体视为驻留在源服务器上的实体的修改版本。正如您所看到的,Person对象的“name”字段已被新值“person1u”替换,但address引用没有被替换。因此,我认为这确实是SB从v. 1.4.4中出现的一个错误。 - Cepr0
我不确定这是否与此处的问题相同。是的,它可能有关联,但你链接的错误只涉及集合,而这个错误甚至影响非集合资源关联(例如@ManyToOne关系)。 - Adam Kučera
我链接那个工单的原因是因为它似乎是连接到同一代码区域的其他工单。如果您查看引用工单中的评论,您将看到这个工单 https://jira.spring.io/plugins/servlet/mobile#issue/DATAREST-1001,它与此问题更直接相关。话虽如此,我认为上述工单需要修复以解决此问题。 - Scott C.
谢谢!您更新的答案解决了这个问题——SDR在处理PUT时存在一个错误(很奇怪我自己没有找到这些票证)。 - Cepr0
为了以后的参考和如何找到错误报告,我去Jira搜索了Put,返回了大约70个问题。我阅读了几个票务的摘要。当我找到一个候选票时,我阅读了整个票以与您的问题进行比较。知道代码区域有所帮助,因此OP中所做的研究是有帮助的。毫无疑问,这需要时间来找到它。 - Scott C.

3

我同意你的观点,这是Spring Data REST中的一个bug,应该报告给开发者。

在我的项目中也遇到了相同的问题,在使用PATCH请求更新实体时运行正常,但PUT请求只更新给定实体的字段,而不更新其关联的资源。

为什么我认为这是一个bug?

  • 正如人们正确指出的那样,PUT应该用于将整个资源替换为修改后的版本,这表明如果您提供资源的所有字段(如POST请求中所示),则它应该工作。然而,在当前的Spring Data REST版本中,只有实体的简单字段被更新,关联的资源保持不变,这使得PUT请求只能“部分工作”,这绝对不是期望的行为(即使它满足RFC)。
  • 此外,Spring Data REST甚至允许您进行部分PUT请求,即仅通过PUT更新您的名称字段有效。但是,对于地址却无效。
  • 这意味着Spring Data REST并没有完全按照RFC规定的方式工作(这可能是另一个辩论的话题),但它也没有提供一致的使用方法 - 当更新一个字段可行而更新其他字段却没有任何错误迹象时。

值得一提的是,我使用的是Spring Data REST 2.6.3版本。


在这个演示中,Oliver Gierke根本不使用PUT来更新/替换资源。也许我们不知道什么?..)) - Cepr0
根据Scott C.在链接中的发言,实际上看起来在PATCH被引入后,PUT并不是非常有用。唯一合理的用例似乎是当您需要请求具有幂等性时。因此,在_常见的_更新情况下,应该优先使用PATCH。 但这并不改变PUT无法按预期工作的事实,因此它是一个错误。 - Adam Kučera
请您在将问题发布到Spring Data REST JIRA之后,能否提供链接? - Adam Kučera
当然,我会在这里发布它。 - Cepr0
自然地,PATCH更新双向关联,如预期所示。)) - Cepr0
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接