我正在寻找一种合理的方法将搜索表示为RESTful url。

设置:我有两个模型,汽车和车库,汽车可以在车库。我的url是这样的:

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

/garage/yyy
  yyy = garage id
  returns garage with given id

Car可以独立存在(因此有/ Car),也可以存在于车库中。如何正确地表示给定车库中的所有汽车?喜欢的东西:

/garage/yyy/cars     ?

车库里的车yyy和zzz的组合呢?

用什么正确的方式来表示搜索具有特定属性的汽车?说:给我看所有四门的蓝色轿车。

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

或者应该改为/cars ?

在这里使用“搜索”似乎不合适——还有什么更好的方式/术语吗?它应该是:

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

搜索参数应该是PATHINFO或QUERYSTRING的一部分吗?

简而言之,我正在寻找跨模型REST url设计和搜索的指导。

[更新]我喜欢贾斯汀的答案,但他没有涵盖多领域搜索的情况:

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

或者类似的东西。我们如何从

/cars/color/blue

多域的情况?


我的建议是:

/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而不是查询字符串的另一个优点是,您可以利用web服务器上存在的用于HTTP流量的本机缓存机制。

这里有一个页面链接,该页面描述了REST中查询字符串的害处:http://web.archive.org/web/20070815111413/http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful

我使用谷歌的缓存,因为正常的页面不为我工作,这里的链接以及: http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful


Justin的答案可能是正确的,尽管在某些应用程序中,将特定的搜索视为其本身的资源可能是有意义的,比如如果你想支持命名保存的搜索:

/search/{searchQuery}

or

/search/{savedSearchName}

虽然我喜欢贾斯汀的回答,但我觉得它更准确地代表了一种过滤,而不是一种搜索。如果我想知道以小卡开头的车呢? 在我看来,你可以把它构建到你处理特定资源的方式中: /汽车/ cam * 或者,你可以简单地将它添加到过滤器中: /汽车/门/ 4 /名称/ cam * /颜色/红色、蓝色、绿色 就我个人而言,我更喜欢后者,但我绝不是REST方面的专家(我第一次听说REST只是在两周前左右……)


虽然在路径中设置参数有一些优势,但在我看来,还有一些重要因素。

Not all characters needed for a search query are permitted in a URL. Most punctuation and Unicode characters would need to be URL encoded as a query string parameter. I'm wrestling with the same problem. I would like to use XPath in the URL, but not all XPath syntax is compatible with a URI path. So for simple paths, /cars/doors/driver/lock/combination would be appropriate to locate the 'combination' element in the driver's door XML document. But /car/doors[id='driver' and lock/combination='1234'] is not so friendly. There is a difference between filtering a resource based on one of its attributes and specifying a resource. For example, since /cars/colors returns a list of all colors for all cars (the resource returned is a collection of color objects) /cars/colors/red,blue,green would return a list of color objects that are red, blue or green, not a collection of cars. To return cars, the path would be /cars?color=red,blue,green or /cars/search?color=red,blue,green Parameters in the path are more difficult to read because name/value pairs are not isolated from the rest of the path, which is not name/value pairs.

最后一个评论。我更喜欢/garages/yyy/cars(总是复数)而不是/garage/yyy/cars(也许这是原始答案中的一个拼写错误),因为它避免了在单数和复数之间改变路径。对于加了's'的单词,变化不是很糟糕,但将/person/yyy/friends改为/people/yyy似乎很麻烦。


扩展Peter的回答——你可以让搜索成为一流的资源:

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)

The Search resource would have fields for color, make model, garaged status, etc and could be specified in XML, JSON, or any other format. Like the Car and Garage resource, you could restrict access to Searches based on authentication. Users who frequently run the same Searches can store them in their profiles so that they don't need to be re-created. The URLs will be short enough that in many cases they can be easily traded via email. These stored Searches can be the basis of custom RSS feeds, and so on.

