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

我们正在推出一个新的REST API,我想要一些关于我们应该如何格式化输入参数的最佳实践的社区意见:

现在,我们的API非常以JSON为中心(只返回JSON)。是否希望/需要返回XML是另一个问题。

由于我们的API输出是以JSON为中心的,我们一直在走一条路径,我们的输入有点以JSON为中心,我一直在想,这可能对一些人来说很方便,但总的来说很奇怪。

例如,要获得一些产品细节,可以同时提取多个产品,我们目前有:

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

我们是否可以将其简化为:

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

或者有JSON输入方便吗?更痛苦?

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

更复杂的情况是当我们想要提供更复杂的输入时。例如,如果我们想在搜索中允许多个过滤器:

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

我们不一定要把过滤器类型(例如productType和color)作为请求名称,就像这样:

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

因为我们想把所有的过滤器输入组合在一起。

说到底,这真的重要吗?可能有太多的JSON util,输入类型并不那么重要。

我知道对API进行AJAX调用的JavaScript客户端可能会喜欢JSON输入,以使他们的工作更轻松。

737932 次浏览

将值列表作为URL参数传递的标准方法是重复它们:

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

大多数服务器代码将其解释为一个值列表,尽管许多服务器有单个值的简化,因此您可能需要查看。

带分隔符的值也可以。

如果你需要向服务器发送JSON,我不喜欢在URL中看到它(这是一种不同的格式)。具体来说,url有大小限制(理论上没有限制,但实际上是这样)。

我见过一些人用rest完成复杂查询的方法分为两步:

  1. POST你的查询要求,接收回一个ID(本质上创建一个搜索条件资源)
  2. GET搜索,引用上面的ID
  3. 如果需要,可以选择删除查询需求,但是注意这些需求是可重用的。

第一个例子:

一个正常的乘积查找应该是这样的

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

所以我认为最好的做法是由你来做这件事

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

第二个案例

使用查询字符串参数进行搜索-就像这样。我倾向于用AND和OR组合术语,而不是使用[]

附:这可能是主观的,所以做你觉得舒服的事情。

将数据放在url中的原因是链接可以粘贴到站点上/在用户之间共享。如果这不是一个问题,无论如何使用JSON/ POST代替。

编辑:经过反思,我认为这种方法适合具有复合键的实体,但不适用于多个实体的查询。

第一:

我认为有两种方法

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

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

http://our.api.com/Product/<id1>,<id2>:正如James所建议的,可以是一个选项,因为Product标签后面是一个参数

或者我最喜欢的是:

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

第二个

当你必须对url调用进行查询时。我建议再次使用HATEOAS。

1)对http://our.api.com/term/pumas/productType/clothing/color/black进行get调用

2)对http://our.api.com/term/pumas/productType/clothing,bags/color/black,red进行get调用

3)(使用HATEOAS)做一个得到调用' http://our.api.com/term/pumas/productType/ ->接收url所有服装可能的url ->调用你想要的(服装和包)->接收可能的颜色url ->调用你想要的

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

后退一步

首先,REST将URI描述为通用唯一ID。太多的人纠结于uri的结构以及哪些uri比其他uri更“rest化”。这个论点就像说给一个人起名叫“Bob”比给他起名叫“Joe”更好一样可笑——这两个名字都能完成“识别一个人”的工作。 URI只不过是一个统一的惟一名称。

因此,在REST的眼中,争论?id=["101404","7267261"]是否比?id=101404,7267261\Product\101404,7267261更宁静是徒劳的。

话虽如此,很多时候,uri的构造方式通常可以作为rest式服务中其他问题的良好指示器。在您的uri和问题中有一些危险信号。

建议

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

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

    uri标识资源。每个资源都应该有一个规范URI。这并不意味着你不能有两个uri指向同一个资源——有很好的定义方法来做这件事。如果你决定同时使用JSON和基于列表的格式(或任何其他格式),你需要决定哪一种格式是主要的规范 URI。对指向同一“资源”的其他uri的所有响应都应该包括Content-Location

    继续使用名称类比,拥有多个uri就像拥有人们的昵称一样。这是完全可以接受的,而且通常也很方便,但是如果我使用昵称,我可能还是想知道他们的全名——指那个人的“官方”方式。这样,当有人提到某人的全名“尼古拉斯·特斯萨”时,我就知道他们说的是我所说的“尼克”

  2. 在URI中“搜索”

    更复杂的情况是当我们想要提供更复杂的输入时。例如,如果我们想在搜索中允许多个过滤器…

    我的一般经验法则是,如果您的URI包含一个动词,那么它可能表示某些东西关闭了。URI标识一个资源,但是它们不应该指示我们对该资源所做的什么。这就是HTTP的工作,或者用restful术语来说,我们的“统一接口”。

    为了彻底打破名称类比,在URI中使用动词就像在想要与某人交互时更改他们的名字一样。如果我与Bob互动,当我想对Bob说Hi时,Bob的名字不会变成“BobHi”。类似地,当我们想要“搜索”产品时,我们的URI结构不应该从“/Product/…”变成“/ search /…”

回答你最初的问题

  1. 关于["101404","7267261"]101404,7267261:为了简单起见,我的建议是避免使用JSON语法(即,当你真的没有必要时,不要要求你的用户进行URL编码)。这将使您的API更加可用。更好的是,正如其他人所建议的那样,使用标准的application/x-www-form-urlencoded格式,因为它可能是最终用户最熟悉的格式(例如?id[]=101404&id[]=7267261)。它可能不是“漂亮的”,但漂亮的uri并不一定意味着可用的uri。然而,重申一下我最初的观点,最终当谈到REST时,这并不重要。不要过于纠结于此。

  2. 您的复杂搜索URI示例可以用与产品示例非常相同的方式解决。我建议再次使用application/x-www-form-urlencoded格式,因为它已经是许多人熟悉的标准。另外,我建议将两者合并。

你的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更加同质化了。用户知道,如果他们想检索一个Product或List of Products(两者在RESTful术语中都被认为是一个单一的“资源”),他们对/Product/... uri感兴趣。

我会支持nategood的回答,因为它是完整的,它似乎满足了你的需要。尽管如此,我还是想补充一个关于以这种方式识别多个(1个或更多)资源的评论:

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

在这样做时,你:

将客户端复杂化 通过强迫他们将你的响应解释为一个数组,这对我来说是违反直觉的,如果我提出以下请求:http://our.api.com/Product/101404

创建冗余api 用一个API获取所有产品,上面的API获取1个或多个产品。因为为了用户体验,你不应该向用户显示超过1页的详细信息,我认为超过1个ID是没有用的,纯粹是用来过滤产品的

这可能不是什么问题,但是您要么必须自己在服务器端通过返回单个实体(通过验证您的响应是否包含一个或多个实体)来处理这个问题,要么让客户端管理它。

例子

我想从令人惊异的订购一本书。我确切地知道这是哪本书,当我导航恐怖书籍时,我在列表中看到它:

  1. 10000条惊人的线,0个惊人的测试
  2. 神奇怪物的回归
  3. 让我们复制令人惊叹的代码
  4. 结局的惊人开端

在选择第二本书后,我被重定向到一个详细介绍列表中书籍部分的页面:

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

还是在一页里只给我这本书的全部细节?

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

我的意见

我建议在获取该资源的详细信息时,当保证惟一性时,在路径变量中使用ID。例如,下面的api提供了获取特定资源详细信息的多种方法(假设产品有唯一的ID,该产品的规范有唯一的名称,并且您可以自顶向下导航):

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

当您需要多个资源时,我建议从更大的集合中筛选。同样的例子:

/products?ids=

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