REST API至少可以通过两种方式获取参数:
-
As part of the URL-path (即
/api/resource/parametervalue
) -
As a query argument (即
/api/resource?parameter=value
)
这里的最佳做法是什么?是否有任何一般指导原则何时使用1以及何时使用2?
真实世界的例子:Twitter使用查询参数来指定间隔 . ( http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321
)
将这些参数放在URL路径中会被认为是更好的设计吗?
14 回答
根据URI standard,路径用于分层参数,查询用于非分层参数 . OFC . 对你来说,它可以是非常主观的等级 .
在将多个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 templates的功能非常有限......
这是一个非常有趣的问题 .
您可以同时使用它们,对此主题没有任何严格的规则,但使用URI路径变量有一些优点:
但是如果使用路径变量,则所有这些服务都可以缓存您的GET请求 .
它为用户提供了有关数据结构的更多信息 .
但是,如果您的数据没有任何层次结构关系,您仍然可以使用路径变量,使用逗号或分号:
/市/经度,纬度
作为一项规则,当参数的排序很重要时使用逗号,当排序无关紧要时使用分号:
/ IconGenerator /红色,蓝色,绿色
除了这些原因之外,在某些情况下,使用查询字符串变量非常常见:
当您需要浏览器自动将HTML表单变量放入URI时
当您处理算法时 . 例如,Google引擎使用查询字符串:
http:// www.google.com/search?q=rest
总而言之,没有任何强有力的理由使用这种方法之一,但只要有可能,就使用URI变量 .
这个主题的一个“维度”已经被忽略了,但它非常重要:有时候“最佳实践”必须与我们正在实施的平台或扩展REST功能相结合 .
实际例子:
现在许多Web应用程序都实现了MVC(模型,视图,控制器)架构 . 他们假设提供了某种标准路径,当这些Web应用程序带有“启用SEO URL”选项时更是如此 .
只是提到一个相当着名的Web应用程序:一个OpenCart电子商务商店 . 当管理员启用“SEO URL”时,它希望所述URL具有相当标准的MVC格式,如:
哪里
special-offers
是处理URL的MVC控制器(显示特价商品页面)list-all
是要调用的控制器的操作或函数名称 . (*)limit = 25是一个选项,表示每页将显示25个项目 .
(*)
list-all
是我为清晰起见而使用的虚函数名 . 实际上,OpenCart和大多数MVC框架都有一个默认的暗示(通常在URL中省略)index
函数,当用户想要执行默认操作时会调用该函数 . 所以现实世界的URL将是:使用现在相当标准的应用程序或类似于上面的框架结构,您通常会得到一个针对它进行优化的Web服务器,它会为它重写URL(真正的"non SEOed URL"将是:
http://www.domain.tld/index.php?route=special-offers/list-all&limit=25
) .因此,作为开发人员,您面临处理现有基础架构并调整“最佳实践”的问题,除非您是系统管理员,否则确切地知道如何调整Apache / NGinx重写配置(后者可能很讨厌!)等等上 .
因此,您的REST API通常会更好地遵循引用Web应用程序的标准,既可以与其一致,又可以轻松/快速(从而节省预算) .
回到上面的实际例子,一致的REST API就像以下URL一样:
或(非SEO网址)
混合使用“路径形成”参数和“查询形成”参数 .
IMO参数应该更好作为查询参数 . url用于标识资源,而添加的查询参数用于指定所需资源的哪个部分,资源应具有的任何状态等 .
迟到的答案,但我会对已经分享的内容添加一些额外的见解,即请求有几种类型的“参数”,你应该考虑到这一点 .
定位器 - 例如资源标识符,例如ID或操作/视图
过滤器 - 例如提供搜索,排序或缩小结果集的参数 .
州 - 例如会话识别,api密钥,whatevs .
内容 - 例如要存储的数据 .
现在让我们来看看这些参数可能出现的不同位置 .
请求 Headers 和Cookie
URL查询字符串("GET"vars)
网址路径
正文查询字符串/ multipart("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
,其中部分你知道每个部分应该是什么意思(文章和5的基础知识显然意味着让我获得类型文章的数据与id 5)和其他参数被指定为URI的一部分 . 它们可以是page=2
或page/2
的形式,如果您知道在URI中的某个点之后"folders"是成对的键值 .过滤器总是放在查询字符串中,因为虽然它们是查找正确数据的一部分,但它们仅用于返回定位器单独返回的子集或修改 .
mysite.com/article/?query=Obama
(子集)中的搜索是一个过滤器,/article/5?order=backwards
(修改)也是如此 . 想想它的作用,而不仅仅是它的名称!如果"view"确定输出格式,则它是一个过滤器(
mysite.com/article/5?view=pdf
),因为它返回找到的资源的修改,而不是在我们想要的资源上进行归位 . 如果它决定我们看到的文章的哪个特定部分(mysite.com/article/5/view=summary
),那么它就是一个定位器 .请记住,缩小一组资源是过滤的 . 找到资源中特定的内容是找到... duh . 子集过滤可以返回任意数量的结果(甚至为0) . 定位将始终找到某个特定的实例(如果存在) . 修改过滤将返回与定位器相同的数据,但修改后(如果允许这样的修改) .
希望这有助于给人们一些尤里卡时刻,如果他们已经迷失在哪里放东西!
我看到很多REST API不能很好地处理参数 . 经常出现的一个例子是URI包括个人身份信息 .
http://software.danielwatrous.com/design-principles-for-rest-apis/
我认为一个必然的问题是参数根本不应该是一个参数,而应该转移到请求的 HEADER 或 BODY .
这是我的意见 .
查询参数用作请求的元数据 . 它们充当现有资源调用的过滤器或修改器 .
例:
/calendar/2014-08-08/events
应该提供当天的日历活动 .
如果您想要特定类别的活动
/calendar/2014-08-08/events?category=appointments
或者如果您需要超过30分钟的活动
/calendar/2014-08-08/events?duration=30
石蕊测试将检查是否仍可以在没有查询参数的情况下提供请求 .
这取决于设计 . REST over HTTP没有URI规则(主要的是它们是唯一的) . 通常它涉及品味和直觉......
我采取以下方法:
url path-element:资源及其path-element形成目录遍历和子资源(例如/ items / ,/ users / items) . 如果不确定问你的同事,他们是否认为遍历并且他们认为"another directory"最可能的路径元素是正确的选择
url参数:当没有真正的遍历时(具有多个查询参数的搜索资源是一个非常好的示例)
根据REST实现,
1) Path variables 用于对资源的直接操作,如联系人或歌曲ex ..
GET etc / api / resource / 或
GET etc / api / resource / 将返回相应的数据 .
2) Query perms/argument 用于直接资源,例如歌曲ex ..,GET / api / resource / 的元数据?metadata = genres它将返回该特定歌曲的流派数据 .
“打包”并根据universe-resource-locator提供的“上下文”对数据进行POST,这意味着定位符为#1 .
注意#2的局限性 . 我更喜欢POST到#1 .
注意:讨论了限制
POST在Is there a max size for POST parameter content?
获取Is there a limit to the length of a GET request?和Max size of URL parameters in _GET
附:这些限制是基于客户端功能(浏览器)和服务器(配置) .
作为程序员经常在客户端,我更喜欢查询参数 . 此外,对我来说,它将URL路径与参数分开,增加了清晰度,并提供了更多的可扩展性 . 它还允许我在URL / URI构建和参数构建器之间具有单独的逻辑 .
如果涉及到某种树,我确实喜欢manuel aldana关于另一种选择的说法 . 我可以看到用户特定的部件就像这样 .
如果有记录的最佳实践,我还没有找到它们 . 但是,在确定将参数放在url中的位置时,我会使用以下几条准则:
可选参数往往更容易放入查询字符串中 .
如果要在参数值与现有资源不对应时返回404错误,那么我倾向于路径段参数 . 例如
/customer/232
其中232不是有效的客户ID .但是如果你想返回一个空列表,那么当找不到参数时,我建议使用查询字符串参数 . 例如
/contacts?name=dave
如果参数影响URI空间的整个子树,则使用路径段 . 例如语言参数
/en/document/foo.txt
与/document/foo.txt?language=en
我更喜欢将唯一标识符放在路径段而不是查询参数中 .
URI的官方规则可在此RFC规范here中找到 . 还有另一个非常有用的RFC规范here,它定义了参数化URI的规则 .
没有硬性和快速的规则,但是我想要使用的纯概念立场的经验法则可以简单地总结如下:URI路径(根据定义)表示资源,查询参数本质上是该资源的修饰符 . 到目前为止,这可能没有帮助......使用REST API,您可以使用
GET
,PUT
和DELETE
来处理单个资源的主要方法 . 因此,是否应该在路径中表示某些内容或作为参数可以减少这些方法是否对所讨论的表示有意义 . 你是否合理地在这条道路上做了什么?这样做会在语义上合理吗?你当然可以在任何地方使用PUT
并弯曲后端来处理它,但你应该PUT
表示实际资源的代表而不是它的一些不必要的情境化版本 . 对于集合,可以使用POST
完成相同的操作 . 如果您想要添加到特定集合中,那么对于POST
来说有意义的URL .这仍然留下一些灰色区域,因为一些路径可能指向父资源的子女的数量,这在某种程度上是自由裁量的并且取决于他们的使用 . 这绘制的一条硬线是任何类型的传递表示都应该使用查询参数来完成,因为它没有底层资源 .
响应原始问题(Twitter的API)中给出的现实世界示例,参数表示过滤资源状态(而不是层次结构)的传递查询 . 在该特定示例中,添加到由那些约束表示的集合是完全不合理的,并且此外,该查询将不能被表示为在对象图的术语中有任何意义的路径 .
采用这种面向资源的透视图可以轻松地直接映射到域模型的对象图,并将API的逻辑驱动到一切都非常干净的地方,并且一旦进入清晰度,就会采用相当自我记录的方式 . 通过逐步使用映射到通常不合适的数据模型(即RDBMS)的传统URL路由的系统,也可以使概念更清楚 . Apache Sling肯定是一个好的起点 . 像Zope这样的系统中对象遍历调度的概念也提供了更清晰的模拟 .
我通常倾向于#2,作为查询参数(即/ api / resource?parameter = value) .
第三种选择是在body中实际发布参数=值 .
这是因为它对多参数资源更有效,并且可以扩展以供将来使用 .
无论你挑选哪一个,一定要选一个,不要混搭 . 这会导致令人困惑的API .