REST API最佳实践:在哪里放置参数?[关闭]


348

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

  1. 作为网址路径的一部分(即/api/resource/parametervalue
  2. 作为查询参数(即/api/resource?parameter=value

最佳做法是什么?是否有使用1和何时使用2的一般准则?

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

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

Answers:


254

如果有记录的最佳做法,我还没有找到它们。但是,在确定将参数放置在url中的位置时,使用了一些准则:

可选参数往往更容易放入查询字符串中。

如果您想在参数值与现有资源不对应时返回404错误,那么我倾向于使用路径段参数。例如/customer/232232不是有效的客户ID。

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

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

我更喜欢唯一标识符位于路径段而不是查询参数中。

URI的正式规则可在此RFC规范中找到。这里还有另一个非常有用的RFC规范它定义了参数化URI的规则。


5
官方规则URI和草稿sepc非常有用且有趣!:-)
KajMagnus 2011年

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

152

较晚的答案,但我将对已共享的内容添加一些其他见解,即请求有几种类型的“参数”,您应该考虑到这一点。

  1. 定位符-例如ID或操作/视图之类的资源标识符
  2. 过滤器-例如提供搜索,排序或缩小结果集范围的参数。
  3. 状态-例如会话标识,API密钥,Whatevs。
  4. 内容-例如要存储的数据。

现在,让我们看一下这些参数可以放置的不同位置。

  1. 请求标题和Cookie
  2. URL查询字符串(“ GET”变量)
  3. 网址路径
  4. 正文查询字符串/多部分(“ POST”变量)

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

类似地,内容在请求主体中只有一个属于的地方,可以作为查询字符串或作为HTTP多部分和/或JSON内容。这与服务器向您发送内容时从服务器收到的内容一致。因此,您不应该无礼,并以不同的方式进行操作。

将诸如“ id = 5”或“ action = refresh”或“ page = 2”之类的定位符作为URL路径是有意义的,例如mysite.com/article/5/page=2您可以部分地知道每个部分的含义(诸如文章和5显然意味着获取ID为5的article类型的数据,并且将其他参数指定为URI的一部分。它们可以采用的形式page=2,或者page/2如果您知道URI中的某个点之后,“文件夹”是成对的键值。

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

如果“视图”确定输出格式,则它是一个过滤器(mysite.com/article/5?view=pdf),因为它返回对找到的资源的修改,而不是返回到我们想要的资源上。如果它决定了我们可以看到文章的哪一部分,mysite.com/article/5/view=summary那么它就是一个定位器。

请记住,缩小一组资源正在过滤。在资源中找到特定的东西就是在……。子集过滤可以返回任意数量的结果(甚至为0)。定位将始终找到某个特定实例(如果存在)。修改过滤将返回与定位器相同的数据,但修改后除外(如果允许这样的修改)。

希望如果有人迷失在哪里放置东西,这会给人们一些尤里卡的机会!


2
那为什么没有id过滤器呢?它返回资源的一个子集
乔纳森。

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

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

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

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

21

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

我采取以下方法:

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

1
实际上,关于URI的外观有相当明确的规则,而对于如何将其应用于RESTful URI则几乎没有歧义。
DanMan 2014年

18

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


7
实际上,路径和查询都可以组合使用来标识资源。RFC 3986http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
Darrel Miller 2010年

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


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

17

根据REST实施,

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

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


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

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

1
@ Bart,Satish在路径中指的是变量,这实际上是您所引用的首选项。.但是,如果流派实际上是元数据,而不是歌曲实体/资源的字段,那么我可以看到更多的敏感性。在使用上有一个查询字符串..
布雷特·卡斯威尔

@BrettCaswell知道了!感谢您指出。非常感谢!
Bart Calixto 2015年

16

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

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

注意:讨论了以下限制

POST in 是否有POST参数内容的最大大小?

GET in GET请求的长度是否有限制?_GET中的URL参数的最大大小

ps这些限制基于客户端功能(浏览器)和服务器(配置)。


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

5

根据URI标准,路径用于分层参数,而查询用于非分层参数。Ofc。对您来说分层是非常主观的。

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

例如:

  • /users/123/users/123?fields="name, age"
  • /users/users?name="John"&age=30

为了减少地图,我喜欢使用以下方法:

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

因此,如何构造URI取决于您(和服务器端路由器)。

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


4

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

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


4

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

这仍然留下一些灰色区域,因为某些路径可能指向父级资源的子级占多少,这在一定程度上是酌情决定的,并取决于其使用。这样做的一条硬线是,任何类型的传递表示都应使用查询参数来完成,因为它没有基础资源。

作为对原始问题(Twitter的API)中给出的真实示例的响应,这些参数表示一个传递查询,该查询根据资源的状态(而不是层次结构)进行过滤。在该特定示例中,将这些约束添加到由这些约束表示的集合中将是完全不合理的,并且进一步地,查询将无法被表示为在对象图方面有意义的路径。

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


4

这是我的意见。

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

例:

/calendar/2014-08-08/events

应该提供当天的日历活动。

如果您想要特定类别的事件

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

或者如果您需要30分钟以上的活动

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

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


2

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

第三种选择是将参数=值实际发布到主体中。

这是因为它对于多参数资源更有效,并且对于将来的使用更可扩展。

无论选择哪一个,请确保只选择一个,不要混搭。这导致了令人困惑的API。


2

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

实际示例:

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

仅提及一个相当著名的Web应用程序:一个OpenCart电子商务商店。当管理员启用“ SEO URL”时,它期望所述URL以非常标准的MVC格式出现,例如:

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

哪里

  • special-offers 是MVC控制器,将处理URL(显示“特价”页面)

  • list-all是要调用的控制器的动作或功能名称。(*)

  • limit = 25是一个选项,指出每页将显示25个项目。

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

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

使用现在与上述类似的相当标准的应用程序或框架结构,您通常会得到针对其进行优化的Web服务器,该服务器将为其重写URL(真正的“非SEOed URL”为:)http://www.domain.tld/index.php?route=special-offers/list-all&limit=25

因此,作为开发人员,您必须面对现有的基础结构并适应您的“最佳实践”,除非您是系统管理员,否则请确切地知道如何调整Apache / NGinx重写配置(后者可能很讨厌!)等等上。

因此,您的REST API在遵循引用的Web应用程序标准方面通常会更好,无论是与它的一致性还是简化/速度(从而节省预算)。

回到上面的实际示例,一致的REST API可能具有以下URL:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

或(非SEO网址)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

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



0

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

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

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

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

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

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

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

/城市/经度,纬度

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

/ IconGenerator /红色;蓝色;绿色

除了这些原因外,在某些情况下,使用查询字符串变量也很常见:

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

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

综上所述,没有充分的理由使用其中一种方法,但是只要有可能,就使用URI变量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.