我正在创建REST API。其中,有一种名为company
的资源类型,它具有相当多的属性/字段。
处理company
资源时的两个常见用例是:
我提出了两种不同的API设计方法,需要选择其中一种(可能还有更好的方法,所以请随意评论):
1。使用子资源进行细粒度更新
由于公司的属性可以分为不同类别(例如街道,城市和州代表地址......电话,邮件和传真代表联系信息等等),一种方法可能是使用以下路线:
/company/id
:可用于使用GET
/company/id/address
:可用于使用PUT
/company/id/contact
:可用于使用PUT
等等。
但是:在GET
这样的子资源上使用/company/id/address
永远不会发生。同样,更新/company/id
本身也永远不会发生(参见上面的用例)。我不确定这种方法是否遵循REST的想法,因为我使用不同的URL加载和操作相同的数据。
2。使用HTTP PATCH进行细粒度更新
在这种方法中,没有额外的路线进行部分更新。相反,只有一个端点:
/company/id
:可以使用GET
来获取整个公司,同时使用{{1}更新资源的子集(地址,联系信息等) }。
从技术角度来看,我很确定这两种方法都能正常工作。但是,我不想以不应该使用的方式使用REST。你更喜欢哪种方法?
答案 0 :(得分:3)
您是否真的知道GET
响应中包含的每个字段?如果没有,那么为地址和联系人创建自己的资源不仅仅是好的。也许您稍后会找到一个可以重用这些资源的进一步用例。
此外,您还可以在资源中嵌入其他资源。 JSON HAL (hal-json)f.e。显式提供_embedded
属性,您可以在其中嵌入当前状态f.e.子资源。具有嵌入式资源的虚构公司资源的简化类似HAL的JSON表示可能如下所示:
{
"name":"Test Company",
"businessType":"PLC",
"foundingYear": 2010,
"founders": [
{
"name": "Tim Test",
"_links": {
"self": {
"href": "http://example.org/persons/1234"
}
}
}
],
...
"_embedded": {
"address": {
"street": "Main Street 1",
"city": "Big Town",
"zipCode": "12345",
"country": "Neverland"
"_links": {
"self": {
"href": "http://example.org/companies/someCompanyId/address/1"
},
"googleMaps": {
"href": "http://maps.google.com/?ll=39.774769,-74.86084"
}
}
},
"contacts": {
"CEO": {
"name": "Maria Sample",
...
"_links": {
"self": {
"href": "http://example.org/persons/1235"
}
}
},
...
}
}
}
因此,通过向包含的特定资源的URI发送PUT
请求,可以更新嵌入式资源。当GET
请求我被缓存时,您可能需要提供更细粒度的缓存设置(例如,带有条件GET请求a.k.a If-Modified-Since
或ETAG头字段)以在更新后检索实际状态。这些头文件应考虑整个资源(包括嵌入一次)以返回更新的状态。
PUT
与PATCH
关于"部分更新":虽然PUT
的语义相当清楚,但PATCH
经常与部分更新相混淆,只是将某些属性的新状态发送给服务。然而,This article描述了PATCH
应该做什么。
简而言之,对于PATCH
请求,客户端负责比较资源的当前状态并计算将当前资源转换为所需状态的必要步骤。在计算步骤之后,请求必须包含服务器必须理解的指令才能执行这些指令,从而产生更新的版本。 PATCH
请求还是原子的 - 要么所有指令都成功,要么都没有。这会为此请求添加一些事务要求。
答案 1 :(得分:2)
在这种特殊情况下,我会使用PATCH
而不是子资源方法。首先,这不是一个真正的子资源。这只是为了消除更新整个大实体(资源)的问题而引入的虚假抽象。而PATCH
是REST兼容,完善且通用的方法。
和(IMO 最终比率),想象一下你需要以某种方式扩展公司(通过添加杂志,场地,CTO等)。您是否要添加新端点以使客户端能够更新此新添加的资源部分?怎么结束?具有无人理解的多个端点。使用PATCH
,您的API已准备好用于公司的新元素。