REST API至少可以通过两种方式具有参数:



作为URL路径的一部分(即/api/resource/parametervalue

作为查询参数(即/api/resource?parameter=value

此处的最佳做法是什么?何时使用1和何时使用2有任何一般性准则吗?

真实示例:Twitter使用查询参数指定间隔。 (http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321

将这些参数放在URL路径中是否被认为是更好的设计?

#1 楼

如果有记录的最佳实践,我还没有找到它们。但是,在确定将参数放置在url中的位置时,这里使用了一些准则:想要在参数值与现有资源不对应时返回404错误,那么我倾向于使用路径段参数。例如/customer/232其中232不是有效的客户ID。

但是如果您想返回一个空列表,那么当找不到该参数时,我建议使用查询字符串参数。例如/contacts?name=dave

如果参数影响URI空间的整个子树,请使用路径段。例如语言参数/en/document/foo.txt/document/foo.txt?language=en

我更喜欢唯一标识符位于路径段而不是查询参数中。 。这里还有另一个非常有用的RFC规范,它定义了参数化URI的规则。

评论


官方规则URI和sepc草案非常有用且有趣! :-)

– KajMagnus
2011年4月16日在10:54



404错误测试对我有很多帮助,可以避免将信息放入属于查询参数,标头或请求正文的路径中。谢谢指出!

–凯文·康登(Kevin Condon)
2014年12月17日15:59

#2 楼

较晚的答案,但是我将对已共享的内容添加一些其他见解,即请求中有几种类型的“参数”,您应该考虑到这一点。定位器-例如资源标识符,例如ID或操作/视图
过滤器-例如用于搜索,排序或缩小结果集的参数。
状态-例如会话标识,API密钥,其他内容。
内容-例如要存储的数据。

现在让我们看一下这些参数可以在哪里放置。 “ GET” vars)
URL路径
正文查询字符串/多部分(“ POST” vars)

通常,您希望在标题或cookie中设置State,具体取决于哪种类型状态信息。我认为我们都可以对此达成共识。如果需要,请使用自定义的HTTP标头(X-My-Header)。

类似地,内容在查询正文中仅属于一个位置,属于查询字符串或http multipart, /或JSON内容。这与服务器向您发送内容时从服务器收到的内容一致。因此,您不应该无礼,并以不同的方式来做。例如mysite.com/article/5/page=2,其中您部分地知道每个部分的含义(例如article和5这样的基础知识显然会为我提供id为5的article类型的数据),并且其他参数被指定为URI的一部分。如果您知道URI中的某个点之后,“文件夹”是成对的键值,则它们可以采用page=2page/2的形式。

过滤器始终位于查询字符串中,因为尽管它们是查找正确数据的一部分,但它们仅在其中返回子集或对定位符单独返回的内容的修改。 mysite.com/article/?query=Obama(子集)中的搜索是一个过滤器,/article/5?order=backwards(修改)中的搜索也是一个过滤器。考虑一下它的作用,而不仅仅是它的名字!

如果“视图”确定输出格式,则它是一个过滤器(mysite.com/article/5?view=pdf),因为它返回对找到的资源的修改,而不是返回到我们想要的资源。如果它决定了我们看到文章的哪个特定部分(mysite.com/article/5/view=summary),那么它就是一个定位器。在资源中找到特定的东西就是在...。子集过滤可以返回任意数量的结果(甚至为0)。定位将始终找到某个特定实例(如果存在)。修改过滤将返回与定位器相同的数据,但修改后除外(如果允许这样的修改)。

希望人们在丢失放置物品的地方迷失时,这会给人们一些尤里卡的时刻!

评论


那么为什么没有id过滤器呢?它返回资源的子集

–乔纳森。
2013年12月31日下午3:18

@乔纳森否,它返回特定的资源,即文章编号5。过滤器始终是缩小资源集合中搜索范围的一种方法。如果您只想要该特定资源,那么应该有一种指定的方式来获取该资源。过滤意味着您可以返回多个资源。 ID不是过滤器,而是确定的单个资源。如果您拥有RANGE ID,那么即使该范围仅包含一个ID,也将是一个过滤器。如果过滤器还包括资源类型,则它将返回ID为5的所有资源,而不仅仅是文章。

– Tor Valamo
2014年1月3日在5:16



@Jonathan .:就像提到的DarrelMiller一样,在id未知的情况下,您希望对object / id的请求返回404,而您希望object?id = id返回并清空列表。另外,我认为任何类型的过滤/子设置都应返回一个列表。

– njzk2
2014年8月12日下午16:47

页面是一个困难的页面,因为正如您所说的,它可以是资源(页面集合)的过滤器,但是同时,它也是该集合中的特定资源。我将始终按定位器而不是过滤器请求文章页面。但是,页面可以是某物列表(例如用户列表)的过滤器。但是页面本质上是定界符,也就是“从项目(page-1)*每页开始并显示每页项目”。出于各种原因,将其用作过滤器是正确的。称其为“页面”在技术上是错误的。语义上更正确的是将其称为“ from”或“ startAt”

– Tor Valamo
2014年10月24日19:50



(续)“页面”的语义含义是它是不变的特定资源。它来自物理打印。如果我们从来没有书籍或印刷品,那么“页面”就不是真正的单词。如果您有一个动态的项目列表,分为“页面”,则应该提供一个特定的起点,无论是数字,字母还是特定于项目,以及“每页有多少”过滤器。如果要引用您列表中的内容,请提供详细信息。另外,我不想转到第5页,只是意识到您现在已将内部页面更改为50,而不是20。

– Tor Valamo
2014年10月24日19:59



#3 楼

这取决于设计。 REST over HTTP没有针对URI的规则(主要是它们是唯一的)。通常涉及品味和直觉问题。

我采用以下方法:


url path-element:资源及其路径元素形成目录遍历和子资源(例如/ items / {id},/ users / items)。如果不确定,请询问您的同事,是否认为遍历并且他们认为在“另一个目录”中,最可能的路径元素是正确的选择
url参数:当真正没有遍历时(具有多个查询参数的搜索资源是很好的例子)


评论


实际上,关于URI的外观有相当明确的规则,而关于如何将其应用于RESTful URI的定义也很少。

– DanMan
2014年5月24日15:45

#4 楼

IMO的参数应该更好地作为查询参数。 url用于标识资源,而添加的查询参数用于指定所需的资源部分,资源应具有的任何状态等。

评论


实际上,路径和查询都可以组合使用来标识资源。 RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query中对此进行了澄清

– Darrel Miller
2010-10-26 17:30

@DarrelMiller我知道这是一篇老文章,但我想了解更多有关事实,也可以使用查询参数来标识资源。您提供的链接现已失效。我已经看过RFC3986,但看不到您如何推论这个事实。而且,根据定义,标识符参数不应是可选的,因此似乎不适合使用查询参数进行标识。

–米凯尔·马拉奇(Mickael Marrache)
13年4月15日在18:38

@MickaelMarrache参见第3.4节tools.ietf.org/html/rfc3986#section-3.4中的第一行

– Darrel Miller
13年4月15日在19:23

@DarrelMiller谢谢!我的问题来自以下事实:通常,中间HTTP组件不缓存包含查询字符串的请求的响应。因此,似乎查询参数更适合根据某些条件搜索资源,而不是唯一地标识资源。

–米凯尔·马拉奇(Mickael Marrache)
13年4月16日在7:31

#5 楼

根据REST实现,

1)路径变量用于对资源的直接操作,例如联系人或歌曲
例如。
GET等/ api / resource / {songid}或
GET等/ api / resource / {contactid}将返回各自的数据。

