REST API可以至少以两种方式拥有参数:
作为url路径的一部分(即/api/resource/parametervalue)
作为查询参数(例如/api/resource?=参数值)
这里的最佳实践是什么?什么时候使用1,什么时候使用2,有什么一般的指导方针吗?
真实世界的例子:Twitter使用查询参数来指定间隔。(http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321)
把这些参数放在URL路径中会被认为是更好的设计吗?
如果有记录在案的最佳实践,我还没有找到它们。然而,当我决定在url中放置参数时,这里有一些指导原则:
可选参数往往更容易放在查询字符串中。
如果您希望在参数值与现有资源不对应时返回404错误,那么我倾向于使用路径段参数。例如:/customer/232,其中232不是有效的客户id。
然而,如果你想返回一个空列表,那么当参数没有找到时,我建议使用查询字符串参数。例如/联系人吗?name =戴夫
如果一个参数影响URI空间的整个子树,则使用路径段。例如,语言参数/en/document/foo.txt和/document/foo.txt?语言= en
我希望唯一标识符位于路径段中,而不是查询参数中。
uri的官方规则可以在这个RFC规范中找到。这里还有另一个非常有用的RFC规范,它定义了参数化uri的规则。
There are no hard and fast rules, but the rule of thumb from a purely conceptual standpoint that I like to use can briefly be summed up like this: a URI path (by definition) represents a resource and query parameters are essentially modifiers on that resource. So far that likely doesn't help... With a REST API you have the major methods of acting upon a single resource using GET, PUT, and DELETE . Therefore whether something should be represented in the path or as a parameter can be reduced to whether those methods make sense for the representation in question. Would you reasonably PUT something at that path and would it be semantically sound to do so? You could of course PUT something just about anywhere and bend the back-end to handle it, but you should be PUTing what amounts to a representation of the actual resource and not some needlessly contextualized version of it. For collections the same can be done with POST. If you wanted to add to a particular collection what would be a URL that makes sense to POST to.
这仍然留下了一些灰色区域,因为一些路径可以指向多少子代的父资源,这在某种程度上是自由裁量的,取决于他们的使用。这所画出的一条界限是,任何类型的传递表示都应该使用查询参数来完成,因为它不会有底层资源。
为了响应原始问题中给出的真实示例(Twitter的API),参数表示一个可传递查询,它过滤资源的状态(而不是层次结构)。在那个特定的例子中,向由这些约束表示的集合中添加数据是完全不合理的,而且该查询将无法表示为在对象图中有任何意义的路径。
The adoption of this type of resource oriented perspective can easily map directly to the object graph of your domain model and drive the logic of your API to the point where everything works very cleanly and in a fairly self-documenting way once it snaps into clarity. The concept can also be made clearer by stepping away from systems that use traditional URL routing mapped on to a normally ill-fitting data model (i.e. an RDBMS). Apache Sling would certainly be a good place to start. The concept of object traversal dispatch in a system like Zope also provides a clearer analog.
回答晚了,但我将对已经分享的内容添加一些额外的见解,即请求有几种类型的“参数”,您应该考虑到这一点。
定位器——例如资源标识符,如id或操作/视图
过滤器——例如,提供搜索、排序或缩小结果集的参数。
状态-例如会话标识,api密钥,等等。
内容-例如要存储的数据。
现在,让我们看看这些参数可能发生的不同情况。
请求报头和cookie
URL查询字符串("GET"变量)
URL路径
正文查询字符串/多部分("POST" vars)
通常,您希望在头文件或cookie中设置State,这取决于它是哪种类型的状态信息。我想我们都同意这一点。如果需要,使用自定义http报头(X-My-Header)。
类似地,Content只有一个归属位置,即在请求体中,可以作为查询字符串,也可以作为http multipart和/或JSON内容。这与服务器向您发送内容时您从服务器接收到的内容一致。所以你不应该无礼,用不同的方式去做。
像“id=5”或“action=refresh”或“page=2”这样的定位器作为URL路径是有意义的,例如mysite.com/article/5/page=2,其中您部分知道每个部分的含义(例如article和5显然意味着为我获取id为5的article类型的数据),并且附加参数被指定为URI的一部分。它们的形式可以是page=2,或者page/2,如果您知道URI中的某个点之后,“文件夹”是成对的键-值。
过滤器总是在查询字符串中,因为尽管它们是查找正确数据的一部分,但它们只是返回Locators单独返回的数据的子集或修改。在mysite.com/article/?query=Obama(子集)中的搜索是一个过滤器,/article/5?顺序向后=(修改)。想想它是做什么的,而不仅仅是它的名字!
如果“view”决定输出格式,那么它是一个过滤器(mysite.com/article/5?view=pdf),因为它返回对找到的资源的修改,而不是指向我们想要的资源。如果它决定我们可以看到文章的哪个特定部分(mysite.com/article/5/view=summary),那么它就是一个定位器。
记住,缩小一组资源就是筛选。在资源中定位某个特定的东西就是定位…咄。子集筛选可以返回任意数量的结果(甚至是0)。定位总是会找到某事物的特定实例(如果它存在)。修改筛选将返回与定位器相同的数据,除了被修改(如果允许这样的修改)。
如果人们不知道该把东西放在哪里,希望这能给他们一些顿悟的时刻!