我的应用程序在资源/foo
。 通常情况下,它是由这样的HTTP响应净荷表示:
{"a": "some text", "b": "some text", "c": "some text", "d": "some text"}
客户端并不总是需要这个对象的所有四个成员。 什么是客户端的REST的语义的方式来告诉它在表示所需要的服务器? 例如,如果它想:
{"a": "some text", "b": "some text", "d": "some text"}
它应该如何GET
呢? 一些可能的(我在寻找修正,如果我误解了REST):
-
GET /foo?sections=a,b,d
。 - 查询字符串(称为毕竟查询字符串),似乎意味着“寻找资源匹配这个条件,并告诉我他们”,而不是“代表根据本定制这个资源给我。”
-
GET /foo/a+b+d
我最喜欢的,如果REST语义并不包括因为它的简单这个问题 。 - 打破URI不透明,违反HATEOAS。
- 似乎打破资源之间的区别(一个URI的唯一意义是标识一个资源)和表示。 但是,这是值得商榷的,因为它与一致
/widgets
代表一个像样的列表/widget/<id>
的资源,这是我从来没有遇到过问题。
- 松开我的约束,应对
GET /foo/a
等,并让客户端使每组件的请求/foo
就是了。 - 乘法开销,这可以成为一个噩梦,如果
/foo
有数百个组件和客户需要的100。 - 如果我想支持的HTML表示
/foo
,我使用Ajax,这是有问题的,如果我只想要一个HTML页面可以抓取,通过简约的浏览器等呈现 - 为了保持HATEOAS,它也需要链接到这些“子资源”等表述中存在,可能是在
/foo
: {"a": {"url": "/foo/a", "content": "some text"}, ...}
-
GET /foo
, Content-Type: application/json
和{"sections": ["a","b","d"]}
在请求体。 - Unbookmarkable和不可缓存。
- HTTP不用于定义身体语义
GET
。 这是合法的HTTP,但我怎么能保证一些用户的代理不从剥离体GET
请求? - 我的REST客户端不会让我把身体上的
GET
请求,所以我不能使用进行测试。
- 自定义HTTP标头:
Sections-Needed: a,b,d
- 我宁愿避免自定义页眉如果可能的话。
- Unbookmarkable和不可缓存。
-
POST /foo/requests
, Content-Type: application/json
和{"sections": ["a","b","d"]}
在请求体。 收到201
与Location: /foo/requests/1
。 然后GET /foo/requests/1
接收的所希望的表示/foo
- 笨重的; 需要背的往复和一些奇怪的前瞻性的代码。
- Unbookmarkable和不可缓存自
/foo/requests/1
仅仅是只使用一次,而且只有不停,直到它被请求的别名。
我建议的查询字符串溶液(您的第一个)。 你对其他替代参数都是很好的理由(和我已经在实践中试图解决同样的问题时,碰到的)。 特别是,“放宽这些限制/响应foo/a
”解决方案可以在有限的情况下工作,但介绍了很多复杂的成从实施和消费的API,并已经没有了,在我的经验,是值得努力。
我会弱对抗你的“意思似乎是”一个常见的例子论证:认为是一个对象(大名单的资源GET /Customers
)。 这是完全合理的页面,这些对象,这是司空见惯使用查询字符串来做到这一点: GET /Customers?offset=100&take=50
作为一个例子。 在这种情况下,查询字符串上未列出的对象的任何属性过滤,它为对象的副视提供的参数。
更具体地讲,我说,你可以通过这些标准使用的查询字符串的保持一致性,并HATEOAS:
- 物体返回应该是相同的实体作为从URL,而不查询字符串返回。
- 没有查询字符串的Uri应该返回完整的对象-可与相同的URI查询字符串的任何视图的超集。 所以,如果你缓存未修饰的Uri的结果,你知道你有充分的实体。
- 对于给定的查询字符串返回的结果必须是确定的,所以尤里斯与查询字符串很容易缓存
但是,有什么能换来这些URI有时可以带来更复杂的问题:
- URI的仅由查询字符串可能是不希望的不同的返回不同的实体类型(
/foo
是一个实体,但foo/a
是一个字符串); 另一种方法是,返回的局部填充的实体 - 如果你使用不同的实体类型为子查询的话,如果你的
/foo
不具有a
,一个404
状态误导( /foo
确实存在!),但空的响应可能同样令人困惑 - 返回的局部填充实体可能是不期望的,但实体的返回部分可能是不可能的,或者可以是更混乱
- 返回部分填充实体可能不是可能的,如果你有一个强大的架构(如果
a
是强制性的,但客户端的请求只有b
,你被迫返回无论是垃圾值a
或一个无效的对象)
在过去,我曾尝试通过定义所需的实体的具体命名为“意见”,并允许像查询字符串来解决这个?view=summary
或?view=totalsOnly
-限制排列数。 这也使得对于“有道理”的服务消费的实体的一个子集的定义,并且可以记录。
最后,我认为这归结为一致性比什么都重要的问题:你可以相对容易地使用查询字符串符合HATEOAS指导,但你的选择必须是在您的API一致的,我会说,有据可查。
我已经决定在以下方面:
支持几个成员组合 :我会拿出每一个组合的名称。 例如,如果一篇文章有作者,日期,和身体的成员, /article/some-slug
将返回所有的它和/article/some-slug/meta
将只返回作者和日期。
支持多种组合:我会用连字符分隔成员名称: /foo/abc
。
无论哪种方式,我将返回404
,如果组合是不支持的。
建筑约束
休息
确定资源
从定义 REST的:
资源是一个随时间变化的隶属函数其为时间映射到一组实体,或值的,这是等价的。 在设定的值可能是资源的陈述和/或资源标识符。
的表示是一个HTTP主体和标识符是URL。
这是至关重要的。 一个标识符是刚刚与其他标识符和表示相关联的值。 这是从识别符→表示映射不同。 该服务器可以映射它想要的任何标识的任何表示,只要两者都是由相同的资源有关。
它是由开发商拿出,通过像“用户”和“岗位”的事情类的思维合理描述业务资源定义。
HATEOAS
如果我真的在乎完美HATEOAS,我可以把一个超链接某处/foo
表示到/foo/members
,并表示将只包含一个超链接到成员的每一个支持的组合。
HTTP
从定义 URL的:
查询组件包含非分层数据,与路径部件数据一起,服务于URI的方案的范围和命名授权文件(如有)内标识的资源。
这样/foo?sections=a,b,d
和/foo?sections=b
都是唯一的标识符。 但是,它们可以在同一资源内同时被映射到不同的表现有关 。
HTTP的404
代码意味着服务器无法找到任何映射到,不就是URL不与任何资源相关的URL。
功能
没有浏览器或缓存永远不会有斜杠或连字符的麻烦。
其实这取决于资源的功能。 例如,如果资源代表的实体:
/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"
}
HTTP / 1.1
- 如果请求非法字段名 - 404(未找到)回到
- 如果要求不同的字段名称 - 不同的反应将产生,这也与缓存对齐。
- 缺点:如果相同的字段被请求的,但顺序的字段(比如:之间不同
fields=id,name
或fields=name,id
),虽然响应是一样的,这些反应将分别缓存。
HATEOAS
- 在我看来纯粹HATEOAS不适合解决这方面的问题。 因为为了做到这一点,你需要现场组合的每种排列,这是矫枉过正,因为它是广泛腹胀API(说你有一个资源的8个字段单独的资源,你需要 排列!)。
- 如果你只为字段但不是所有的排列模拟资源,它会对性能产生影响,例如,你想带往返最低数量。
如果A,B,C都像管理员对角色属性正确的方法是使用资源的属性是你提出的第一种方式GET /foo?sections=a,b,d
在这种情况下,因为你将应用筛选到foo
集合。 否则,如果a,b和c是的singole资源foo
集合,将要走的路是做了一系列的GET
请求/foo/a /foo/b /foo/c
。 这种做法,就像你说的,有要求很高的有效载荷,但它遵循的方法问题的REST的正确方法。 我不会用你所提出的第二个建议,因为加炭在URL中具有特殊的含义。
另一项建议是放弃使用GET和POST,并创建了一个动作foo
集合,像这样: /foo/filter
或/foo/selection
或表示在该集合的任何动作动词。 通过这种方式,有一个POST请求的身体,你可以通过资源你会的JSON列表。
你可以使用一个第二供应商媒体类型中的请求首部应用/ vnd.com.mycompany.resource.rep2,则不能收藏此然而,查询参数不能缓存(/富?段= A,B,C ),你可以看看矩阵参数但是对于这个问题,他们应该是缓存URL矩阵参数与请求参数