2)查询权限/参数用于间接资源,例如歌曲的元数据
例如。,
GET / api / resource / {songid}?元数据= genres,它将返回该特定歌曲的流派数据。

评论


实际上没有REST标准。每个维基百科:与基于SOAP的Web服务不同,没有针对RESTful Web API的“正式”标准。[14]这是因为REST是一种体系结构样式,与作为协议的SOAP不同。即使REST不是标准,但诸如Web之类的RESTful实现也可以使用HTTP,URI,XML等标准。

– DavidRR
2013年9月4日15:58

我不喜欢2方法。我宁愿使用/ api / genres?songid = 123或/ api / songs / {song-id} / genres

–巴特杯
2014年1月10日在2:37



@ Bart,Satish在路径中指的是变量,这实际上是您所偏好的。.但是,如果流派实际上是元数据,而不是歌曲实体/资源的字段,那么我可以看到更多的敏感性。在上面使用查询字符串。

–布雷特·卡斯威尔(Brett Caswell)
2015年3月20日19:49



@BrettCaswell知道了!感谢您指出。真的很感激!

–巴特杯
15年3月21日在4:21

#6 楼

根据Universe-resource-locator提供的“上下文”对数据进行“打包”和POST,这对于定位器来说是#1。

请注意#2的限制。我更喜欢POST而不是#1。

