做复杂的RESTful搜索方法的正确方法是什么?


44

遵循REST原则,我想为我的API创建一个GET方法,该方法使用一些条件进行搜索并将结果返回给客户端。问题是标准最多可以包含14个参数,其中一个是复杂对象列表,因此...

  • 我什至不知道是否可以将这些复杂的对象与url参数进行编码/解码。

  • 我没有计算网址可以获得多长时间,但是我确定它会足够大,甚至可能达到网址长度限制?

同样,搜索应该以“实时”方式显示结果,我的意思是,每当用户从搜索表单中更改某些内容时,他应该能够看到新结果而无需按下任何“搜索”按钮。

您能否向我澄清这些要点,对于创建具有很多参数的静态搜索方法有何建议?


3
顺便说一句(我注意到在撰写本文时,这两个答案都没有提到实时部分),实时搜索通常应该只是一遍又一遍地发送更新的请求。例如,当你输入,你会喜欢发送请求的东西search?q=tsearch?q=tesearch?q=test,等等。考虑限制查询的发送频率,以免损害服务器。您也可以选择返回大量信息,并在客户端进行过滤。如果用户输入可以将范围缩小很多的广泛类别,则效果很好。

2
无论解决方案如何,请确保以技术中立的格式交换数据。因此,请勿使用内部表示,否则将很难为您的API编写客户端。
Kwebble

根据您的需求,该库可以完成工作:github.com/jirutka/rsql-parser。如果您使用JPA,则它具有JPA扩展名,或者您可以编写自己的访问者以映射到搜索引擎的API。您可以添加自己的运算符。
Walfrat

Answers:


54

在您阅读我的答案之前,我想说我同意@Neil。我们必须选择战斗。我们通常想尽力而为,但有时讨论的空间太小,我们不得不违背自己的意愿做出决定。

无论如何,在尼尔的回答中,我又想念一件事。文件资料。只是为了确保开发人员知道POST请求/search是安全的。

那就是。

1.有机会

GET首先考虑该选项。查看此问题网址的最大长度。评估最长的查询字符串是否超过2000个字符。如果不是,并且您不希望如此,请使用GET。这看起来很丑陋,但是至少您可以为URL添加书签,当然,它具有从该方法的语义(幂等,安全和缓存)中获得的所有优点。

1.1尝试编码查询字符串

例如,在base 64中。甚至javascript也支持base 64编码

这是这样的:

  1. 使用所有过滤器构建JSON并将其标准化。
  2. 解析成字符串
  3. 编码
  4. 发送编码的JSON作为请求参数(/search?q=SGVsbG8gV29ybGQh....)。
  5. 在服务器端,解码参数q
  6. 反序列化JSON字符串

以前,请使尽可能长的JSON字符串,对其进行编码并取其长度。评估编码的字符串是否适合URL。我在Fiddle.js上实现了以下代码段,供您测试。(我希望它仍然有效)1

Base 64编码是确定性和可逆的,因此不会发生冲突。

使用编码查询,我们还可以将搜索保存在数据库中,为URL加上书签,共享链接等。当然,我们不必转义/取消转义字符串。

1.2尝试使用别名

在阅读有关如何设计REST API的博客时,我想起了另一种选择。常见查询的别名

我觉得这些很有趣,原因如下

  • 缩短查询字符串的长度。它使API更加干净和用户友好

    GET / tickets /?status = closed&closedAt = xxx vs GET / tickets / recently-closed /

  • 可与更多别名或更多请求参数结合使用。

    GET / tickets /?status = closed&closedAt = xxx&within = 30min vs GET / tickets / recents -closed /?within = 30min

  • 我们可以将别名与编码的查询字符串结合使用

    GET / tickets /?status = closed&closedAt = xxx&within = 30min vs GET / tickets / recently-closed /?q = SGVsbG8g ...


1:我使用了JSON,但是我们可以在服务器端反序列化它之后就可以使用其他格式。


2
这既实用又正确。还值得注意的是,大多数编程语言使将哈希值转换为查询字符串变得微不足道,因此从GET操作开始非常容易。
阿兰·哈达德

1
我喜欢Springstackoverflow.com/questions/16942193/…我不敢相信它在第一次尝试:D时会起作用。关于url长度,小于1k,尽管我们仍然需要迭代规范。
anat0lius

然后,使用GET。为简单起见。使用Spring MVC,您可以使用GET实现完全相同的映射。寻找Spring的WebArgumentResolver ;-)
Laiv

Base64使有效负载大小增加约4/3。尽管urlencoding可以使特殊字符为3/1,但使用大多数安全字符的查询将保持相​​同的大小。还有其他使用base64的理由吗?
villasv

并不是的。我只是不喜欢(取消)转义网址。有效负载的过剩是这里的权衡。它仍然必须符合每个请求的GET最大大小。这就是为什么我创建了代码段。供用户尝试。当我写下答案时,我将Web语义放在实现细节之上。答案的重点是“继续尝试GET”。找到您的方式或使用我与您分享的任何方式。
莱夫

13

如果您只有锤子,那么一切看起来都像钉子。看来这里的问题是您正在尝试将搜索页面变成RESTful页面,而这似乎并不是RESTful设计要解决的常见模式。

只需执行一个带有用户提供的参数的POST请求,即可从后端获取您所需的信息。我认为除了执行搜索外,您不需要执行其他任何操作,因此您没有必要在整个页面中插入。只需添加/搜索到你的URL的末尾,这样你就不会冒险碰上与你/用户分页冲突这是REST风格。


2
@LiLou_:对于该需求,只有两种现实的可能性:1.将所有数据读取到您的前端并在那里进行过滤。这在所需的内存量中可能是禁止的。2.对搜索条件的每次更改都向服务器发出新请求。这是POST还是GET请求都没有关系,但是所涉及的网络延迟可能会破坏用户对“实时”更新的感知。
巴特·范·英根·谢瑙

2
我不同意,POST在语义上意味着其他含义。我建议现在使用GET并在参数中传递所有过滤器数据,如果参数太多,则在应用程序级别是错误的。
CodeYogi

2
@CodeYogi有时客户没有给您讨论的空间。我已经实现了40到50列的类似Excel的视图页面,每个页面都有自己的过滤器。当然可以排序。无论如何,使用GET还是有可能的,但看起来似乎不太时尚
Laiv

1
在这种情况下,@ Laiv需要进行认真的讨论,因为POST是用于更改服务器状态的。这样的用例并不罕见,因此应该小心处理。
CodeYogi

2
在这些情况下,必须提供文档。我已经与客户就他们的应用程序的可用性进行了认真的讨论。后来我被证明是正确的,因为最终用户同意了我的看法。但是,有时您必须选择战斗。
Laiv

0

它完全取决于您的API模型是什么:无或为动词。

如果API为None,则您可能希望获取对象列表,如下所示:

GET: /api/v1/objects

在这种情况下,您必须将数据作为请求参数发送。因此,您必须将参数描述为键值的平面列表:

GET: /api/v1/objects

key1 : val1
key2.key1 : val 21
key2.key2 : val 22
....

一些平台支持自定义参数解析器(例如Spring MVC),您可以将参数转换为对象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.