为什么 REST 动词不一定对应于 CRUD 操作?

Why do REST verbs not necessarily correspond to CRUD actions?

我参加了 Eric Evans (author of Domain Driven Design) 的一次演讲,他指出,假设四个 REST 操作自动映射到四个 CRUD 操作是对 REST 的混淆。即

POST   x=> Create
GET    x=> Read
PUT    x=> Update
DELETE x=> Delete

我有点想念他的推理。关于架构问题的一些不同之处。

我的问题是:为什么 REST 操作与 CRUD 操作不对应?

因为幂等性

POST 和 PUT 都可以用于“创建”和“更新”操作。 POST和PUT的区别是幂等性:PUT是幂等的,而POST不是。

Idempotence is the property of certain operations ..., that can be applied multiple times without changing the result beyond the initial application.

这意味着为了符合 HTTP 规范,您必须使用 PUT 进行幂等操作,POST 进行非幂等操作。这在 RFC7231 section 4.2.2.

中指定

请注意,幂等性由 HTTP 标准描述,与 REST 没有直接关系。

例子

通过示例可以轻松理解幂等性。让我们在 REST API 上创建一个应该创建“客户”资源的操作。幂等变体如下所示:

PUT /customers/cust42
{ "name": "John Doe" }

这将创建名为“John Doe”且 ID 为“cust42”的客户。这里重要的部分是客户端指定ID。如果客户端再次发出相同的 PUT 请求,则不会创建新资源 - 操作是幂等的。

另一方面,对于 POST,请求将如下所示:

POST /customers
{ "name": "John Doe" } 

服务器确定 ID 并创建名为“John Doe”的客户资源。此操作 不是 幂等的,因为再次发出相同的 POST 将创建一个 second 用户资源。

所以选择 POST 或 PUT 不是“创建”与“更新”的问题。上面的示例都创建了一个资源,但一个以幂等方式 (PUT) 进行,而另一个则不是 (POST).

与 DDD 的关系

在 DDD 中,目标是以对领域(及其专家)有意义的方式围绕领域问题创建一种语言。因此,以 CRUD 命名所有内容通常不是一个好主意。

不过,上述指南对于 DDD 仍然有用。如果您的 DDD 应用程序具有幂等的状态更改 API 操作,则使用 PUT,否则使用 POST。请参阅 Guillaume31 的回答,了解一些很好的面向领域的 DELETE 示例。

Why do REST actions not correspond to CRUD actions?

否则将意味着无法通过 REST 接口访问具有丰富域(即非 CRUD)的应用程序。

单独的 HTTP 动词不足以描述域操作。但结合 URI、内容类型并位于总体域应用协议中,您可以拥有从 HTTP 世界到有意义的域命令领域的映射。

因此,DELETE 在不同情况下可以转化为不同的事物:删除、擦除、存档、拒绝、取消、结束(流程/事务)等。

一个常见的误解是 REST 资源映射到域实体,HTTP 方法映射到这些域实体上的操作。在某些情况下这可能是正确的,但在那些情况下(没有业务逻辑的简单 CRUD)body 不会使用 DDD。

澄清一下,我们在这里讨论的是 HTTP 方法,而不是 "REST actions"。 REST 使用标准来构建客户端和服务之间的统一接口,而 HTTP 就是这样的标准之一。如果您花时间在此处阅读有关 HTTP 方法的信息:http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html,那么您将意识到,在 CRUD 和 PGPD 之间没有 1:1 映射。 theDmi 已经提到了幂等性,但是例如你也可以使用 POST 来发送搜索查询,这当然不是 "create".

正如 guillaume31 所写,REST 不是 RPC。您将可能的操作映射到 URI、HTTP 方法和其他请求元数据,而不仅仅是映射到 HTTP 方法。例如,您可以将 beginTransaction() 操作映射到 POST /transactions/ 超链接或将 wishHerHappyBirthday() 操作映射到 POST /greetings/ {subject: "her", occasion: "birthday"} 超链接。基于 HTTP 方法和 URI 进行路由更容易和更快,但这不是强制性的,如果需要,您也可以使用任何 HTTP header 和 body。