Spring 数据 REST - PUT 请求自 v.2.5.7 以来无法正常工作

Spring Data REST - PUT request does not work properly since v.2.5.7

自版本 2.5.7 Spring 数据 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),那么我们会得到一个错误:

Can not construct instance of Address: no String-argument constructor/factory method to deserialize from String value

其他类型的关联也会发生同样的情况,例如一对多(单向和双向)——请参阅我的 example application 代码和测试。

此问题存在于 所有 版本的 Spring Boot 自 1.4.4 以来,包括最新的稳定版 1.5.6 以及最新的 2.0。 0-快照版本!

要解决这种情况,我们可以切换到 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 since 1.4.4 (SDR - from v.2.5.7).

我调试了我的项目,发现问题的原因隐藏在方法 DomainObjectReader#mergeForPut 中(参见 its source)- 它 从不 替换与新资源相关联。

在我 post 这个问题之前 Spring JIRA,如果你的项目中有这个问题,请在这里报告,你怎么看

你可以得到我的测试 here 并在你的项目中检查它 - 测试是 'standalone' 并且不依赖于其他 classes/modules(我希望只排除 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"
        }
    }
}

然后检查 'new' 人的地址 - 地址未更新:

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. , it turned out that SDR has a bug, which is described in two tickets: DATAREST-1001 and DATAREST-1012

看起来问题 already been reported as a bug: - 请验证。据我所知,这就是您在上面报告的问题。

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

我同意你的看法,这是 Spring Data REST 中的错误,应该报告。

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

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

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

郑重声明,我使用的是 Spring Data REST 2.6.3.