Spring Data REST - PUT 请求自 v.2.5.7 起无法正常工作

2022-09-03 07:35:39

由于版本2.5.7 Spring Data REST无法正确执行PUT请求以更新具有关联资源的资源。与按预期工作的 PATCH 请求不同!

例如,与 具有多对一关联。如果我们使用 SDR v.2.5.6(Spring Boot v.1.4.3)执行 PUT 请求,则一切正常。但是,如果我们切换到版本2.5.7(即Spring Boot v.1.4.4),那么我们会收到一个错误:PersonAddres

无法构造地址的实例:没有字符串参数构造函数/工厂方法从字符串值反序列化

其他类型的关联也是如此,例如一对多(单向和双向)关联 - 请参阅我的示例应用程序代码和测试。

自1.4.4以来,所有版本的Spring Boot都存在此问题,包括最新的稳定版1.5.6版本以及最新的2.0.0-SNAPSHOT版本!

要解决此问题,我们可以切换到SDR v.2.5.6(Spring Boot v.1.4.3)。

我准备了一个Postman请求集合来帮助您解决问题:SDR PUT问题

更新 2017-08-14

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

由于我在这个项目中使用龙目岛,所以有必要告诉龙目岛在生成的构造函数中使用注释来抑制。所以我设置了“lombok.config”文件,错误消失了。@ConstructorPropertieslombok.anyConstructor.suppressConstructorProperties=true

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

下面的示例演示了这一点。当我们尝试通过将他的地址从(初始值)更改为 - 来更新Person时,它保持不变:!除了上一个问题之外,自1.4.4(SDR - 来自v.2.5.7)以来的所有版本的Spring Boot中都存在这个问题。addresses/1addresses/2addresses/1

我调试了我的项目,发现问题的原因隐藏在方法中(请参阅其源代码) - 它永远不会用新资源替换关联的资源。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"
        }
    }
}

然后检查“新”人员地址 - 地址未更新:

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-08-24

多亏了Scott C. answer,事实证明SDR有一个错误,在两个票证中描述了这一DATAREST-1001DATAREST-1012


答案 1

看起来问题已被报告为错误: - 请验证。据我所知,这就是您上面报告的问题。

请注意,我正在修改我之前的答案,使其成为此错误报告。


答案 2

我同意你的观点,这是Spring Data REST中的一个错误,应该报告。

我在项目中遇到了同样的问题,通过PATCH请求更新实体工作正常,但PUT请求仅更新给定实体的字段,而不更新其关联资源。

为什么我认为这是一个错误?

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

为了记录,我使用的是Spring Data REST 2.6.3。