REST - 将ID放入正文中还是不放入?

Kon*_*rus 85 rest

假设我想为人们提供RESTful资源,客户端可以在其中分配ID.

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

现在,客户应该如何保存(或"PUT")呢?

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

有哪些常见的模式和解决方法?仅在位置上的ID似乎是最教条正确的方式,但它也使实际实现更难.

Jør*_*ldt 54

使用不同的读/写模型没有任何问题:客户端可以编写一个资源表示,其中服务器可以返回其中添加/计算元素的另一个表示(或者甚至是完全不同的表示 - 任何规范中都没有任何内容,唯一的要求是PUT应该创建或替换资源).

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

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

GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
Run Code Online (Sandbox Code Playgroud)

  • 这是错误的。看我的回答。PUT 必须包含整个资源。如果您想排除 id 并仅更新部分记录,请使用 PATCH。 (3认同)
  • 如果您应用类型(静态或动态),您将无法轻松拥有没有 ID 的模型......因此,从 PUT 请求的 URL 中删除 ID 会容易得多。它不会是“宁静的”,但它会是正确的。 (2认同)
  • 保留没有“id”的额外 TO 以及带有 id 和实体的 TO 以及额外的转换器,并且对程序员来说开销太大。 (2认同)

Jay*_*ete 21

如果它是一个公共API,你回答时应该保守,但要宽大地接受.

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

支持1和2的方法是,如果请求正文中没有提供id,则从url获取id,如果它在请求正文中,则验证它是否与url中的id匹配.如果两者不匹配,则返回400 Bad Request响应.

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

  • 我有点不同意,但没有投反对票。我认为API应该是严格的。如果我想用 API 做某事,那么它应该是一种方式(并且根据文档),而不是可选的,例如 URI 与正文。如果我们让它变得宽松,那么 API 上的代码和维护就会更多,因为允许多个选项 - 如果正文为空,则检查 URI,反之亦然。此外,它不仅破坏了客户端调用和服务器在此 API 上接收的方式的一致性,而且破坏了整个互联网的一致性。如果我们都同意一种处理这些事情的方法并且每个人都坚持它,那就更好了。较少主观的建议/指南 (7认同)
  • 经过多年使用 API 的工作,我对此的看法发生了变化,我同意你的观点@James。按照您建议的方式进行操作可以使客户更清楚地了解要做什么。 (6认同)
  • 根据https://tools.ietf.org/html/rfc7231#section-4.3.4如果不一致,最好返回409(冲突) (3认同)
  • 这应该是公认的解决方案。API应该始终是用户友好的。它在主体中应该是可选的。我不应该从POST收到ID,然后必须在PUT中将其设为未定义。此外,所作出的400个响应点就正确了。 (3认同)

bth*_*hen 8

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

GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}
Run Code Online (Sandbox Code Playgroud)

然后,如果要更新该资源,可以执行(伪代码):

updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)
Run Code Online (Sandbox Code Playgroud)

这样做的一个优点是客户端不必了解服务器的用户ID的内部表示.只要客户端有办法发现它们,ID就可能发生变化,甚至URL本身也可能发生变化.例如,在获取人员集合时,您可以返回如下响应:

GET /people
{ "people": [
    "/person/1",
    "/person/2"
  ]
}
Run Code Online (Sandbox Code Playgroud)

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

使用此方法,您可以根据资源和位置更多地考虑对象,而在ID方面则更少.因此,唯一标识符的内部表示与客户端逻辑分离.这是REST背后的原始动力:通过使用HTTP的功能,创建比以前存在的RPC系统更松散耦合的客户端 - 服务器体系结构.有关HATEOAS的更多信息,请查看Wikipedia文章以及这篇简短的文章.


yur*_*s87 8

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

话虽如此,我认为 PUT 应该主要用于更新,并且也id应该始终在 URL 中传递。因此,使用 PUT 来更新资源标识符是一个坏主意。id当URL 中的内容与id正文中的内容不同时,这会让我们陷入不希望的情况。

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

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

这是我能回答这个问题的最接近的了,因为这个话题总体上是一个意见问题。


bor*_*jab 6

在插入中,您不需要在 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"}  
    
    Run Code Online (Sandbox Code Playgroud)
  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"}
    
    Run Code Online (Sandbox Code Playgroud)

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

您还将看到可以避免插入和更新后的获取。


小智 5

之前已经有人问过这个问题 - 讨论值得一看:

RESTful GET 响应是否应该返回资源的 ID?

这是很容易陷入关于什么是“RESTful”、什么不是“RESTful”的争论的问题之一。

对于它的价值,我尝试从一致的资源角度进行思考,而不是在方法之间改变它们的设计。然而,恕我直言,从可用性的角度来看,最重要的是整个 API 保持一致!


Com*_*g88 5

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

看:

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(OK)或 204(无内容)响应代码应该被发送以指示请求的成功完成。

如果请求通过缓存并且请求 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"
Run Code Online (Sandbox Code Playgroud)

RESTful 实践表明,您在 /{id} 上放置的内容无关紧要——记录的内容应更新为有效负载提供的内容——但 GET /{id} 仍应链接到相同的资源。

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

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