通常不建议使用 POST 进行搜索操作,因为您会失去 GET 必须提供的所有优势 - 语义、幂等性、安全性(可缓存性)......
许多 RESTful 和类 REST 系统使用简单的 GET 查询,并将搜索参数作为 query 或 path 参数,以允许基于客户端和服务器的查询和结果缓存。从 HTTP 1.1 开始。除非正确指定缓存标头,否则缓存包含查询参数的 GET 请求不是问题。
但是预定义的查询有LIKE 查询的味道,你尽量避免。特别是 ElasticSearch 允许动态地向类型添加新字段。这可能会引入新的开销,以跟上添加新的预定义过滤器以支持对这些字段的查询。因此,从长远来看,根据需要动态添加查询可能是基本要求。不过,这并不难实现。
因此,包含动态添加的搜索过滤器的 GET /users/12345 查询的示例输出可能如下所示:
{
"id": "12345",
"firstName": "Max",
"lastName": "Test",
"_schema": {
"href": "http://example.com/schema/user"
}
"_links": {
"self": {
"href": "/users/12345",
"methods": ["get", "put", "delete"]
},
"curies": [{
"name": "usr",
"href": "http://example.com/docs/rels/{rel}",
"templated": true
}],
"usr:employee": {
"href": "/companies/112233",
"title": "Sample Company",
"type": "application/hal+json"
}
},
"_embedded": {
"usr:address": [
{
"_schema": {
"href": "http://example.com/schema/address"
},
"street" : "Sample Street",
"zip": "...",
"city": "...",
"state": "...",
"location": {
"longitude": "...",
"latitude": "..."
}
"_links": {
"self": {
"href": "/users/12345/address/1",
"_methods": ["get", "post", "put", "delete"],
}
}
}
],
"usr:search": {
"_schema": {
"href": "http://example.com/schema/user_search"
}
"_links": {
"self": {
"href": "/users/12345/search",
"methods: ["post", "delete"]
}
},
"filters": [
"_schema": {
"href": "http://example.com/schema/user_search_filter"
},
"_links": {
"self": {
"href": "/users/12345/search/filters",
"methods: ["get"]
},
"next": {
"href": "/users/12345/search/filters?page=2"
"methods: ["get"]
}
},
{
"byName": {
"query": {
"constant_score": {
"filter": {
"term": {
"name": {
"href": "/users/12345#name"
}
}
}
}
}
"_links": {
"self": {
"href": "/users/12345/search/filter/byName",
"methods": ["get", "put", "delete"],
"_schema": {
"href": "http://example.com/schema/search_byName"
}
"type": "application/hal+json"
}
}
}
},
{
"in20kmDistance" : {
"query": {
"filtered" : {
"query" : {
"match_all" : {}
},
"filter" : {
"geo_distance" : {
"distance" : "20km",
"Location" : {
"lat" : {
"href": "/users/12345/address/location#lat"
},
"lon" : {
"href": "/users/12345/address/location#lon"
}
}
}
}
}
}
}
"_links": {
"self": {
"href": "/users/12345/search/filter/in20kmDistance,
"methods": ["get", "put", "delete"],
"_schema": {
"href": "http://example.com/schema/search_in20kmDistance"
}
"type": "application/hal+json"
}
}
}
},
{
...
}
]
}
}
}
上面的示例代码包含一个用户表示,其中嵌入了地址和搜索过滤器,采用扩展的JSON HAL 格式。由于 RESTful 资源应尽可能一目了然,因此示例包含指向其位置和架构的链接,以便 post 和 put 操作也知道服务器可能需要哪些字段。
search 资源充当过滤器的控制器,因为它只允许一次添加新过滤器或删除所有过滤器,而通过在 /users/{userId}/search/filters?page=pageNo 上调用 GET 来遍历过滤器页面。
一个实际的过滤器现在包含要执行的实际指令 - 在这种情况下,一个 ElasticSearch 查询用户名或当前地址 20 公里距离内的所有内容 - 以及一个指向执行的实际 URI 的链接询问。请注意,ElasticSearch 代码实际上包含指向包含实际查询应使用的数据的资源的链接。当然,也可以返回包含实际用户数据的有效 ElasticSearch 查询,甚至返回 JSON Pointer,而不是数据的 URI——这又是一些实现细节。
这种方法允许在运行时动态添加新查询或更新现有查询,同时在查询时保持GET 语义不变。此外,还可以使用缓存功能,这可能会显着提高性能 - 特别是在用户数据不经常更改的情况下。
然而,这种方法的缺点是,您必须返回更多关于用户查找的数据。您还可以考虑不返回嵌入式过滤器,并让客户端显式地轮询这些过滤器。此外,当前过滤器是通过某个名称添加的,该名称充当键。在实践中,这可能会导致命名冲突。因此,最终 UUID 会更好,但如果人类必须调用这些 URI,也会带走语义,因为 byName 对人类来说肯定比 de305d54-75b4-431b-adb2-eb6b9e546014 更具语义,但这更多的是实现细节。