用于search的REST式URLdevise

我正在寻找一种合理的方式来将search表示为RESTfulurl。

设置:我有两个模型,汽车和车库,汽车可以在车库。 所以我的网站看起来像:

/car/xxxx xxx == car id returns car with given id /garage/yyy yyy = garage id returns garage with given id 

汽车可以独立存在(因此/汽车),也可以存在车库。 什么是正确的方式来代表某车库中的所有车辆? 就像是:

 /garage/yyy/cars ? 

如何在车库yyy和zzz的汽车联盟?

什么是正确的方式来表示search具有某些属性的汽车? 说:给我看看所有蓝色轿车,4门:

 /car/search?color=blue&type=sedan&doors=4 

还是应该是/汽车呢?

“search”的使用似乎不合适 – 有什么更好的方式/术语? 它应该是:

 /cars/?color=blue&type=sedan&doors=4 

search参数应该是PATHINFO还是QUERYSTRING的一部分?

总之,我正在寻找一个很好的指导/教程交叉模型REST的urldevise,并进行search。

[更新]我喜欢贾斯汀的回答,但他没有涉及多领域的search案例:

 /cars/color:blue/type:sedan/doors:4 

或类似的东西。 我们如何去

 /cars/color/blue 

到多场的情况下?

为了search,使用querystrings。 这是完美的RESTful:

 /cars?color=blue&type=sedan&doors=4 

常规querystrings的一个好处是,他们是标准的,广泛的理解,他们可以从forms得到生成。

RESTful漂亮的URLdevise是基于一个结构(目录结构,date:articles / 2005/5/13,对象及其属性等)来显示资源,斜线/表示分层结构,使用-id代替。

层次结构

我会个人喜欢:

 /garage-id/cars/car-id /cars/car-id #for cars not in garages 

如果用户删除了/car-id部分,它会使cars预览 – 直观。 用户完全知道他在树中的位置,他在看什么。 他从第一眼就知道,车库和汽车是有关系的。 /car-id也表示它不属于/car/id

search

searchquery是可以的 ,只有你的偏好,应该考虑的是什么。 有趣的部分来自joinsearch(见下文)。

 /cars?color=blue;type=sedan #most prefered by me /cars;color-blue+doors-4+type-sedan #looks good when using car-id /cars?color=blue&doors=4&type=sedan #I don't recommend using &* 

或者基本上什么都不是如上所述的斜线。
公式: /cars[?;]color[=-:]blue[,;+&] ,*虽然我不会使用&符号,因为它从文本乍看起来是无法识别的。

** 你知道在URI中传递JSON对象是RESTful吗? **

选项列表

 /cars?color=black,blue,red;doors=3,5;type=sedan #most prefered by me /cars?color:black:blue:red;doors:3:5;type:sedan /cars?color(black,blue,red);doors(3,5);type(sedan) #does not look bad at all /cars?color:(black,blue,red);doors:(3,5);type:sedan #little difference 

可能的function?

取消searchstring(!)
search任何汽车,但不是 黑色红色
?color=!black,!red
color:(!black,!red)

joinsearch
在车库ID为1..20101..103999不是 5 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
然后,您可以构build更复杂的search查询。 (查看CSS3属性匹配的匹配子串的想法,例如search包含“bar” user*=bar 。)

结论

无论如何,这可能是对你来说最重要的部分,因为你可以做到这一点,不pipe你喜欢什么,只要记住, RESTful URI表示一个容易理解的结构,例如类似目录的/directory/file/collection/node/item ,date/articles/{year}/{month}/{day} ..而当你忽略任何最后一段,你立刻就知道你得到了什么。

所以..所有这些字符都可以不编码

  • 毫无保留: a-zA-Z0-9_.-~
  • 保留: ;/?:@=&$-_.+!*'(),
  • 不安全*: <>"#%{}|\^~[]`

*为什么不安全,为什么要编码: RFC 1738见2.2

RFC 3986见2.2
尽pipe我之前说过,但这是一个常见的区别,也就是说有些更重要。

  • 通用的delimeters:: :/?#[]@
  • sub-delimeters: !$&'()*+,;= ,; !$&'()*+,;=

更多阅读:
层次结构: 见2.3 , 见1.2.3
urlpath参数语法
CSS3属性匹配
IBM:REST风格的Web服务 – 基础
注意:RFC 1738由RFC 3986更新

虽然在path上有参数有一些优点,但是IMO有一些超标的因素。

  • search查询所需的所有字符都不能在url中使用。 大多数标点符号和Unicode字符需要被URL编码为查询string参数。 我正在摔跤相同的问题。 我想在URL中使用XPath,但并非所有XPath语法都与URIpath兼容。 因此,对于简单path, /cars/doors/driver/lock/combination将适合于在驱动程序的门XML文档中find“ combination ”元素。 但是/car/doors[id='driver' and lock/combination='1234']并不那么友好。

  • 根据其中一个属性过滤资源并指定资源是有区别的。

    例如,因为

    /cars/colors将返回所有车辆的所有颜色列表(返回的资源是颜色对象的集合)

    /cars/colors/red,blue,green会返回一个红色,蓝色或绿色的颜色对象列表,而不是汽车的集合。

    返回汽车,path将是

    /cars?color=red,blue,green/cars/search?color=red,blue,green

  • path中的参数更难以阅读,因为名称/值对与path的其余部分(不是名称/值对)不是隔离的。

