Rest 休息-是否将ID放入身体?
假设我想为人们提供一个RESTful资源,客户机可以在其中分配ID 一个人看起来像这样:Rest 休息-是否将ID放入身体?,rest,Rest,假设我想为人们提供一个RESTful资源,客户机可以在其中分配ID 一个人看起来像这样:{“id”:,“name”:“Jimmy”} 现在,客户端应该如何保存(或“放置”)它 PUT/person/UUID{“id”:,“name”:“Jimmy”}-现在我们有一个讨厌的重复,我们必须一直验证:身体中的id与路径中的id匹配吗 不对称代表: PUT/person/UUID{“name”:“Jimmy”} GET/person/UUID返回{“id”:,“name”:“Jimmy”} 正文中没
{“id”:,“name”:“Jimmy”}
现在,客户端应该如何保存(或“放置”)它
PUT/person/UUID{“id”:,“name”:“Jimmy”}
-现在我们有一个讨厌的重复,我们必须一直验证:身体中的id与路径中的id匹配吗PUT/person/UUID{“name”:“Jimmy”}
返回GET/person/UUID
{“id”:,“name”:“Jimmy”}
PUT/person/UUID{“name”:“Jimmy”}
返回GET/person/UUID
{“name”:“Jimmy”}
POST
看起来是个好主意,因为ID是由客户机生成的常见的模式和解决方法是什么?仅在位置上使用ID似乎是最教条正确的方法,但它也使实际实现更加困难。您可能需要研究修补程序/放置请求类型 修补程序请求用于部分更新资源,而在PUT请求中,您必须将整个资源发送到服务器上被覆盖的位置 就url中的ID而言,我认为您应该始终拥有它,因为识别资源是一种标准做法。甚至StripeAPI也是这样工作的
您可以使用修补程序请求更新服务器上ID为的资源,以识别该资源,但不更新实际ID。使用不同的读/写模型没有什么错:客户端可以编写一个资源表示,在服务器返回另一个表示后,可以在其中添加/计算元素(或者甚至是一种完全不同的表示——任何规范中都没有反对这一点的内容,唯一的要求是PUT应该创建或替换资源) 因此,我选择(2)中的非对称解决方案,并在编写以下内容时避免服务器端的“讨厌的重复检查”:
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
PUT/person/UUID{“name”:“Jimmy”}
GET/person/UUID返回{“id”:,“name”:“Jimmy”}
这个问题的一个解决方案涉及到“作为应用程序状态引擎的超文本”或“HATEOAS”这一令人困惑的概念这意味着REST响应包含可用的资源或要作为超链接执行的操作。使用此方法(这是REST最初概念的一部分),资源的唯一标识符/ID本身就是超链接。因此,例如,您可以有如下内容:
GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}
这样做的一个优点是,客户端不必知道服务器内部用户ID的表示形式。只要客户端有办法发现,ID可能会更改,甚至URL本身也可能会更改。例如,当获取一组人员时,您可以返回如下响应:
GET /people
{ "people": [
"/person/1",
"/person/2"
]
}
(当然,您也可以根据应用程序的需要为每个人返回完整的person对象)
使用这种方法,您更多地根据资源和位置来考虑对象,而更少地根据ID来考虑对象。因此,唯一标识符的内部表示与您的客户端逻辑分离。这就是REST背后的原始动力:创建比现有RPC系统更松散耦合的客户端-服务器体系结构之前,通过使用HTTP的功能。有关HATEOAS的更多信息,请查看以及此。这是以前提出的问题-讨论值得一看: 这是一个很容易陷入争论的问题
值得一提的是,我试着从一致的资源的角度来考虑,而不是在方法之间改变它们的设计。然而,从可用性的角度来看,最重要的是你在整个API中都是一致的!我从语义Web的角度来看这一点,因为这是实现rea的一个好方法l如我在中所述的REST一致性。从这个角度来看,毫无疑问可以选择选项(1),因为Web资源的ID(IRI)应该始终等于我可以用来查找/取消引用资源的URL。 我认为验证并不是很难实施,也不是计算上的问题,所以我不认为这是一个有效的理由去选择(2)。
我认为选项(3.)并不是一个真正的选项,因为POST(新建)与PUT(更新/替换)具有不同的语义。使用不同的方法没有什么不好的。但我认为最好的方法是使用第二个的解决方案
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
PUT/person/UUID{“name”:“Jimmy”}
GET/person/UUID返回{“id”:,“name”:“Jimmy”}
它主要以这种方式使用即使实体框架也使用这种技术当实体添加到dbContext中时,没有生成ID的类是通过实体框架中的引用生成的ID。在插入中,您不需要在URL中添加ID。这样,如果您在PUT中发送ID,您可能会被解释为更新更改主键
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"}
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"}
您还将看到,在插入和更新之后,您可以避免get。如果它是一个公共API,您在回复时应该保守,但要自由地接受 我的意思是,你应该同时支持1和2。我同意3没有意义 支持1和2的方法是,如果请求正文中没有提供id,则从url获取id;如果请求正文中没有提供id,则验证它是否与url中的id匹配。如果两者不匹配,则返回
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"}
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"