注意:讨论了

的局限性。POST参数内容是否有最大大小? GET请求的长度是否有限制?和_GET

p.s中URL参数的最大大小。这些限制基于客户端功能(浏览器)和服务器(配置)。

评论


附加组件:机智的路由可以具有版本(通过标头区分),从而提供了已开发的功能,而无需更改使用您在restify中编写的其余完整(api)代码的代码->查找“版本化路由”

– dgm
2013年12月6日在21:42

#7 楼

根据URI标准,路径用于层次结构参数,而查询用于非层次结构参数。 Ofc。

在将多个URI分配给同一资源的情况下,我希望将参数-标识所必需-放入路径中,并将参数-必需将表示形式构建到查​​询中。 (对我来说,这样更容易布线。)例如,




/users/123/users/123?fields="name, age"


/users/users?name="John"&age=30


对于缩小地图,我喜欢使用以下方法:


/>
因此,构造URI的方法完全取决于您(以及服务器端路由器)。

注意:提及这些参数是查询参数。因此,您真正要做的是定义一种简单的查询语言。通过复杂的查询(包含诸如和或大于等的运算符),建议您使用已经存在的查询语言。 URI模板的功能非常有限...

#8 楼

作为经常在客户端上的程序员,我更喜欢查询参数。另外,对我来说,它将URL路径与参数分开,增加了清晰度,并提供了更大的可扩展性。它还使我在URL / URI构建和参数构建器之间具有独立的逻辑。

我喜欢曼努埃尔·阿尔达纳(Manuel aldana)关于其他选择的说法,如果其中涉及某种树木。我可以看到像这样的用户专用部件被树了起来。

#9 楼

没有硬性规定,但从我喜欢使用的纯粹概念角度来看,经验法则可以简单地总结如下:URI路径(按定义)代表资源,而查询参数实质上是该资源的修饰符。到目前为止,这可能无济于事...使用REST API,您可以使用GETPUTDELETE来对单个资源进行操作的主要方法。因此,可以将某些东西表示在路径中还是作为参数表示,可以简化为那些方法对于所讨论的表示是否有意义。您会合理地在该路径上执行某些操作吗,这样做在语义上是否合理?当然,您可以在任何地方进行某些操作并弯曲后端以进行处理,但是您应该进行以下操作:它代表实际资源的形式,而不是不必要的上下文化版本。对于集合,也可以使用PUT完成。如果您想添加到特定集合中,那么将是一个有意义的URL。

这仍然留下一些灰色区域,因为某些路径可能指向父资源的子代。在某种程度上是酌情决定的,并取决于它们的使用。这样做的一个硬性规定是,任何类型的传递表示都应使用查询参数来完成,因为它没有基础资源。问题(Twitter的API),参数代表传递查询,该查询根据资源状态(而不是层次结构)进行过滤。在那个特定示例中,将这些约束添加到由这些约束表示的集合中将是完全不合理的,而且该查询将不能被表示为对对象图而言有意义的路径。

采用这种面向资源的透视图,可以轻松地直接映射到您的域模型的对象图,并将API的逻辑推向一个清晰的状态,即一切工作都非常干净,并且以相当自我记录的方式运行。通过远离使用映射到通常不适合的数据模型(即RDBMS)的使用传统URL路由的系统,也可以使概念更清晰。 Apache Sling当然是一个不错的起点。在像Zope这样的系统中,对象遍历分派的概念也提供了更清晰的模拟。

