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 请求不同!
例如,Person
与 Addres
具有多对一关联。如果我们使用 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.
自版本 2.5.7 Spring 数据 REST 未正确执行 PUT 更新资源的请求 有相关资源。与按预期工作的 PATCH 请求不同!
例如,Person
与 Addres
具有多对一关联。如果我们使用 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.
看起来问题 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.