【发布时间】:2017-02-10 00:04:35
【问题描述】:
我即将开始用 Java 开发一个新的 rest api。 我的问题是关于 PATCH 的使用 - 为什么?
假设我们有一个名为 Address.java 的实体
public class Address {
@Id
private Long id
@NotNull
private String line1;
private String line2; //optional
@NotNull
private String city;
@NotNull
private String state;
}
要创建一个新地址,我会执行以下 http 请求:
POST http://localhost:8080/addresses
提出以下要求:
{
"line1" : "mandatory Address line 1",
"line2" : "optional Address line 2",
"city" : "mandatory City",
"state" : "cd"
}
假设创建的记录的 id 为 1
对应的@RestController AddressResource.java会有这个方法:
@PostMapping(value = "/addresses")
public ResponseEntity<Address> create(@valid Address newAddress) {
addressRepo.save(newAddress);
}
@valid 将确保实体在将数据存储到表之前是有效的。
现在假设,我从上面的公寓搬到街对面的房子。如果我使用 PATCH,它就变成了
PATCH http://localhost:8080/addresses/1
带有请求负载:
{
"line1" : "1234 NewAddressDownTheStreet ST",
"line2" : null
}
对应的@RestController 方法是:
@PatchMapping(value = "/addresses/{id}")
public ResponseEntity<Address> patchAddress(@PathVariable Long id, Address partialAddress)
{
Address dbAddress = addressRepo.findOne(id);
if (partialAddress.getLine1() != null) {
dbAddress.setLine1(partialAddress.getLine1());
}
if (partialAddress.getLine2() != null) {
dbAddress.setLine2(partialAddress.getLine2());
}
if (partialAddress.getCity() != null) {
dbAddress.setCity(partialAddress.getCity());
}
if (partialAddress.getState() != null) {
dbAddress.setState(partialAddress.getState());
}
addressRepo.save(dbAddress)
}
现在如果你查询表,我的地址不会是吗?
"line1" : "1234 NewAddressDownTheStreet ST",
"line2" : "optional Address line 2", <-- INCORRECT. Should be null.
"city" : "mandatory City",
"state" : "cd"
可以看出,上述更新导致 line2 的值不正确。 这是因为在 java 中,当一个类被实例化时,Address 类中的所有实例变量都被初始化为 null(或默认初始值,如果它们是原始值)。所以没有办法区分 line2 被从默认值更改为 null。
问题 1) 是否有解决此问题的标准方法?
另一个缺点是,我不能使用@Valid 注释在入口点验证请求 - 因为它只是部分的。因此,无效数据可能会进入系统。
例如,假设有具有以下定义的附加字段:
@Min(0)
@Max(100)
private Integer lengthOfResidencyInYears,
而且用户不小心输入了 190(当他们真正的意思是 19 年时),它不会失败。
如果我使用 PUT,客户端将需要发送完整的地址对象,而不是 PATCH。 这样做的好处是我可以使用@Valid 来确保地址确实有效
如果假设在进行任何更新之前必须始终执行 GET,那么为什么不使用 PUT 而不是 PATCH? 我错过了什么吗?
一边
我的结论是,使用动态类型语言的开发人员是使用 PATCH 的支持者,因为我看不出从静态类型语言 Java 或 C# 中使用它有什么好处。它似乎增加了更多的复杂性。
【问题讨论】:
-
似乎状态有效,在 PATCH 请求后,您的数据库查询应显示行:“可选地址行 2”,因为您正在检查,partialAddress.getLine2() != null。
-
补丁请求应该包含客户端计算的指令,服务器可以使用该指令将某些资源的状态 A 转换为状态 B,而不仅仅是简化的部分更新。进一步阅读:SO documentation 和 good blog post
-
这是个好问题。关于 line2 中的 null 正是因为您手动映射字段并检查非 null 值以覆盖。目前我正在做一个我们使用Dozer 的项目,您可以选择映射空值。无论如何,我们使用 POST 添加/创建资源并使用 PUT 修改它们,但是使用此映射器它可以进行部分映射。所以我在争论这是否是首选方式,或者我应该使用 POST(创建)、PUT(完全更新)、PATCH(部分更新)。
标签: java spring rest api backend