使用一长串查询参数设计RESTful查询API


153

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

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

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

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


2
使用简短的(1-char等)参数名称?
Madbreaks 2013年

2
它可能不是真正的RESTful,但是我认为在GET和POST方面必须切合实际。如果要发送的变量太多而又无法减少它们,则我将其发布。我不喜欢过度填充URL,但这就是我。
Doug Dawson

谢谢。即使这个问题已经解决,也正是我需要回答的问题。我很高兴你问。
Casey Crookston

Answers:


142

请记住,使用REST API,这全都是您的观点问题。

REST API中的两个关键概念是端点和资源(实体)。松散地说,端点要么通过GET返回资源,要么通过POST和PUT接受资源,依此类推(或上述方式的组合)。

接受POST后,您发送的数据可能会导致也可能不会导致创建新资源及其关联的端点,这很可能不会在POSTed网址下“生效”。换句话说,当您POST时,您会将数据发送到某个地方进行处理。POST端点通常不在资源所在的位置。

引用RFC 2616(省略了不相关的部分,并突出显示了相关的部分):

9.5开机自检

POST方法用于请求源服务器接受请求中包含的实体作为请求行中Request-URI标识的资源的新下属。POST旨在允许采用统一的方法来覆盖以下功能:

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

...

POST方法执行的操作可能不会导致可以由URI标识的资源。在这种情况下,取决于响应是否包括描述结果的实体, 200(确定)或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": "..."
    }

没有任何关于此端点的RESTful。它接受请求主体形式的数据(实体)。这些数据就是搜索条件 -像其他任何DTO一样。该端点响应以下请求生成资源(实体):Search Results。搜索结果资源是一种临时资源,可立即提供给客户端,无需重定向,也不会暴露于其他规范的URL。

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


您能否建议DTO的某些类命名约定?
夸德兹

我个人会和BooksSearchCriteriaDTOBooksSearchResultsDTO
阿米尔·阿比里

在POST / books / search的情况下,最佳的HTTP响应代码是什么?201还适用吗?
L. Holanda

9
201相反-意味着已经创建了资源。预期某个资源在某处拥有自己的唯一URI。201适用POST于CRUD的C部分。我会使用普通的200,也可以选择204作为空的搜索结果。
阿米尔·阿比里

@AmirAbiri非常感谢。
mohamed-mhiri

84

许多人已经接受了这样的做法,即具有太长或太复杂的查询字符串(例如,查询字符串不能轻松处理嵌套数据)的GET可以作为POST发送,而主体中表示的是复杂/长数据的请求。

在HTTP规范中查找POST规范。它的范围非常广。(如果要在REST漏洞中驾驶战舰,请使用POST。)

您将失去GET语义的某些优势...像自动重试一样,因为GET是幂等的,但是如果您能忍受这一点,那么接受使用POST处理真正较长或复杂的查询可能会更容易。

(大胆的题外话...我最近发现,根据HTTP规范,GET 可以包含一个文档正文。有一个部分写着:“任何请求都可以有一个文档正文,但本节中列出的除外”。我搜索并找到了一个HTTP作者正在谈论的线程,这是有意为之的,因此路由器等无需区分不同的消息。练习很多基础结构部件确实会降低GET的主体。因此,您可以使用主体中表示的过滤器进行GET,例如POST,但您会掷骰子。)


11
另请参见关于HTTP GET与身体的更多讨论的问题。
RickyA 2013年

6

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

实际要求

POST /书籍

实体主体

{“ title”:“ Ipsum”,“ year”:2017}

标头

X-HTTP-Method-Override:GET

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

这样,您可以使设计与REST原理保持一致。

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


2
IETF不推荐使用X前缀的HTTP标头:tools.ietf.org/html/rfc6648
jannis,2008年


@jannis您链接的RFC保持1.4。它不建议对现有的 X-删除和1.5。它不会覆盖现有规格。…… X-IMO会留在这里吗?
Jan Molnar

-3

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


如果您解释您的示例,那会更好。用什么编程语言编写的?
Aleks Andreev '18

嗨@AleksAndreev。谢谢您的意见。好了吗 tks
acacio.martins

这个问题是关于RESTful服务的设计,而不是关于实现。此答案不能回答问题。
异端猴子

@ user1331413恕我直言,是的,现在更好。谢谢您的努力。然而,正如Mike McCaughan所说,问题是关于REST概念,而不是实现
Aleks Andreev
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.