当您将搜索视为资源时,有许多使用搜索的可能性。

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


对于搜索,使用querystrings。这是完美的RESTful:

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

常规查询字符串的一个优点是它们是标准的、被广泛理解的,并且它们可以从form-get生成。


这不是REST。您不能为API内的资源定义uri。资源导航必须是超文本驱动的。如果您想要漂亮的uri和大量的耦合,这是可以的,但是不要将其称为REST,因为它直接违反了RESTful体系结构的约束。

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


RESTful pretty URL设计是关于基于结构(类似目录的结构,日期:articles/2005/5/13,对象及其属性,..)显示资源,斜杠/表示层次结构,使用-id代替。

层次结构

我个人更喜欢:

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

如果用户删除了/car-id部分,它将带来直观的汽车预览。用户确切地知道他在树的什么位置,他在看什么。他一看就知道车库和汽车是有关系的。与/car/id不同,/car-id也表示它属于一起。

搜索

搜索查询是可以的,因为它是,只有你的偏好,应该考虑什么。有趣的部分出现在加入搜索时(见下文)。

/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   #also possible, but & blends in with text

或者基本上任何不是斜杠的东西,如上所述。 公式:/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

可能的功能?

否定搜索字符串(!) 搜查所有车,但不包括黑车和红车 ?颜色=黑色!红色的 颜色:(黑色!红色)

加入搜索 在id 1的车库中搜索红色、蓝色或黑色的3门汽车。20或101..103或999,而不是5 /车库[id = 1 - 20101 - 103999, ! 5] /汽车[=红色、蓝色、黑色,门= 3) 然后可以构造更复杂的搜索查询。(查看CSS3属性匹配,了解匹配子字符串的思想。例如,搜索包含“bar”user*=bar的用户。)

结论

不管怎样,这对您来说可能是最重要的部分,因为您可以按照自己的想法来做,只要记住RESTful URI表示一个容易理解的结构,例如类似目录的/目录/文件、/集合/节点/项、日期/文章/{年}/{月}/{日}..当你省略最后的任何部分时,你马上就知道你得到了什么。

所以. .,所有这些字符都允许未编码:

无限制的:a-zA-Z0-9_. - ~ 通常允许编码和不允许,这两种用法是等效的。 特殊字符:$-_.+!*'(), 保留:;/ ?: @ = & 可以按照它们所代表的目的未编码地使用,否则必须对它们进行编码。 不安全的 : <>"#%{}|^~[]` 为什么不安全,为什么应该编码:RFC 1738见2.2

更多字符类请参见RFC 1738#page-20。

RFC 3986见2.2 尽管我之前说过,这里有一个常见的delimeter的区别,这意味着一些“比”其他更重要。

发电机:[] @ sub-delimeters : !$&'()*+,;=

更多阅读: 层次结构:见2.3,见1.2.3 Url路径参数语法 CSS3属性匹配 IBM: rest式Web服务——基础知识 注:RFC 1738由RFC 3986更新


RESTful不建议在URL的/cars/search中使用动词。过滤/搜索/分页API的正确方法是通过查询参数。然而,在某些情况下,你不得不打破常规。例如,如果您正在跨多个资源进行搜索,那么您必须使用类似/search?q =查询

您可以访问http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/了解设计RESTful API的最佳实践


我使用两种方法来实现搜索。

1)最简单的情况,用于查询相关元素,并用于导航。

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

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

也可以创建更复杂的搜索:

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

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

2)复杂查询被认为是常规资源,可以被创建和恢复。

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

创建搜索的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


此外,我还建议:

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

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


对于你的情况,这里有很多好的选择。你仍然应该考虑使用POST主体。

查询字符串非常适合您的示例,但如果您有更复杂的东西,例如任意长的项目列表或布尔条件,您可能想要将post定义为一个文档,客户端通过post发送。

这允许更灵活的搜索描述,以及避免服务器URL长度限制。