我们正在推出一个新的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实用程序,输入类型并不重要。
我知道我们的JavaScript客户端对API进行AJAX调用可能会欣赏JSON输入以使他们的生活更轻松。
答案 0 :(得分:310)
首先,REST将URI描述为通用唯一ID。有太多人陷入了URI的结构,哪些URI比其他URI更“安静”。 这种说法和说“某人”命名某人“Bob”比命名他“Joe”更好 - 这两个名字都可以完成“识别一个人”的工作。一个URI只不过是一个 >通用唯一名称。
因此,在REST的眼中,争论?id=["101404","7267261"]
是否比?id=101404,7267261
或\Product\101404,7267261
更安静是有点徒劳的。
现在,已经说过,很多时候如何构造URI通常可以作为RESTful服务中其他问题的良好指标。一般来说,你的URI和问题都有一些红旗。
同一资源和Content-Location
我们可能希望接受这两种风格,但这种灵活性是否会导致更多的混乱和头痛(可维护性,文档等)?
URI标识资源。每个资源都应包含一个规范URI。这并不意味着您不能将两个URI指向同一资源但有明确定义的方法可以执行此操作。如果您决定同时使用JSON和基于列表的格式(或任何其他格式),则需要确定哪些格式是主规范 URI。对指向相同“资源”的其他URI的所有响应都应包含Content-Location
header。
坚持使用名称类比,拥有多个URI就像为人们设置昵称。这是完全可以接受的,而且通常非常方便,但是如果我使用昵称,我仍然可能想知道他们的全名 - “官方”方式来引用那个人。这样,当有人用他们的全名“Nichloas Telsa”提及某人时,我知道他们正在谈论我称之为“尼克”的同一个人。
您的URI中的“搜索”
更复杂的情况是我们想要提供更复杂的输入。例如,如果我们想在搜索中允许多个过滤器...
我的一般经验法则是,如果您的URI包含动词,则可能表示某些内容已关闭。 URI标识资源,但是它们不应该指示我们正在对该资源执行的 。这是HTTP的工作或宁静的术语,我们的“统一界面”。
要击败名称类比死,在URI中使用动词就像在想要与他们交互时更改某人的名字一样。如果我和鲍勃互动,当我想对他说嗨时,鲍勃的名字不会成为“BobHi”。同样,当我们想要“搜索”产品时,我们的URI结构不应该从“/ Product / ...”更改为“/ Search /...”。
关于["101404","7267261"]
vs 101404,7267261
:我的建议是为了简单起见避免使用JSON语法(即,当你不需要时,不要求你的用户进行URL编码)。这将使您的API更有用。更好的是,正如其他人所建议的那样,请使用标准application/x-www-form-urlencoded
格式,因为最终用户可能最熟悉它(例如?id[]=101404&id[]=7267261
)。它可能不是“漂亮”,但Pretty URIs并不一定意味着可用的URI。但是,重申我的初步观点,最终在谈到REST时,无关紧要。不要过分依赖它。
您的复杂搜索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有点均匀化。用户知道如果他们想要检索产品或产品列表(两者都被视为RESTful术语中的单个“资源”),他们对/Product/...
URI感兴趣。
答案 1 :(得分:202)
将值列表作为URL参数传递的标准方法是重复它们:
http://our.api.com/Product?id=101404&id=7267261
大多数服务器代码会将此解释为值列表,尽管许多服务器代码具有单值简化,因此您可能需要查看。
定界值也可以。
如果您需要将JSON发送到服务器,我不喜欢在URL中看到它(这是一种不同的格式)。特别是,URL具有大小限制(实际上,如果不是理论上的话)。
我看到一些RESTful执行复杂查询的方式分为两步:
POST
您的查询要求,接收ID(实质上是创建搜索条件资源)GET
搜索,引用上述ID 答案 2 :(得分:19)
<强>首先强>
我认为你可以做2种方式
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
,该调用应返回{{1}的等效网址并在此之后调用它们。
<强>第二强>
当您必须对网址调用进行查询时。我建议再次使用HATEOAS。
1)拨打http://our.api.com/Product/<id>
2)打电话给http://our.api.com/term/pumas/productType/clothing/color/black
3)(使用HATEOAS)打电话给`http://our.api.com/term/pumas/productType/ - &gt;收到所有衣服可能的网址 - &gt;打电话给你想要的(衣服和包包) - &gt;收到可能的颜色网址 - &gt;打电话给你想要的人
答案 3 :(得分:7)
第一种情况:
正常的产品查找看起来像这样
http://our.api.com/product/1
所以我认为最好的做法就是为你做这个
http://our.api.com/Product/101404,7267261
第二种情况
使用查询字符串参数进行搜索 - 就像这样。我很想将术语与AND和OR结合使用,而不是使用[]
。
PS这可能是主观的,所以做你觉得舒服的事情。
将数据放入网址的原因是链接可以粘贴在网站上/在用户之间共享。如果这不是问题,请务必使用JSON / POST。
编辑:反思我认为这种方法适合具有复合键的实体,但不适用于多个实体的查询。
答案 4 :(得分:7)
您可能想查看RFC 6570。此URI模板规范显示了url如何包含参数的许多示例。
答案 5 :(得分:0)
我会支持nategood的答案,因为它已经完成,似乎满足了你的需求。不过,我想添加一条关于识别多个(1个或多个)资源的评论:
http://our.api.com/Product/101404,7267261
这样做,您:
复杂化客户端
通过强制他们将您的响应解释为数组,如果我提出以下请求,这对我来说是反直觉的:http://our.api.com/Product/101404
创建冗余API 使用一个API获取所有产品,使用上面的一个获得1个或多个产品。由于您不应该为了UX而向用户显示超过1页的详细信息,我相信拥有1个以上的ID将毫无用处,仅用于过滤产品。
这可能不是那么有问题,但您必须通过返回单个实体(通过验证您的响应是否包含一个或多个实体)或让客户端管理它来自己处理服务器端。
示例强>
我想从 Amazing 订购一本书。我确切地知道它是哪本书,我在浏览恐怖书时在列表中看到它:
选择第二本书后,我被重定向到一个详细列出书籍部分的页面:
--------------------------------------------
Book #1
--------------------------------------------
Title: The return of the amazing monster
Summary:
Pages:
Publisher:
--------------------------------------------
或者只在页面中提供该书的完整详细信息?
---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------
我的意见
当获取此资源的详细信息时,我建议在保证unicity时使用路径变量中的ID。例如,下面的API提供了多种获取特定资源详细信息的方法(假设产品具有唯一ID,并且该产品的规范具有唯一名称,您可以自上而下导航):
/products/{id}
/products/{id}/specs/{name}
当您需要超过1个资源时,我建议您从更大的集合中进行过滤。对于同一个例子:
/products?ids=
当然,这是我的意见,因为它没有强加。