#10 楼

这是我的看法。

查询参数用作请求的元数据。它们充当现有资源调用的过滤器或修饰符。

示例:

/calendar/2014-08-08/events

应提供当天的日历事件。

如果要特定事件类别

/calendar/2014-08-08/events?category=appointments

,或者如果您需要超过30分钟的事件

/calendar/2014-08-08/events?duration=30

石蕊测试检查是否仍然可以在没有查询参数的情况下满足请求。

#11 楼

我通常倾向于#2,作为查询参数(即/ api / resource?parameter = value)。

第三个选择是将parameter = value实际发布到主体中。

这是因为它对多参数资源更有效,并且在将来的使用中具有更大的可扩展性。 。这导致了令人困惑的API。

#12 楼

这个主题的一个“维度”被遗漏了,但它非常重要:有时候,“最佳实践”必须与我们正在实现或通过REST功能增强的平台相结合。实际示例:

当今许多Web应用程序都实现了MVC(模型,视图,控制器)体系结构。他们假定提供了一定的标准路径,甚至在这些Web应用程序带有“启用SEO URL”选项时也是如此。

仅提及一个相当著名的Web应用程序:OpenCart电子商务商店。
当管理员启用“ SEO URL”时,它期望所说的URL以非常标准的MVC格式出现,例如: br />
special-offers是将处理URL的MVC控制器(显示特价页面)。
list-all是控制器的操作或要调用的函数名称。 (*)
limit = 25是一个选项,指出每页将显示25个项目。

(*)list-all是我为清楚起见而使用的虚构函数名称。实际上,OpenCart和大多数MVC框架具有默认的,隐含的(通常在URL中省略)index函数,当用户希望执行默认操作时会调用该函数。因此,真实世界的网址应为:

http://www.domain.tld/special-offers/list-all?limit=25


现在,使用与上述类似的相当标准的应用程序或框架结构,您通常会获得经过优化的Web服务器为此,它会重写URL(真正的“非SEOed URL”将是:http://www.domain.tld/index.php?route=special-offers/list-all&limit=25)。 “,除非您是系统管理员,否则请确切地知道如何调整Apache / NGinx重写配置(后者可能很讨厌!)等等。

因此,您的REST API通常会更好遵循引用的Web应用程序的标准,既要与Web应用程序保持一致,又要简化/提高速度(从而节省预算)。

回到上面的实际示例,一个一致的REST API就是带有类似URL的东西:
>
http://www.domain.tld/special-offers?limit=25


混合了“路径形成”参数和“查询形成”参数。

#13 楼

我看到很多REST API不能很好地处理参数。经常出现的一个例子是URI包含个人身份信息。

http://software.danielwatrous.com/design-principles-for-rest-apis/

我认为一个必然的问题是何时参数不应该是参数,但应将其移至请求的HEADER或BODY。

#14 楼

这是一个非常有趣的问题。

您可以同时使用它们,对此主题没有严格的规定,但是使用URI路径变量具有一些优点:



缓存:
Internet上的大多数Web缓存服务都在包含查询参数时不缓存GET请求。
之所以这样做,是因为许多RPC系统都使用GET请求更改服务器中的数据(失败!获取必须是一种安全的方法)

但是,如果您使用路径变量,则所有这些服务都可以缓存GET请求。



层次结构:
路径变量可以表示层次结构:
/ City / Street / Place

它为用户提供有关结构的更多信息数据。

但是如果您的数据没有任何层次结构关系,您仍然可以使用路径变量,使用逗号或分号:

/城市/经度,纬度

通常,在参数排序很重要时使用逗号,在参数排序时使用分号无关紧要:

/ IconGenerator / red; blue; green

其中的一部分,在某些情况下,使用查询字符串变量很常见:


在需要浏览器将HTML表单变量自动放入URI时
在处理算法时。例如,谷歌引擎使用查询字符串:

http:// www.google.com/search?q=rest

总而言之,没有任何强烈的理由要使用此方法之一,但只要有可能,请使用URI变量。