REST-put ID 是否在 body 中?

假设我想为人们提供一个 RESTful 资源,在这里客户机可以分配 ID。

一个人看起来像这样: {"id": <UUID>, "name": "Jimmy"}

现在,客户机应该如何保存(或“ PUT”)它?

  1. PUT /person/UUID {"id": <UUID>, "name": "Jimmy"}-现在我们有这个讨厌的复制,我们必须一直验证: 身体中的 ID 与路径中的 ID 是否匹配?
  2. 不对称代表:
    • PUT /person/UUID {"name": "Jimmy"}
    • 返回 {"id": <UUID>, "name": "Jimmy"}
  3. 在 body-ID 中没有 ID,只有位置:
    • PUT /person/UUID {"name": "Jimmy"}
    • 返回 {"name": "Jimmy"}
  4. 由于 ID 是由客户机生成的,因此使用 POST似乎不是一个好主意。

有哪些常见的模式和解决方法?只在位置中使用 ID 似乎是最教条的正确方法,但它也使得实际实现更加困难。

75602 次浏览

您可能需要查看 PATCH/PUT 请求类型。

PATCH 请求用于部分更新资源,而在 PUT 请求中,必须发送整个资源,在服务器上覆盖该资源。

至于在 URL 中有一个 ID,我认为您应该始终使用它,因为标识资源是一种标准做法。甚至 Stripe API 也是这样工作的。

您可以使用 PATCH 请求更新服务器上具有 ID 的资源以标识该资源,但不更新实际的 ID。

使用不同的读/写模型并没有什么错: 客户端可以编写一个资源表示,然后服务器可以返回另一个包含添加/计算元素的表示(或者甚至是一个完全不同的表示——在任何规范中都没有相反的规定,唯一的要求是 PUT 应该创建或替换资源)。

因此,我会选择(2)中的非对称解决方案,并避免在编写代码时在服务器端进行“讨厌的复制检查”:

PUT /person/UUID {"name": "Jimmy"}


GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

这个问题的一个解决方案涉及到“超文本作为应用程序状态的引擎”或“ HATEOAS”这个有些令人困惑的概念这意味着 REST 响应包含作为超链接执行的可用资源或操作。使用这种方法(它是 REST 原始概念的一部分) ,资源的唯一标识符/ID 本身就是超链接。例如,你可以这样写:

GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}

然后,如果你想更新这个资源,你可以这样做(伪代码) :

updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)

这样做的一个好处是,客户机不必了解用户 ID 的服务器内部表示形式。ID 可以更改,甚至 URL 本身也可以更改,只要客户机有办法发现它们。例如,当获取一个人员集合时,您可以返回如下响应:

GET /people
{ "people": [
"/person/1",
"/person/2"
]
}

(当然,您也可以根据应用程序的需要为每个人返回完整的 person 对象)。

使用这种方法,您可以更多地考虑对象的资源和位置,而较少地考虑 ID。因此,唯一标识符的内部表示与客户端逻辑是分离的。这是 REST 背后最初的动力: 通过使用 HTTP 的特性,创建比以前存在的 RPC 系统更松散耦合的客户机-服务器架构。要了解更多有关 HATEOAS 的信息,请看看 ABc0以及这个 ABc1。

这个问题以前也有人问过——这种讨论值得一看:

一个 RESTful GET 响应应该返回一个资源的 ID 吗?

这是一个很容易陷入围绕 什么是“ RESTful”,什么不是“ RESTful”辩论的问题。

不管怎样,我试图从一致性资源的角度思考问题,而不是在方法之间更改它们的设计。然而,恕我直言,从可用性的角度来看,最重要的事情是您在整个 API 中都是一致的!

我从 JSON-LD/Semantic Web 的角度来看这个问题,因为这是实现真正的 REST 一致性的好方法,正如我在 这些幻灯片中所概述的。从这个角度来看,选择(1)是没有问题的因为 Web 资源的 ID (IRI)应该始终等于我可以用来查找/取消引用资源的 URL。 我认为验证并不是真的很难实现,也不是计算意图; 所以我不认为这是选择(2)的有效理由. 我认为方案(3)实际上不是一个选项,因为 POST (create new)与 PUT (update/place)具有不同的语义。

使用不同的方法没有什么不好的,但是我认为最好的方法是 和第二名一起解决方案。

 PUT /person/UUID {"name": "Jimmy"}


GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

