REST API最佳实践:如何接受参数值列表作为input

我们正在推出一个新的REST API,我希望有一些关于如何input参数格式化的最佳实践的社区意见:

现在,我们的API非常以JSON为中心(只返回JSON)。 关于我们是否需要返回XML的争论是一个单独的问题。

由于我们的API输出是以JSON为中心的,所以我们一直在走下一条路,在这条路上我们的input是以JSON为中心的,而且我一直认为对于一些人来说可能是很方便的,但总的来说很奇怪。

例如,要获得一些产品的细节,可以一次拉多个产品,我们目前有:

http://our.api.com/Product?id=["101404","7267261"] 

我们是否应该简化为:

 http://our.api.com/Product?id=101404,7267261 

还是有方便的JSONinput? 更多的痛苦?

我们可能想要同时接受这两种风格,但这种灵活性是否会导致更多的混乱和头痛(可维护性,文档等)?

更复杂的情况是我们想要提供更复杂的input。 例如,如果我们想在search时允许多个filter:

 http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]} 

我们不一定要将filtertypes(例如productType和颜色)作为请求名称,如下所示:

 http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"] 

因为我们想把所有的filterinput分组在一起。

最后,这真的很重要吗? 可能有这么多JSON实用程序在那里inputtypes只是没有多大关系。

我知道我们的JavaScript客户端对API进行AJAX调用可能会喜欢JSONinput,使他们的生活更轻松。

退一步

首先,REST将URI描述为通用唯一的ID。 有太多的人抓住了URI的结构,哪些URI比其他的更“宁静”。 这个说法是荒谬的,因为说一个人叫“鲍勃”比称呼他“乔”更好 – 两个人都得到了“识别一个人”的工作。 URI不过是一个普遍唯一的名字。

所以在REST的眼中,争论是否?id=["101404","7267261"]?id=101404,7267261\Product\101404,7267261更宁静。

现在,已经说了很多次,URI是如何构build的,通常可以作为RESTful服务中其他问题的良好指标。 一般来说,你的URI和问题有几个红旗。

build议

  1. 同一资源和Content-Location多个URI

    我们可能想要同时接受这两种风格,但这种灵活性是否会导致更多的混乱和头痛(可维护性,文档等)?

    URI标识资源。 每个资源应该有一个规范的URI。 这并不意味着你不能有两个URI指向相同的资源, 有明确的方法去做。 如果您决定使用JSON和基于列表的格式(或任何其他格式),则需要决定哪些格式是主要的规范 URI。 对指向相同“资源”的其他URI的所有响应应该包括Content-Location标头 。

    使用名称类比来说,拥有多个URI就像拥有人的昵称一样。 这是完全可以接受的,而且往往是相当方便的,但是如果我使用昵称,我仍然可能想知道他们的全名 – “正式”的方式来引用这个人。 这样当有人用全名“Nichloas Telsa”提到某人的时候,我知道他们正在谈论我称为“尼克”的同一个人。

  2. 在你的URI中“search”

    更复杂的情况是我们想要提供更复杂的input。 例如,如果我们想要允许多个filtersearch…

    我的一般经验法则是,如果你的URI包含一个动词,这可能是一个表明某事是closures的。 URI标识一个资源,但是他们不应该指出我们对这个资源做了什么 。 这就是HTTP的工作,或者说是平静的界面。

    要打败这个名字比喻死亡,在URI中使用动词就像当你想与他们交互时改变某人的名字。 如果我和Bob互动,Bob的名字就不会成为“BobHi”,当我想对他说嗨。 同样,当我们要“search”产品时,我们的URI结构不应该从“/ Product / …”更改为“/ Search / …”。

回答你的初始问题

  1. 关于["101404","7267261"]101404,7267261 :我的build议是为了简单起见避免使用JSON语法(也就是说,当你不必要的时候,不要求你的用户做URL编码)。 这将使您的API更有用。 更好的是,正如其他人所build议的,使用标准的application/x-www-form-urlencoded格式,因为它可能是最终用户最熟悉的(例如?id[]=101404&id[]=7267261 )。 它可能不是“漂亮”,但漂亮的URI不一定意味着可用的URI。 不过,重申一下我的初衷,最后在谈到REST时,没关系。 不要太在意。

  2. 您的复杂searchURI示例可以以与您的产品示例非常相似的方式解决。 我build议再次使用application/x-www-form-urlencoded格式,因为它已经是很多人熟悉的标准了。 另外,我会build议合并这两个。

你的URI …

 /Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]} 

URI编码后的URI

 /Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D 

可以转化为…

 /Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red 

