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的规则。
#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=2
或page/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,您可以使用GET
,PUT
和DELETE
来对单个资源进行操作的主要方法。因此,可以将某些东西表示在路径中还是作为参数表示,可以简化为那些方法对于所讨论的表示是否有意义。您会合理地在该路径上执行某些操作吗,这样做在语义上是否合理?当然,您可以在任何地方进行某些操作并弯曲后端以进行处理,但是您应该进行以下操作:它代表实际资源的形式,而不是不必要的上下文化版本。对于集合,也可以使用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变量。
评论
官方规则URI和sepc草案非常有用且有趣! :-)
– KajMagnus
2011年4月16日在10:54
404错误测试对我有很多帮助,可以避免将信息放入属于查询参数,标头或请求正文的路径中。谢谢指出!
–凯文·康登(Kevin Condon)
2014年12月17日15:59