如何设计RESTful搜索/过滤?

时间:2011-02-16 18:45:05

标签: api search rest filter

我目前正在设计和实现PHP中的RESTful API。但是,我没有成功实现我的初始设计。

GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1

到目前为止标准相当,对吧?

我的问题在于第一个GET /users。我正在考虑在请求正文中发送参数来过滤列表。这是因为我希望能够指定复杂的过滤器,而无需获得超长URL,例如:

GET /users?parameter1=value1&parameter2=value2&parameter3=value3&parameter4=value4

相反,我希望有类似的东西:

GET /users
# Request body:
{
    "parameter1": "value1",
    "parameter2": "value2",
    "parameter3": "value3",
    "parameter4": "value4"
}

更具可读性,为您提供设置复杂过滤器的绝佳机会。

无论如何,file_get_contents('php://input')没有返回GET请求的请求正文。我也尝试了http_get_request_body(),但我正在使用的共享主机没有pecl_http。不确定它无论如何都会有所帮助。

我找到this question并意识到GET可能不应该有请求体。这有点不确定,但他们建议不要这样做。

所以现在我不知道该怎么做。如何设计RESTful搜索/过滤功能?

我想我可以使用POST,但这似乎不太RESTful。

7 个答案:

答案 0 :(得分:375)

实现RESTful搜索的最佳方法是将搜索本身视为资源。然后您可以使用POST动词,因为您正在创建搜索。您不必在数据库中逐字创建以使用POST。

例如:

Accept: application/json
Content-Type: application/json
POST http://example.com/people/searches
{
  "terms": {
    "ssn": "123456789"
  },
  "order": { ... },
  ...
}

您正在从用户的角度创建搜索。这个实现细节是无关紧要的。一些RESTful API甚至可能不需要持久性。这是一个实现细节。

答案 1 :(得分:124)

如果您在GET请求中使用请求正文,那么您将违反REST原则,因为您的GET请求将无法缓存,因为缓存系统仅使用URL。

更糟糕的是,您的网址无法加入书签,因为该网址不包含将用户重定向到此网页所需的所有信息

使用网址或查询参数代替请求正文参数。

e.g:

/myapp?var1=xxxx&var2=xxxx
/myapp;var1=xxxx/resource;var2=xxxx 

事实上,HTTP RFC 7231说:

GET请求消息中的有效负载没有定义的语义;在GET请求上发送有效负载主体可能会导致某些现有实现拒绝该请求。

有关详细信息,请查看here

答案 2 :(得分:53)

似乎可以以RESTful方式实现资源过滤/搜索。我们的想法是引入一个名为date new trials 2015-03-20 2 2015-05-23 1 /filters/的新端点。

使用此端点过滤器可视为资源,因此通过/api/filters/方法创建。这种方式 - 当然 - 身体可以用来携带所有参数,也可以创建复杂的搜索/过滤器结构。

创建此类过滤器后,有两种方法可以获得搜索/过滤结果。

  1. 将返回具有唯一ID的新资源以及POST状态代码。然后,使用此ID可以向201 Created发出GET请求,例如:

    /api/users/
  2. 通过GET /api/users/?filterId=1234-abcd 创建新过滤器后,它不会回复POST,而是201 Created一起回复,303 SeeOther标题指向{{1} }}。此重定向将通过底层库自动处理。

  3. 在这两种情况下,需要进行两次请求以获取过滤结果 - 这可能被视为一个缺点,尤其是对于移动应用程序。对于移动应用,我会使用Location /api/users/?filterId=1234-abcdPOST

    如何保留已创建的过滤器?

    它们可以存储在数据库中,以后再使用。它们也可以存储在一些临时存储器中,例如redis并有一些TTL,然后它们将过期并将被移除。

    这个想法有什么好处?

    过滤器,过滤后的结果可以缓存,甚至可以加入书签。

答案 3 :(得分:14)

我认为您应该使用请求参数,但只要没有合适的HTTP标头来完成您想要的操作。 HTTP specification没有明确说明,GET不能拥有正文。但是this paper声明:

  

按照惯例,当GET方法是   使用,所需的所有信息   识别资源是否被编码   URI。没有惯例   HTTP / 1.1用于安全交互(例如,   检索)客户提供的地方   数据到HTTP实体中的服务器   身体而不是在查询部分   一个URI。这意味着安全   操作,URI可能很长。

答案 4 :(得分:10)

如果您的初始API完全是REST的(特别是当您处于alpha阶段时),请不要担心太多。让后端管道首先工作。您可以随时进行某种URL转换/重写以映射事物,迭代完善,直到您获得足够稳定的广泛测试(“beta”)。

您可以定义URI,其参数按URI上的位置和约定进行编码,前缀为您知道始终映射到某个内容的路径。我不知道PHP,但我认为这样的工具存在(因为它存在于其他语言的Web框架中):

.IE。对商店#1的i = 1..4使用param [i] = value [i]执行“用户”类型的搜索(使用value1,value2,value3,...作为URI查询参数的简写):< / p>

1) GET /store1/search/user/value1,value2,value3,value4

2) GET /store1/search/user,value1,value2,value3,value4

或如下(虽然我不推荐它,稍后会详细介绍)

3) GET /search/store1,user,value1,value2,value3,value4

使用选项1,您将所有以/store1/search/user为前缀的URI映射到搜索处理程序(或任何PHP指定)默认搜索store1下的资源(相当于/search?location=store1&type=user

根据API记录和强制执行的约定,参数值1到4用逗号分隔并按顺序显示。

选项2将搜索类型(在本例中为user)添加为位置参数#1。这两种选择都只是一种美容选择。

选项3也是可能的,但我不认为我会喜欢它。我认为在某些资源中搜索的能力应该在搜索本身之前的URI本身中呈现(好像在URI中清楚地表明搜索在资源中是特定的。)

这优于URI上传递参数的优点是搜索是URI的一部分(因此将搜索视为资源,其内容可以 - 并且将随时间变化的资源。)缺点是参数订单是强制性的。

一旦你做了这样的事情,你可以使用GET,它将是一个只读资源(因为你不能POST或PUT到它 - 它会在GET'时得到更新)。它也是一种资源,只有在被调用时才会存在。

还可以通过将结果缓存一段时间或使用DELETE来删除缓存,从而为其添加更多语义。但是,这可能与人们通常使用DELETE的方式背道而驰(因为人们通常使用缓存头来控制缓存。)

你如何去做这将是一个设计决定,但这将是我的方式。它并不完美,我相信会有这样的情况,这样做不是最好的事情(特别是对于非常复杂的搜索标准)。

答案 5 :(得分:8)

当我使用laravel/php后端时,我倾向于使用这样的东西:

/resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource

PHP自动将[]参数转换为数组,所以在这个例子中我最终会得到一个$filter变量,它包含一个过滤器的数组/对象,以及一个页面和任何相关的资源我想急切加载。

如果您使用其他语言,这可能仍然是一个很好的约定,您可以创建一个解析器来将[]转换为数组。

答案 6 :(得分:2)

仅供参考:我知道这有点晚了但是对于任何有兴趣的人来说。 取决于你想要的RESTful,你必须实现自己的过滤策略,因为HTTP规范在这方面不是很清楚。我想建议对所有过滤器参数进行url编码,例如

std::variant<>

我知道它很难看,但我认为这是最RESTful的方式,应该很容易在服务器端进行解析:)