185

我需要设计一个 RESTful 查询 API,它根据几个过滤器返回一组对象。通常的 HTTP 方法是 GET。唯一的问题是,它至少可以有十几个过滤器,如果我们将所有这些过滤器作为查询参数传递,URL 可能会变得很长(长到足以被某些防火墙阻止)。

减少参数的数量不是一种选择。

我能想到的一种替代方法是在 URI 上使用 POST 方法并将过滤器作为 POST 正文的一部分发送。这是否反对 RESTfull(发出 POST 调用以查询数据)。

谁有更好的设计建议?

4

4 回答 4

179

请记住,对于 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,除了实体不是书籍 - 请求实体是书籍搜索条件,响应实体是书籍搜索结果。

于 2015-08-13T09:32:04.517 回答
90

很多人已经接受这样的做法,即查询字符串过长或过于复杂(例如查询字符串不能轻松处理嵌套数据)的 GET 可以作为 POST 发送,复杂/长数据在正文中表示的请求。

在 HTTP 规范中查找 POST 规范。它非常广泛。(如果你想通过 REST 中的漏洞驾驶一艘战舰……使用 POST。)

你失去了 GET 语义的一些好处......比如自动重试,因为 GET 是幂等的,但如果你能忍受它,那么接受使用 POST 处理非常长或复杂的查询可能会更容易。

(哈哈,题外话……我最近发现,根据 HTTP 规范,GET可以包含一个文档正文。有一节说,解释说,“任何请求都可以有一个文档正文,除了本节中列出的那些”……并且它所指的部分没有列出任何内容。我搜索并找到了HTTP作者正在谈论的线程,这是故意的,因此路由器等不必区分不同的消息。但是,在练习很多基础设施确实会丢弃 GET 的主体。因此,您可以使用主体中表示的过滤器进行 GET,例如 POST,但您将掷骰子。)

于 2013-01-10T09:02:55.533 回答
10

简而言之:进行 POST 但使用X-HTTP-Method-Override标头覆盖 HTTP 方法。

真实要求

邮寄/书籍

实体

{ "title": "Ipsum", "year": 2017 }

标头

X-HTTP 方法覆盖:GET

在服务器端,检查头 X-HTTP-Method-Override 是否存在,然后将其值作为构建到后端最终端点的路由的方法。此外,将实体主体作为查询字符串。从后端的角度来看,请求变成了一个简单的 GET。

通过这种方式,您可以使设计与 REST 原则保持一致。

编辑:我知道这个解决方案最初是为了解决某些浏览器和服务器中的 PATCH 动词问题,但在问题中描述的问题很长的 URL 的情况下,它也适用于 GET 动词。

于 2017-05-04T23:32:58.940 回答
-11

如果您使用 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。

于 2018-03-19T17:05:40.137 回答