基于rest的搜索URL设计

我正在寻找一种合理的方法将搜索表示为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

多域的情况?

235574 次浏览

我的建议是:

/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 < / p >

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

/search/{searchQuery}

/search/{savedSearchName}
虽然我喜欢贾斯汀的回答,但我觉得它更准确地代表了一种过滤,而不是搜索。如果我想知道以小卡开头的车呢?

在我看来,你可以把它构建到你处理特定资源的方式中:
/汽车/ cam *

或者,你可以简单地将它添加到过滤器中:
/汽车/门/ 4 /名称/ cam * /颜色/红色、蓝色、绿色

就我个人而言,我更喜欢后者,但我绝不是REST方面的专家(我第一次听说REST是在两周前左右…)

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

  • URL中不允许搜索查询所需的所有字符。大多数标点符号和Unicode字符都需要URL编码为查询字符串参数。我也在努力解决同样的问题。我想在URL中使用XPath,但并非所有XPath语法都与URI路径兼容。因此,对于简单路径,/cars/doors/driver/lock/combination将适用于在司机门XML文档中定位'combination'元素。但是/car/doors[id='driver' and lock/combination='1234']并不是那么友好。

  • 根据某个属性筛选资源与指定资源是有区别的。

    例如,由于

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

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

    要还车,路径应该是

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

  • 路径中的参数更难以读取,因为名称/值对与路径的其他部分(不是名称/值对)没有隔离。

最后一个评论。我更喜欢/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)

Search资源将包含颜色、make model、车库状态等字段,可以用XML、JSON或任何其他格式指定。与Car and Garage资源一样,可以根据身份验证限制对搜索的访问。经常运行相同搜索的用户可以将它们存储在他们的配置文件中,这样就不需要重新创建。网址将足够短,在许多情况下,他们可以很容易地通过电子邮件交易。这些存储的搜索可以作为自定义RSS提要的基础,等等。

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

这个概念在Railscast中有更详细的解释。

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

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

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

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

请看REST发明者的文章

rest式的漂亮URL设计是关于显示基于结构的资源(类似目录的结构,日期: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

搜索

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

/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[,;+&],尽管我不会使用&符号,因为如果这是你的东西,第一眼从文本中无法识别

** __abc0 **

选项列表

/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

可能的功能?

< p > 否定搜索字符串(!)
搜索任何汽车,但 黑色的红色的:
?color=!black,!red
color:(!black,!red) < / p > < p > 加入搜索
搜索车库id为1 . . 20101 . . 103999 5红色的蓝色的黑色的车门为3.的汽车 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
然后可以构造更复杂的搜索查询。(在CSS3属性匹配中可以找到匹配子字符串的概念。例如,搜索包含"bar"user*=bar。)< / p >

结论

不管怎样,这对你来说可能是最重要的部分,因为你可以用你喜欢的方式来做,只要记住宁静的 URI表示一个容易理解的结构,例如目录,如/directory/file/collection/node/item,日期/articles/{year}/{month}/{day}..当你省略最后的任何部分时,你马上就知道你得到了什么。

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

  • unreserved: a-zA-Z0-9_.-~
    通常允许编码和非编码,两种使用都是等效的
  • 特殊字符:$-_.+!*'(),
  • reserved: ;/?:@=&
    ?
  • unsafe: <>"#%{}|^~[]`
    为什么不安全,为什么应该编码:RFC 1738参见2.2

更多字符类请参见RFC 1738 #第20页

< p > RFC 3986见2.2
尽管我之前说过,这里有一个常见的deliometer的区别,这意味着一些“are"比其他“are"更重要
  • 泛型delimeter: :/?#[]@
  • sub-delimeters: !$&'()*+,;=
< p > 更多阅读:
层次结构:见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长度限制。