除了避免URL编码的要求,让事情看起来更加标准化之外,现在它已经对API进行了一些均一化处理。 用户知道,如果他们想要检索产品或产品列表(两者都被认为是RESTful术语中的单一“资源”),他们对/Product/... URI感兴趣。

将值列表作为URLparameter passing的标准方法是重复它们:

http://our.api.com/Product?id=101404&id=7267261

大多数服务器代码会将其解释为值列表,尽pipe许多服务器代码都具有单一值简化的function,所以您可能需要查看。

分隔值也是可以的。

如果你需要发送JSON到服务器,我不喜欢在URL中看到它(这是一种不同的格式)。 特别是URL有一个大小限制(实际上如果不是理论上的话)。

我看到有些人用RESTfully做了一个复杂的查询,分两步进行:

  1. POST您的查询要求,接收一个ID(实质上创build一个search条件资源)
  2. GETsearch,引用上面的ID
  3. 如果需要,可以select删除查询需求,但是请注意它们的需求可供重用。

第一:

我认为你可以做到这两点

http://our.api.com/Product/<id> :如果你只是想要一个logging

http://our.api.com/Product :如果你想要所有的logging

http://our.api.com/Product/<id1>,<id2> :正如詹姆斯所build议的,因为产品标签是一个参数

或者我最喜欢的是:

您可以使用超媒体作为 RestFul WS 的应用程序状态 (HATEOAS)属性的引擎,并调用http://our.api.com/Product来返回http://our.api.com/Product/<id>的等效url http://our.api.com/Product/<id>并在此之后调用它们。

第二

当你不得不对url调用进行查询时。 我会build议再次使用HATEOAS。

1)打电话给http://our.api.com/term/pumas/productType/clothing/color/black

2)打个电话http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3)(使用HATEOAS)打电话给` http://our.api.com/term/pumas/productType/ – >收到所有可能的url – >打电话给你想要的(衣服和包) – >接收可能的颜色URL – >调用你想要的

你可能想看看RFC 6570 。 这个URI模板规范显示了许多URL如何包含参数的例子。

第一种情况:

正常的产品查找将如下所示

http://our.api.com/product/1

所以我认为最好的做法是你做到这一点

http://our.api.com/Product/101404,7267261

第二种情况

用查询string参数search – 很好这样。 我会试图将术语与AND和OR相结合,而不是使用[]

PS这可能是主观的,所以做你觉得舒服的。

将数据放入url的原因是链接可以粘贴在用户的网站上/共享。 如果这不是一个问题,通过一切手段,而不是使用JSON / POST。

编辑:在反思我认为这种方法适合一个实体复合关键,但不是多个实体的查询。

我会和nategood的答案一起,因为它是完整的,它似乎有你的需要。 尽pipe如此,我想添加一条关于如何识别多个(1个或多个)资源的评论:

http://our.api.com/Product/101404,7267261

这样做,你:

通过强迫他们把你的回答作为一个数组来解释客户端 ,这对我来说是非常直观的,如果我提出以下要求: http://our.api.com/Product/101404 : http://our.api.com/Product/101404

用一个API 创build多余的 API来获取所有产品,以及获得一个或多个API。 由于您不应该为了用户体验而向用户展示超过1页的细节,我相信超过1个ID将无用而纯粹用于过滤产品。

这可能不会有问题,但是您必须通过返回一个实体(通过validation您的响应是否包含一个或多个实体)或让客户pipe理它来自己处理服务器端。

我想从Amazing订购一本书。 我确切地知道它是哪本书,并且在导航恐怖书籍时在列表中看到它:

  1. 10 000个惊人的线,0惊人的testing
  2. 惊人的怪物的回报
  3. 让我们复制惊人的代码
  4. 结束的惊人的开始

select第二本书后,我被redirect到一个详细列出书籍部分的页面:

 -------------------------------------------- Book #1 -------------------------------------------- Title: The return of the amazing monster Summary: Pages: Publisher: -------------------------------------------- 

或在一个页面给我的书只有完整的细节?

 --------------------------------- The return of the amazing monster --------------------------------- Summary: Pages: Publisher: --------------------------------- 

我的想法

当得到这个资源的细节时,我build议在保证unicity的时候在pathvariables中使用ID。 例如,下面的API提供了多种方法来获取特定资源的详细信息(假设产品具有唯一的ID,并且该产品的规格具有唯一的名称,您可以自上而下导航):

 /products/{id} /products/{id}/specs/{name} 

当你需要超过1资源,我会build议从一个更大的集合过滤。 对于同样的例子:

 /products?ids= 

当然,这是我的意见,因为它不是强加的。