当实体被添加到 dbContext 中时,它主要是以这种方式使用 甚至实体框架也使用这种技术。没有生成 ID 的类是在实体框架中通过引用生成的 ID。

在插入中,不需要在 URL 中添加 id。这样,如果在 PUT 中发送 ID,可以将其解释为更改主键的 UPDATE。

  1. 插入:

    PUT /persons/
    {"id": 1, "name": "Jimmy"}
    HTTP/1.1 201 Created
    {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
    
    
    GET /persons/1
    
    
    HTTP/1.1 200 OK
    {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
    
  2. 更新

    PUT /persons/1
    {"id": "2", "name": "Jimmy Jr"} -
    HTTP/1.1 200 OK
    {"id": "2", "name": "Jimmy Jr", "other_field"="filled_by_server"}
    
    
    GET /persons/2
    
    
    HTTP/1.1 200 OK
    {"id": "2", "name": "Jimmy Jr", "other_field"="updated_by_server"}
    

JSON API使用这个标准并解决了一些问题,通过一个到新对象的链接返回插入或更新的对象。某些更新或插入可能包含一些将更改其他字段的业务逻辑

您还将看到可以避免在插入和更新之后执行 get。

如果它是一个公共 API,您应该保守地回复,但接受宽松。

我的意思是,你应该同时支持1和2。我同意3是没有意义的。

同时支持1和2的方法是,如果请求主体中没有提供 id,那么从 url 获取 id,如果它在请求主体中,那么验证它是否与 url 中的 id 匹配。如果两者不匹配,则返回一个400 Bad Request 响应。

当返回一个人力资源时要保守,并且总是在 json 中包含 id,即使在 put 中它是可选的。

虽然可以对不同的操作使用不同的表示形式,但是对于 PUT 是包含整个有效载荷的一般推荐。这意味着 id也应该在那里。否则,应该使用 PATCH。

话虽如此,我认为 PUT 应该主要用于更新,而且 id也应该总是在 URL 中传递。因此,使用 PUT 更新资源标识符是一个坏主意。 当 URL 中的 id可能与主体中的 id不同时,这使我们处于一种不希望出现的情况。

那么,我们如何解决这样的冲突呢? 我们基本上有两个选择:

  • 抛出4XX 异常
  • 添加 Warning(X-API-Warn等)头。

这是我能够回答这个问题的最接近的部分,因为这个话题通常是一个观点问题。

仅供参考,这里的答案是错误的。

如果您正在使用 PUT,那么您应该在主体中包含 id。如果使用 PATCH,则不需要正文中的 id。

参见:

Https://restfulapi.net/rest-api-design-tutorial-with-example/

Https://restfulapi.net/rest-put-vs-post/

Https://restfulapi.net/http-methods/#patch

主要使用 PUT API 更新现有资源(如果 资源不存在,则 API 可能决定创建一个新资源 如果 PUT API 已经创建了一个新资源,则源 服务器必须通过 HTTP 响应代码201通知用户代理 (创建)响应,如果修改了现有资源,则 200(确定)或204(无内容)应发送响应代码,以表明 成功完成申请。

如果请求通过缓存并且 Request-URI 标识 一个或多个当前缓存的实体,这些条目应该被处理 对此方法的响应不能缓存。

当要修改单数资源时,请使用 PUT,该单数资源已经是 资源集合的一部分。 PUT 替换其中的资源 如果请求更新了资源的一部分,则使用 PATCH。

补丁

HTTP PATCH 请求是对资源进行部分更新 请参阅 PUT 请求还修改了一个资源实体,以便更清楚地说明- PATCH 方法是部分更新现有 资源和 PUT 应该只有在替换 它的全部

所以你应该这样使用它:

POST    /device-management/devices      : Create a new device
PUT     /device-management/devices/{id} : Update the device information identified by "id"
PATCH   /device-management/devices/{id} : Partial-update the device information identified by "id"

RESTful 实践表明,在/{ id }输入什么并不重要——记录的内容应该更新为有效负载提供的内容——但 GET/{ id }仍然应该链接到相同的资源。

换句话说,PUT/3可能更新到有效负载 id 为4,但 GET/3仍应链接到相同的有效负载(并返回将 id 设置为4的负载)。

如果您决定您的 API 在 URI 和有效负载中需要相同的标识符,那么您的工作就是确保它匹配,但是如果您在有效负载中排除了应该完整存在的标识符,那么一定要使用 PATCH 而不是 PUT。这就是公认的答案错误的地方。PUT 必须替换整个资源,其中-as 补丁可能是部分的。