【发布时间】:2016-08-22 19:23:15
【问题描述】:
据我所知:
-
PUT- 用它的整个表示更新对象(替换) -
PATCH- 仅使用给定字段更新对象(更新)
我正在使用 Spring 来实现一个非常简单的 HTTP 服务器。当用户想要更新他的数据时,他需要向某个端点发送 HTTP PATCH(比如说:api/user)。他的请求正文通过@RequestBody 映射到一个 DTO,如下所示:
class PatchUserRequest {
@Email
@Length(min = 5, max = 50)
var email: String? = null
@Length(max = 100)
var name: String? = null
...
}
然后我使用这个类的一个对象来更新(补丁)用户对象:
fun patchWithRequest(userRequest: PatchUserRequest) {
if (!userRequest.email.isNullOrEmpty()) {
email = userRequest.email!!
}
if (!userRequest.name.isNullOrEmpty()) {
name = userRequest.name
}
...
}
我的疑问是:如果客户(例如网络应用程序)想要清除属性怎么办?我会忽略这样的变化。
我怎么知道,如果用户想要清除一个属性(他故意给我发送 null)或者他只是不想改变它?在这两种情况下,我的对象都将为 null。
我可以在这里看到两个选项:
- 同意客户,如果他想删除一个属性,他应该给我一个空字符串(但是日期和其他非字符串类型呢?)
- 停止使用 DTO 映射并使用一个简单的映射,它可以让我检查一个字段是空的还是根本没有给出。那么请求正文验证呢?我现在使用
@Valid。
应如何正确处理此类情况,与 REST 和所有良好实践相协调?
编辑:
有人可能会说PATCH 不应该用在这样的例子中,我应该使用PUT 来更新我的用户。但是模型更改(例如添加新属性)呢?每次用户更改后,我都必须对我的 API(或单独的用户端点)进行版本控制。例如。我将拥有api/v1/user 端点,它接受PUT 和旧请求正文,api/v2/user 端点接受PUT 和新请求正文。我想这不是解决方案,PATCH 的存在是有原因的。
【问题讨论】:
-
补丁是服务器为了将状态 A 转换为状态 B 必须执行的单个指令的集合。因此,客户端必须告诉服务器转换需要哪些指令。查看JSON Patch,了解 PATCH 请求正文的外观。正如您还询问了如果要删除的字段不可用该怎么办:PATCH RFC 明确指出:所有指令都成功或没有(原子性)
-
@RomanVottner JSON Patch 确实可能是有效的替代方案,但它不像普通的旧 http PATCH 那样容易在客户端实现,假设以下更改的自然描述即
{name: "Mario"}mreaning 更新name属性值为"Mario"。如果是 JSON Patch,请求验证将如何工作? -
@miensol 我不确定您具体要求什么。您的意思是客户端必须如何为名称更改创建 JSON-Patch 正文?或者服务器应该如何执行每条指令?对于入门者:客户端具有资源的状态 A,但他希望资源为状态 B。他必须遍历所有需要更改的字段并向 JSON-Patch 消息添加指令。服务器将必须创建一个事务并尝试通过更新指令执行更改。新字段可能需要预先更改 DB 表和向 DB 层更新指令
-
@RomanVottner 通过验证,我的意思是验证在服务器端的请求,理想情况下是注释驱动,如问题中提供的示例所示。我同意使用事务边界来提供整个操作的原子性是要走的路。但是问题没有提到使用数据库。
-
@miensol 可以使用更通用的类而不是使用定制的
PatchUserRequest,它包含一个 JSON 对象列表(具体说明),在遍历列表时,可能会签入如果值符合映射中定义的验证规则,则将字段映射到验证规则,否则失败会导致事务回滚。这也可以通过在数据层本身上指定约束来实现(尽管您尝试忽略 OP 实际问题的数据库)
标签: spring rest spring-boot patch kotlin