我的应用程序的资源位于/foo
。通常,它由HTTP响应有效负载表示,如下所示:
{"a": "some text", "b": "some text", "c": "some text", "d": "some text"}
客户端并不总是需要此对象的所有四个成员。什么是 RESTfully semantic 方式,客户端告诉服务器它在表示中需要什么?例如如果它想要:
{"a": "some text", "b": "some text", "d": "some text"}
它应该如何GET
呢?一些可能性(如果我误解了REST,我正在寻找纠正):
GET /foo?sections=a,b,d
。
GET /foo/a+b+d
我最喜欢的如果REST语义不能解决这个问题,因为它很简单。
/widgets
资源的可呈现列表的/widget/<id>
一致,我从未遇到过这样的问题。GET /foo/a
等,并让客户端为每个/foo
组件发出请求。
/foo
有数百个组件且客户端需要100个组件,这可能会成为一场噩梦。/foo
的HTML表示,我必须使用Ajax,如果我只想要一个可以抓取,由极简主义浏览器呈现的HTML页面等,则会出现问题。/foo
:{"a": {"url": "/foo/a", "content": "some text"}, ...}
GET /foo
,Content-Type: application/json
和{"sections": ["a","b","d"]}
。
GET
的正文语义。它是合法的HTTP,但我怎样才能保证某些用户的代理不会从GET
请求中删除正文?GET
请求上放置正文,因此我无法将其用于测试。Sections-Needed: a,b,d
POST /foo/requests
,Content-Type: application/json
和{"sections": ["a","b","d"]}
。通过201
收到Location: /foo/requests/1
。然后GET /foo/requests/1
接收所需的/foo
代表
/foo/requests/1
只是一个只能使用一次的别名,只会在请求之前保留。答案 0 :(得分:11)
我建议使用querystring解决方案(你的第一个)。你反对其他选择的论据是很好的论据(也是我在尝试解决同样问题时遇到的问题)。特别是,“放松约束/响应foo/a
”解决方案可以在有限的情况下工作,但是从实现和消费中向API引入了很多复杂性,并且没有,根据我的经验,值得付出努力。
我会用一个常见的例子来弱对抗你的“似乎意味着”的论点:考虑一个大型对象列表的资源(GET /Customers
)。分页这些对象是完全合理的,使用查询字符串来做这件事很常见:GET /Customers?offset=100&take=50
作为示例。在这种情况下,查询字符串不会对列出的对象的任何属性进行过滤,而是为对象的子视图提供参数。
更具体地说,我会说你可以通过这些标准来保持一致性和HATEOAS以使用查询字符串:
然而,为这些Uris 返回的内容有时会带来更复杂的问题:
/foo
是一个实体,foo/a
是一个字符串);另一种方法是返回部分填充的实体/foo
没有a
,则404
状态会产生误导(/foo
确实存在!),但空回应可能同样令人困惑a
是必需的,但客户端仅请求b
,则必须返回{{{}的垃圾值1}},或无效的对象)过去,我尝试通过定义所需实体的特定命名“视图”并允许像a
或?view=summary
这样的查询字符串来解决此问题 - 限制排列的数量。这也允许定义对服务的消费者“有意义”的实体子集,并且可以记录。
最终,我认为这可归结为一致性问题:您可以相对轻松地使用查询字符串来满足HATEOAS指南,但您所做的选择需要在您的API中保持一致,我会说,记录完备。
答案 1 :(得分:6)
我已决定以下内容:
支持少数成员组合:我会为每个组合提供一个名称。例如如果某篇文章包含作者,日期和正文的成员,/article/some-slug
将返回所有内容,/article/some-slug/meta
将返回作者和日期。
支持多种组合:我会用连字符分隔成员名称:/foo/a-b-c
。
无论哪种方式,如果组合不受支持,我都会返回404
。
资源 R 是时间上变化的隶属函数 M R ( t ),其中时间< i> t 映射到一组实体或值,它们是等价的。集合中的值可以是资源表示和/或资源标识符。
表示为HTTP正文,标识符为URL。
这很重要。标识符只是与其他标识符和表示相关联的值。这与标识符→表示映射不同。只要两者都由相同的资源关联,服务器就可以将它想要的任何标识符映射到任何表示。
开发人员可以通过考虑“用户”和“帖子”之类的事物来提出合理描述业务的资源定义。
如果我真的关心完美的HATEOAS,我可以将/foo
表示中的某个超链接放到/foo/members
,该表示只包含指向每个支持的成员组合的超链接。
来自网址的definition:
查询组件包含非分层数据,这些数据与路径组件中的数据一起用于标识URI方案和命名权限(如果有)范围内的资源。
因此/foo?sections=a,b,d
和/foo?sections=b
是不同的标识符。但是,在映射到不同的表示形式时,它们可以在同一资源中关联。
HTTP的404
代码means,服务器无法找到任何要映射URL的内容,而不是URL未与任何资源相关联。
任何浏览器或缓存都不会出现斜杠或连字符问题。
答案 2 :(得分:4)
实际上它取决于资源的功能。 例如,如果资源代表一个实体:
/customers/5
此处'5'代表客户的 id
响应:
{
"id": 5,
"name": "John",
"surename": "Doe",
"marital_status": "single",
"sex": "male",
...
}
因此,如果我们仔细检查它,每个json属性实际上代表客户资源实例上记录的字段。 让我们假设消费者希望获得部分响应,这意味着字段的一部分。我们可以看一下,因为消费者希望能够通过请求选择各个字段,这对他来说很有意思,但不是更多(为了节省流量或性能,如果部分字段难以计算)
我认为在这种情况下,最可读和最正确的API(例如,只获取名称和 surename )
/customers/5?fields=name,surename
响应:
{
"name": "John",
"surename": "Doe"
}
fields=id,name
或fields=name,id
),尽管响应相同,但这些响应将单独缓存。 答案 3 :(得分:2)
如果a,b,c是像admin这样的资源属性的属性,那么使用正确的方法是你建议GET /foo?sections=a,b,d
的第一种方式,因为在这种情况下你应该使用过滤器foo
集合。否则,如果a,b和c是foo
集合的单一资源,则遵循的方式是执行一系列GET
请求/foo/a /foo/b /foo/c
。正如您所说,这种方法对请求具有较高的有效负载,但这是遵循Restfull方法的正确方法。我不会使用你提出的第二个提案,因为网址中的加号具有特殊含义。
另一个建议是放弃使用GET和POST,并为foo
集合创建一个操作,如:/foo/filter
或/foo/selection
或代表集合操作的任何动词。通过这种方式,拥有一个post请求体,你可以传递你想要的资源的json列表。
答案 4 :(得分:1)
您可以在请求标头应用程序/ vnd.com.mycompany.resource.rep2中使用第二个供应商媒体类型,但是您不能将此书签,但查询参数不可缓存(/ foo?sections = a, b,c)您可以查看矩阵参数但是对于这个问题,它们应该可以缓存URL matrix parameters vs. request parameters