最后一个评论。 我更喜欢/garages/yyy/cars (总是复数)to /garage/yyy/cars (也许这是原始答案中的一个错字),因为它避免了改变单数和复数之间的path。 对于添加了“s”的单词,改变并不是那么糟糕,但是改变/person/yyy/friends/people/yyy似乎很麻烦。

为了扩大彼得的答案 – 你可以使search一stream的资源:

 POST /searches # create a new search GET /searches # list all searches (admin) GET /searches/{id} # show the results of a previously-run search DELETE /searches/{id} # delete a search (admin) 

search资源将包含颜色,模型,保存状态等字段,可以用XML,JSON或其他格式指定。 像汽车和车库资源一样,您可以根据身份validation限制对search的访问。 经常运行相同search的用户可以将其存储在他们的configuration文件中,这样他们就不需要重新创build。 url很短,在很多情况下可以通过电子邮件轻松交易。 这些存储的search可以作为自定义RSS源的基础,等等。

当您将其视为资源时,使用search有很多可能性。

这个想法在Railscast中有更详细的解释。

Justin的答案可能是要走的路,尽pipe在某些应用程序中,将特定search视为资源本身是合理的,例如,如果您想要支持命名的保存search:

 /search/{searchQuery} 

要么

 /search/{savedSearchName} 

这不是REST。 您不能在API中定义资源的URI。 资源导航必须是超文本驱动的。 如果你需要漂亮的URI和大量的耦合,那就好,但是不要把它叫做REST,因为它直接违反了RESTful体系结构的限制。

请参阅REST发明人的这篇文章 。

我使用两种方法来执行search。

1)最简单的情况下,查询相关的元素,并进行导航。

  /cars?q.garage.id.eq=1 

这意味着,查询车库ID等于1的汽车。

也可以创build更复杂的search:

  /cars?q.garage.street.eq=FirstStreet&q.color.ne=red&offset=300&max=100 

FirstStreet车库中的所有车辆不是红色的(第3页,每页100个元素)。

2)复杂的查询被认为是创build并可以恢复的常规资源。

  POST /searches => Create GET /searches/1 => Recover search GET /searches/1?offset=300&max=100 => pagination in search 

用于search创build的POST正文如下所示:

  { "$class":"test.Car", "$q":{ "$eq" : { "color" : "red" }, "garage" : { "$ne" : { "street" : "FirstStreet" } } } } 

它基于Grails(标准DSL): http : //grails.org/doc/2.4.3/ref/Domain%20Classes/createCriteria.html

虽然我喜欢贾斯汀的回应,但我觉得它更准确地代表了filter而不是search。 如果我想知道名称以cam开头的汽车怎么办?

我看到它的方式,可以将其构build为处理特定资源的方式:
/汽车/ CAM *

或者,您可以简单地将其添加到filter中:
/汽车/门/ 4 /名/ CAM * /颜色/红,蓝,绿

就我个人而言,我更喜欢后者,但我绝不是REST的专家(仅在2周前才听说过…)

RESTful不build议在URL / cars / search中使用动词不安宁。 筛选/search/分页您的API的正确方法是通过查询参数。 但是,有些情况下,你必须打破规范。 例如,如果您要跨多个资源进行search,则必须使用/ search?q = query之类的内容

您可以通过http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/了解deviseRESTful API的最佳实践

另外我还build议:

 /cars/search/all{?color,model,year} /cars/search/by-parameters{?color,model,year} /cars/search/by-vendor{?vendor} 

在这里, Search被认为是Cars资源的子资源。

这里有很多很好的select。 你仍然应该考虑使用POST正文。

查询string对于您的示例来说是完美的,但是如果您有更复杂的内容,例如任意长的项目列表或布尔条件,则可能需要将post定义为文档,即客户端通过POST进行发送。

这样可以更灵活地描述search,并避免服务器URL长度限制。

我的build议是这样的:

 /garages Returns list of garages (think JSON array here) /garages/yyy Returns specific garage /garage/yyy/cars Returns list of cars in garage /garages/cars Returns list of all cars in all garages (may not be practical of course) /cars Returns list of all cars /cars/xxx Returns specific car /cars/colors Returns lists of all posible colors for cars /cars/colors/red,blue,green Returns list of cars of the specific colors (yes commas are allowed :) ) 

编辑:

 /cars/colors/red,blue,green/doors/2 Returns list of all red,blue, and green cars with 2 doors. /cars/type/hatchback,coupe/colors/red,blue,green/ Same idea as the above but a lil more intuitive. /cars/colors/red,blue,green/doors/two-door,four-door All cars that are red, blue, green and have either two or four doors. 

希望这给你的想法。 从本质上来说,您的Rest API应该很容易被发现,并且可以让您浏览您的数据。 使用URL而不是查询string的另一个优点是,您可以利用Web服务器上存在的本机caching机制来处理HTTP通信。

下面是一个描述REST查询string的恶意页面的链接: http ://web.archive.org/web/20070815111413/http://rest.blueoxen.net/cgi-bin/wiki.pl? QueryStringsConsideredHarmful

我使用了Google的caching,因为正常的页面并不适合我这里的链接: http : //rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful