460

我正在寻找一种将搜索表示为 RESTful 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 的汽车联盟怎么样?

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

/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

到多场案例?

4

12 回答 12

467

对于搜索,请使用查询字符串。这是完全 RESTful 的:

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

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

于 2009-07-04T07:13:55.980 回答
136

RESTful 漂亮的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.

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

/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

##可能的功能?## 否定搜索字符串(!)
搜索任何汽车,但不是 黑色红色
?color=!black,!red
color:(!black,!red)

联合搜索在 id 1..20101..103999不是5
的车库中 搜索具有3 个门的红色蓝色黑色汽车 然后您可以构建更复杂的搜索查询。(查看CSS3 属性匹配以了解匹配子字符串的想法。例如搜索包含 "bar" 的用户。) /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
user*=bar

#结论#无论如何,这对你来说可能是最重要的部分,因为你可以随心所欲地做,只要记住RESTful URI 代表一种易于理解的结构,例如目录/directory/file,,,/collection/node/item日期/articles/{year}/{month}/{day}..和当你省略任何最后一段时,你会立即知道你得到了什么。

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

  • unreserved:a-zA-Z0-9_.-~
    通常允许编码和不编码,两种用途是等效的。
  • 特殊的角色:$-_.+!*'(),
  • 保留:;/?:@=&
    可以未编码用于它们所代表的目的,否则必须对其进行编码。
  • 不安全:<>"#%{}|^~[]`
    为什么不安全以及为什么要编码:RFC 1738 见 2.2

另请参阅RFC 1738#page-20了解更多字符类。

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

  • 通用分隔符::/?#[]@
  • 分界线:!$&'()*+,;=

更多阅读:
层次结构:见 2.3见 1.2.3
url 路径参数语法
CSS3 属性匹配
IBM:RESTful Web 服务 - 基础
注:RFC 1738 由 RFC 3986 更新

于 2013-03-15T13:10:17.863 回答
37

尽管在路径中包含参数有一些优势,但在 IMO 中,还有一些重要的因素。

  • 并非搜索查询所需的所有字符都允许在 URL 中使用。大多数标点符号和 Unicode 字符都需要作为查询字符串参数进行 URL 编码。我正在努力解决同样的问题。我想在 URL 中使用 XPath,但并非所有 XPath 语法都与 URI 路径兼容。因此,对于简单的路径,在驾驶员车门 XML 文档中/cars/doors/driver/lock/combination定位 ' ' 元素是合适的。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/friendsto就/people/yyy显得麻烦了。

于 2009-05-29T15:48:13.683 回答
36

要扩展彼得的答案-您可以使 Search 成为一流的资源:

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)

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

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

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

于 2009-05-29T16:03:41.923 回答
11

贾斯汀的答案可能是要走的路,尽管在某些应用程序中,将特定搜索本身视为资源可能是有意义的,例如,如果您想支持命名保存的搜索:

/search/{searchQuery}

或者

/search/{savedSearchName}
于 2008-10-16T10:52:48.443 回答
8

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

1) 最简单的情况,查询关联元素和导航。

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

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

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

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

FirstStreet 中所有车库中非红色的汽车(第 3 页,每页 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

于 2014-10-10T14:23:06.557 回答
4

这不是 REST。您不能在 API 中为资源定义 URI。资源导航必须是超文本驱动的。如果你想要漂亮的 URI 和大量的耦合,那很好,但不要称之为 REST,因为它直接违反了 RESTful 架构的约束。

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

于 2009-07-20T18:52:20.833 回答
3

此外,我还建议:

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

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

于 2016-01-12T21:49:04.787 回答
3

对于您的情况,这里有很多不错的选择。您仍然应该考虑使用 POST 正文。

查询字符串非常适合您的示例,但如果您有更复杂的内容,例如任意长的项目列表或布尔条件,您可能希望将帖子定义为客户端通过 POST 发送的文档。

这允许对搜索进行更灵活的描述,并避免服务器 URL 长度限制。

于 2017-09-15T14:45:36.833 回答
2

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

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

于 2014-10-06T06:53:25.610 回答
1

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

在我看来,您可以将其构建到处理特定资源的方式中:
/cars/cam*

或者,您可以简单地将其添加到过滤器中:
/cars/doors/4/name/cam*/colors/red, blue,green

就我个人而言,我更喜欢后者,但我绝不是 REST 专家(大约 2 周前才第一次听说它......)

于 2009-03-04T15:02:54.450 回答
-4

我的建议是:

/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

于 2008-10-16T05:03:22.783 回答