设计具有大量查询参数的RESTful查询API

204

我需要设计一个RESTful查询API,基于少量过滤器返回一组对象。通常用于此的HTTP方法是GET。唯一的问题是,它可能有至少十几个过滤器,如果我们将它们全部作为查询参数传递,URL就会变得相当长(足以被某些防火墙屏蔽)。

减少参数数量不是一个选项。

我能想到的一个替代方法是在URI上使用POST方法,并将过滤器作为POST正文的一部分发送。这是否违背RESTful的特性(通过POST调用来查询数据)。

有没有人有更好的设计建议?


2
使用短参数名(如1个字符等)吗? - Madbreaks
4
可能并不是真正符合RESTful标准,但在处理GET和POST时需要实用主义。如果你有太多要发送的变量而无法减少它们,那么就使用POST方法。我不喜欢过度将参数塞进URL里,但这只是我的个人喜好。 - Doug Dawson
3
谢谢。虽然这个问题已经关闭,但这确实是我需要答案的问题。我很高兴你提出了这个问题。 - Casey Crookston
4个回答

209

记住,在REST API中,一切都取决于你的观点。

REST API中的两个关键概念是端点和资源(实体)。粗略地说,一个端点通过GET返回资源,或者通过POST和PUT接受资源等操作(或上述操作的组合)。

人们普遍认为,使用POST时,您发送的数据可能会导致新资源及其相关端点的创建,这些资源很可能不会“存在于” POST的URL下。换句话说,当您提交POST请求时,您在某个地方发送数据进行处理。POST端点并不是资源通常被找到的位置。

引用RFC 2616 (省略无关部分,突出显示相关部分):

9.5 POST

使用POST方法请求源服务器接受请求行中Request-URI标识的资源的实体作为新的子资源。POST旨在允许统一的方法涵盖以下功能:

  • ...
  • 向数据处理过程提供块数据,例如提交表单的结果;
  • ...

...

POST方法执行的操作可能不会导致可以通过URI标识的资源。在这种情况下,200(OK)或204(无内容)是适当的响应状态,这取决于响应是否包括描述结果的实体

如果在源服务器上创建了资源,则响应应为201(已创建)...

我们已经习惯了端点和资源表示“事物”或“数据”,无论是用户、消息、书籍,还是任何问题域。但是,一个端点也可以公开不同的资源,例如搜索结果。

考虑以下示例:

GET    /books?author=AUTHOR
POST   /books
PUT    /books/ID
DELETE /books/ID

这是一个典型的REST CRUD。但是,如果我们加入以下内容会怎样:

POST /books/search

    {
        "keywords": "...",
        "yearRange": {"from": 1945, "to": 2003},
        "genre": "..."
    }

这个端点没有任何不符合REST的地方。它接受请求体中以数据实体的形式出现的数据(实体)。这些数据就是搜索条件——像其他DTO一样。该端点根据请求产生响应的资源(实体):搜索结果。 搜索结果资源是一个临时的资源,立即由客户端提供,无需重定向,并且不会从其他规范url中公开。

它仍然是REST,只是实体不是书籍——请求实体是书籍搜索条件,响应实体是书籍搜索结果。


你能为DTO提供一些类命名规范的建议吗? - Kwadz
个人而言,我会选择BooksSearchCriteriaDTOBooksSearchResultsDTO - Amir Abiri
对于POST /books/search这种情况,最好的HTTP响应代码是什么?201仍然适用吗? - L. Holanda
12
201 是相反的,它意味着已经创建了一个资源。这个资源预计会在某个地方拥有自己独特的URI。当 POST 用于 CRUD 的 C 部分时,201 是合适的。我建议使用普通的200,或者在搜索结果为空时选择204。 - Amir Abiri
1
请注意,现在理论上来说GET也完全可以胜任这项任务 - “RFC2616被引用为“HTTP / 1.1规范”,现已过时。在2014年,它被RFC7230-7237所取代。引用的“处理请求时应忽略消息正文”已被删除。现在只是“请求消息框架独立于方法语义,即使该方法不定义消息正文的使用”。第二个引用“GET方法表示检索由Request-URI标识的任何信息...”已被删除。”- 来自https://dev59.com/53NA5IYBdhLWcg3wZ85S - ejaksla

92
很多人已经接受这种做法,即可以将具有太长或太复杂查询字符串的GET请求(例如,查询字符串不易处理嵌套数据)作为POST请求发送,其中复杂/长数据表示在请求的正文中。
查找HTTP规范中POST的规范。它非常广泛。(如果您想通过REST中的漏洞操纵战舰...请使用POST。)
您失去了一些GET语义的好处...例如自动重试,因为GET是幂等的,但是如果您可以忍受,使用POST处理非常长或复杂的查询可能更容易。
(哈哈大篇幅离题...我最近发现,根据HTTP规范,GET可以包含文档正文。有一节说:“除本节所列举的请求外,任何请求都可以有一个文档正文”......而它所指的那一节没有列出任何请求。我搜索并找到了一个HTTP作者正在讨论此事的帖子,这是有意为之,以便路由器和类似设备不必区分不同的消息。但是,在实践中,许多基础设施组件确实会删除GET的正文。因此,您可以使用POST中表示的筛选条件来进行GET,但是您将冒险。)

11
有关在 HTTP GET 请求中包含请求体的讨论,请参见 此问题 - RickyA
许多人发明了不同于RESTful和HTTP规范的东西。RESTful的作者Fielding对此感到遗憾。 - Zon

10
简而言之:使用 X-HTTP-Method-Override 标头发送 POST 请求但覆盖 HTTP 方法。 实际请求: POST /books 实体主体: { "title": "Ipsum", "year": 2017 } 标头: X-HTTP-Method-Override: GET
在服务器端,检查标头 X-HTTP-Method-Override 是否存在,然后将其值作为方法来构建到后端最终端点的路由。同时,将实体主体作为查询字符串。从后端的角度来看,请求变得很简单,就像一个普通的 GET 请求一样。
这样可以使设计与 REST 原则保持协调。 编辑: 我知道这个解决方案最初是为了解决某些浏览器和服务器中的 PATCH 动词问题,但它也适用于我的情况,即在 URL 非常长的情况下使用 GET 动词,这正是问题所描述的。

2
IETF 已经弃用了 X- 前缀的 HTTP 头部: https://tools.ietf.org/html/rfc6648 - jannis
使用 X-HTTP-Method-Override 的 Google 翻译 API POST 请求 - David
@jannis,你链接的RFC版本是1.4。它对现有的“X-”删除没有做出建议,并且在1.5中也没有覆盖现有规格说明。...我认为,“X-”将继续存在。 - Jan Molnár

-11
如果您正在使用Java和JAX-RS进行开发,我建议您在@GET中使用@QueryParam。
当我需要浏览列表时,我也有同样的问题。
请参考以下示例:
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。


1
最好您能解释一下您的例子。它是使用什么编程语言编写的? - Aleks Andreev
嗨@AleksAndreev。谢谢你的意见。它变得更好了吗?谢谢。 - acacio.martins
1
这个问题涉及到RESTful服务的设计,而不是实现。这个回答并没有回答这个问题。 - Heretic Monkey
1
@user1331413 我个人认为现在好多了。感谢你的努力。不过,正如Mike McCaughan所说,这个问题是关于REST概念而不是实现的。 - Aleks Andreev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接