REST API最佳实践:在哪里放置参数?

REST API可以至少以两种方式拥有参数:

  1. 作为url路径的一部分(即/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路径中会被认为是更好的设计吗?

1022347 次浏览

在我看来,参数应该更好地作为查询参数。url用于标识资源,而添加的查询参数用于指定您想要的资源的哪一部分、资源应该具有的任何状态等等。

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

我采取以下方法:

  • url path-element:资源和它的path-element组成了一个目录遍历和一个子资源(例如/items/{id}, /users/items)。不确定的时候问问你的同事,如果他们认为遍历和他们认为在“另一个目录”最有可能的路径元素是正确的选择
  • Url参数:当没有遍历时(有多个查询参数的搜索资源就是一个很好的例子)

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

第三种选择是将parameter=value放到函数体中。

这是因为它可以更好地用于多参数资源,并且具有更强的可扩展性以供将来使用。

不管你选哪一个,确保你只选一个,不要混搭。这导致了一个令人困惑的API。

如果有记录在案的最佳实践,我还没有找到它们。然而,当我决定在url中放置参数时,这里有一些指导原则:

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

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

然而,如果你想返回一个空列表,那么当参数没有找到时,我建议使用查询字符串参数。例如/contacts?name=dave

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

我希望唯一标识符位于路径段中,而不是查询参数中。

uri的官方规则可以在RFC规范在这里中找到。还有另一个非常有用的RFC规范在这里,它定义了uri参数化的规则。

作为一名经常在客户端工作的程序员,我更喜欢使用查询参数。另外,对我来说,它将URL路径从参数中分离出来,增加了清晰度,并提供了更多的可扩展性。它还允许我在URL/URI构建和参数构建器之间有单独的逻辑。

我很喜欢曼努埃尔·阿尔达纳说的另一种选择如果有某种树的话。我可以看到用户特定的部分被树形分开。

根据universe-resource-locator提供的“上下文”“打包”和POST数据,这对于定位器来说意味着#1。

注意第二条的局限性。比起第一条,我更喜欢post。

注:此处讨论的局限性

POST参数内容有最大大小吗?中的POST

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

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

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

这仍然留下了一些灰色区域,因为一些路径可以指向多少子代的父资源,这在某种程度上是自由裁量的,取决于他们的使用。这所画出的一条界限是,任何类型的传递表示都应该使用查询参数来完成,因为它不会有底层资源。

为了响应原始问题中给出的真实示例(Twitter的API),参数表示一个可传递查询,它过滤资源的状态(而不是层次结构)。在那个特定的例子中,向由这些约束表示的集合中添加数据是完全不合理的,而且该查询将无法表示为在对象图中有任何意义的路径。

采用这种类型的面向资源的透视图可以很容易地直接映射到领域模型的对象图,并将API的逻辑驱动到一个点,即一旦它变得清晰,一切都以一种相当自记录的方式非常干净地工作。通过远离使用传统URL路由映射到通常不拟合的数据模型(即RDBMS)的系统,这个概念也可以更清楚。Apache吊索肯定是一个很好的开始。在Zope这样的系统中,对象遍历分派的概念也提供了一个更清晰的类比。

根据REST实现,

1) 路径变量用于对资源的直接操作,如联系人或歌曲 交货。< br > GET etc /api/resource/{songid}或
GET etc /api/resource/{contactid}将返回相应的数据。< / p >

2) 查询烫发/参数用于间接资源,如歌曲的元数据 . ., GET / api /资源/ {songid} ?元数据=类型,它将返回特定歌曲的类型数据

回答晚了,但我将对已经分享的内容添加一些额外的见解,即请求有几种类型的“参数”,您应该考虑到这一点。

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

现在,让我们看看这些参数可能发生的不同情况。

  1. 请求头&饼干
  2. URL查询字符串("GET"变量)
  3. URL路径
  4. 正文查询字符串/多部分("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=2page/2的形式,如果你知道在URI中的某个点之后,“文件夹”是成对的键-值。

过滤器总是在查询字符串中,因为尽管它们是查找正确数据的一部分,但它们只是返回Locators单独返回的数据的子集或修改。mysite.com/article/?query=Obama(子集)中的搜索是一个过滤器,/article/5?order=backwards(修改)也是。想想它是做什么的,而不仅仅是它的名字!

如果"view"决定输出格式,则它是一个过滤器(mysite.com/article/5?view=pdf),因为它返回对找到的资源的修改,而不是指向我们想要的资源。如果它决定我们要看到文章的哪个特定部分(mysite.com/article/5/view=summary),则它是定位器。

记住,缩小一组资源是过滤。在资源中定位某个特定的东西就是定位…咄。子集筛选可以返回任意数量的结果(甚至是0)。定位总是会找到某事物的特定实例(如果它存在)。修改筛选将返回与定位器相同的数据,除了被修改(如果允许这样的修改)。

如果人们不知道该把东西放在哪里,希望这能给他们一些顿悟的时刻!

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

实际的例子:

现在许多web应用程序都实现了MVC(模型、视图、控制器)架构。他们假设提供了一个特定的标准路径,当那些web应用程序带有“启用SEO url”选项时更是如此。

这里只提一个相当著名的网络应用程序: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函数,当用户想要执行默认操作时,该函数会被调用。真实世界的URL是:

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 url)

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

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

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

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

我认为一个必然的问题是当一个参数根本不应该是一个参数,而应该被移动到请求的身体

以下是我的观点。

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

例子:

/calendar/2014-08-08/events

应该给出当天的日历事件。

如果需要特定类别的事件

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

或者你需要超过30分钟的比赛

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

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

根据URI标准,路径是用于层次参数的,查询是用于非层次参数的。离岸金融中心。对你来说什么是等级制度是非常主观的。

在将多个uri分配给相同资源的情况下,我喜欢将用于标识的参数放入路径中,将用于构建表示的参数放入查询中。(对我来说,这种方式更容易路由。)

例如:

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

对于map reduce,我喜欢使用以下方法:

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

因此,如何构造uri实际上取决于您(以及您的服务器端路由器)。

注意:只是提到这些参数是查询参数。因此,您真正要做的是定义一种简单的查询语言。对于复杂查询(包含与、或、大于等操作符),我建议您使用已经存在的查询语言。URI模板的能力非常有限…

这是个很有趣的问题。

你可以同时使用它们,关于这个主题没有任何严格的规则,但是使用URI路径变量有一些优点:

    <李> 缓存: 大多数互联网上的web缓存服务不缓存GET请求时,他们包含查询参数。 他们这样做是因为有很多RPC系统使用GET请求来更改服务器中的数据(失败!!)Get必须是一个安全的方法)

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

    <李> 层次结构: 路径变量可以表示层次结构: 李/城市/街道/ < / >

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

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

/城市/经度,纬度

作为规则,当参数的顺序重要时使用逗号,当顺序不重要时使用分号:

/ IconGenerator /红色,蓝色,绿色

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

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

http:// www.google.com/search ? q =休息

总而言之,没有任何强烈的理由使用这种方法,但只要可以,就使用URI变量。