我需要设计一个 RESTful 查询 API,它根据几个过滤器返回一组对象。通常的 HTTP 方法是 GET。唯一的问题是,它至少可以有十几个过滤器,如果我们将所有这些过滤器作为查询参数传递,URL 可能会变得很长(长到足以被某些防火墙阻止)。
减少参数的数量不是一种选择。
我能想到的一种替代方法是在 URI 上使用 POST 方法并将过滤器作为 POST 正文的一部分发送。这是否反对 RESTfull(发出 POST 调用以查询数据)。
谁有更好的设计建议?
我需要设计一个 RESTful 查询 API,它根据几个过滤器返回一组对象。通常的 HTTP 方法是 GET。唯一的问题是,它至少可以有十几个过滤器,如果我们将所有这些过滤器作为查询参数传递,URL 可能会变得很长(长到足以被某些防火墙阻止)。
减少参数的数量不是一种选择。
我能想到的一种替代方法是在 URI 上使用 POST 方法并将过滤器作为 POST 正文的一部分发送。这是否反对 RESTfull(发出 POST 调用以查询数据)。
谁有更好的设计建议?
请记住,对于 REST API,这完全取决于您的观点。
REST API 中的两个关键概念是端点和资源(实体)。简单地说,端点要么通过 GET 返回资源,要么通过 POST 和 PUT 等(或以上的组合)接受资源。
可以接受的是,使用 POST,您发送的数据可能会或可能不会导致创建新资源及其关联的端点,这些端点很可能不会在 POSTed url 下“存活”。换句话说,当您发布时,您将数据发送到某处进行处理。POST 端点不是通常可以找到资源的位置。
引用RFC 2616(省略不相关部分,突出显示相关部分):
9.5 发布
POST 方法用于请求源服务器接受请求中包含的实体作为请求行中的请求URI 标识的资源的新下级。POST 旨在允许统一的方法涵盖以下功能:
- ...
- 向数据处理过程提供数据块,例如提交表单的结果;
- ...
...
POST 方法执行的操作可能不会产生可由 URI 标识的资源。在这种情况下,200 (OK) 或 204 (No Content) 是适当的响应状态,具体取决于响应是否包含描述结果的实体。
如果在源服务器上创建了资源,则响应应该是 201 (Created)...
我们已经习惯了代表“事物”或“数据”的端点和资源,无论是用户、消息还是书籍——无论问题领域如何规定。但是,端点也可以公开不同的资源——例如搜索结果。
考虑以下示例:
GET /books?author=AUTHOR
POST /books
PUT /books/ID
DELETE /books/ID
这是一个典型的 REST CRUD。但是,如果我们添加:
POST /books/search
{
"keywords": "...",
"yearRange": {"from": 1945, "to": 2003},
"genre": "..."
}
这个端点没有什么是非 RESTful 的。它接受请求正文形式的数据(实体)。该数据是搜索标准- 与其他任何数据一样的 DTO。此端点生成资源(实体)以响应请求:搜索结果。搜索结果资源是一个临时资源,立即提供给客户端,无需重定向,也不会从其他一些规范 url 中暴露出来。
它仍然是 REST,除了实体不是书籍 - 请求实体是书籍搜索条件,响应实体是书籍搜索结果。
很多人已经接受这样的做法,即查询字符串过长或过于复杂(例如查询字符串不能轻松处理嵌套数据)的 GET 可以作为 POST 发送,复杂/长数据在正文中表示的请求。
在 HTTP 规范中查找 POST 规范。它非常广泛。(如果你想通过 REST 中的漏洞驾驶一艘战舰……使用 POST。)
你失去了 GET 语义的一些好处......比如自动重试,因为 GET 是幂等的,但如果你能忍受它,那么接受使用 POST 处理非常长或复杂的查询可能会更容易。
(哈哈,题外话……我最近发现,根据 HTTP 规范,GET可以包含一个文档正文。有一节说,解释说,“任何请求都可以有一个文档正文,除了本节中列出的那些”……并且它所指的部分没有列出任何内容。我搜索并找到了HTTP作者正在谈论的线程,这是故意的,因此路由器等不必区分不同的消息。但是,在练习很多基础设施确实会丢弃 GET 的主体。因此,您可以使用主体中表示的过滤器进行 GET,例如 POST,但您将掷骰子。)
简而言之:进行 POST 但使用X-HTTP-Method-Override标头覆盖 HTTP 方法。
真实要求
邮寄/书籍
实体
{ "title": "Ipsum", "year": 2017 }
标头
X-HTTP 方法覆盖:GET
在服务器端,检查头 X-HTTP-Method-Override 是否存在,然后将其值作为构建到后端最终端点的路由的方法。此外,将实体主体作为查询字符串。从后端的角度来看,请求变成了一个简单的 GET。
通过这种方式,您可以使设计与 REST 原则保持一致。
编辑:我知道这个解决方案最初是为了解决某些浏览器和服务器中的 PATCH 动词问题,但在问题中描述的问题很长的 URL 的情况下,它也适用于 GET 动词。
如果您使用 Java 和 JAX-RS 进行开发,我建议您使用 @QueryParam 和 @GET
当我需要查看列表时,我也有同样的问题。
参见示例:
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
@Path("/poc")
public class UserService {
@GET
@Path("/test/")
@Produces(MediaType.APPLICATION_JSON)
public Response test(@QueryParam("code") final List<Integer> code) {
Integer int0 = codigo.get(0);
Integer int1 = codigo.get(1);
return Response.ok(new JSONObject().put("int01", int0)).build();
}
}
URI 模式: “poc/test?code=1&code=2&code=3
@QueryParam会自动将查询参数“orderBy=age&orderBy=name”转换为 java.util.List。