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

时间:2015-01-12 10:38:15

标签: 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. 由于ID是由客户生成的,所以POST似乎不是一个好主意。
  5. 有哪些常见的模式和解决方法?仅在位置上的ID似乎是最教条正确的方式,但它也使实际实施更难。

10 个答案:

答案 0 :(得分:46)

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

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

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

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

答案 1 :(得分:16)

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

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

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

当返回人员资源时,保守并且始终在json中包含id,即使它在put中是可选的。

答案 2 :(得分:8)

这个问题的一个解决方案涉及“超文本作为应用程序状态引擎”的概念有些令人困惑,&#34;或者&#34; HATEOAS。&#34;这意味着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"
  ]
}

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

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

答案 3 :(得分:3)

在插入中,您无需在URL中添加ID。这样,如果您在PUT中发送ID,则可以将其解释为更新主键以更新主键。

  1. INSERT:

    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"}
    
  3. JSON API使用此标准并解决了返回插入或更新的对象以及指向新对象的链接的一些问题。某些更新或插入可能包含一些将更改其他字段的业务逻辑

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

答案 4 :(得分:2)

以前曾经问过 - 讨论值得一看:

Should a RESTful GET response return a resource's ID?

这是其中一个容易陷入围绕what is and is not "RESTful"辩论的问题。

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

答案 5 :(得分:2)

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

话虽如此,我认为PUT应该主要用于更新,id也应该始终在URL中传递。因此,使用PUT更新资源标识符是一个坏主意。 当网址中的id与正文中的id不同时,它会让我们处于不良情境。

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

  • 抛出4XX异常
  • 添加WarningX-API-Warn等)标题。

我可以回答这个问题,因为这个话题一般都是意见问题。

答案 6 :(得分:1)

我从JSON-LD /语义Web的角度来看这个,因为这是实现真正的REST一致性的好方法,正如我在these slides中所概述的那样。从这个角度来看,毫无疑问可以选择(1.),因为Web资源的ID(IRI)应始终等于我可以用来查找/取消引用资源的URL。 我认为验证并不是很难实现,也不是计算上的强项;因此我认为这不是选项(2.)的有效理由。 我认为选项(3.)实际上不是一个选项,因为POST(create new)具有与PUT(更新/替换)不同的语义。

答案 7 :(得分:1)

使用不同的方法没有什么不好。但我认为最好的方法是解决方案 with 2nd

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

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

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

答案 8 :(得分:1)

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

请参阅:

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。

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"

REST的实践表明,在/ {id}处输入内容无关紧要-记录的内容应更新为有效负载提供的内容-但是GET / {id}仍应链接到相同的资源。

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

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

答案 9 :(得分:0)

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

PATCH请求用于部分更新资源,而在PUT请求中,您必须将整个资源发送到服务器上被覆盖的位置。

就网址中的ID而言,我认为您应该始终拥有它,因为识别资源是标准做法。甚至Stripe API都是这样工作的。

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