ChatGPT解决这个技术问题 Extra ChatGPT

带有 URL 查询参数的 HTTP POST——好主意还是不好?

我正在设计一个通过 HTTP 的 API,我想知道是否使用 HTTP POST 命令,但只有 URL 查询参数,没有请求正文,是一个好方法。

注意事项:

“良好的 Web 设计”需要通过 POST 发送非幂等操作。这是一个非幂等的动作。

当请求参数存在于 URL 中时,更容易开发和调试此应用程序。

API 不适合广泛使用。

似乎发出没有正文的 POST 请求需要更多的工作,例如必须显式添加 Content-Length: 0 标头。

在我看来,没有正文的 POST 也有点违背大多数开发人员和 HTTP 框架的期望。

通过 URL 查询而不是请求正文在 POST 请求上发送参数是否有更多的陷阱或优势?

编辑:正在考虑的原因是这些操作不是幂等的,并且除了检索之外还有副作用。请参阅the HTTP spec

特别是,已经建立了约定,即 GET 和 HEAD 方法不应该具有采取除检索之外的操作的意义。这些方法应该被认为是“安全的”。这允许用户代理以特殊的方式表示其他方法,例如 POST、PUT 和 DELETE,以便用户意识到正在请求可能不安全的操作。 ...方法还可以具有“幂等性”的属性,因为(除了错误或过期问题)N > 0 相同请求的副作用与单个请求相同。 GET、HEAD、PUT 和 DELETE 方法共享此属性。此外,方法 OPTIONS 和 TRACE 不应该有副作用,因此本质上是幂等的。

如果您不打算在正文中提供数据,为什么还要使用 POST?
因为操作不是幂等的。
@Jared,请注意,从 2.5 年前开始,这个问题中没有出现“REST”这个词。 :) 关于幂等性的 HTTP 规范适用于 Web 服务的每月风格架构。幸运的是,这个 API 被设计为代理的系统无论如何都已经过时了。
因为服务器日志不记录 POST 参数,而是记录查询字符串。运行一系列请求而不在浏览器中检测它,然后查看回溯,比单击它们要容易得多。 API 也不是浏览器到服务器,而是服务器到服务器。最重要的是,无论如何,整个事件都是罐头。 :)
对于其他不知道幂等意味着什么的人:| restapitutorial.com/lessons/idempotency.html

L
Luke Girvin

如果您的操作不是幂等的,那么您必须使用 POST。如果你不这样做,你只是在自找麻烦。 GETPUTDELETE 方法要求是幂等的。想象一下,如果客户端为您的服务预取每一个可能的 GET 请求,您的应用程序会发生什么——如果这会导致客户端可见的副作用,那么就有问题了。

我同意发送带有查询字符串但没有正文的 POST 似乎很奇怪,但我认为在某些情况下它可能是合适的。

将 URL 的查询部分视为对资源的命令,以限制当前请求的范围。通常,查询字符串用于对 GET 请求(如 ?page=1&sort=title)进行排序或过滤,但我认为在 POST 上也可以限制范围(可能如 ?action=delete&id=5)。


我为这个特殊情况选择了这个答案,但我认为 R. Bemrose 的论点对于公共 API 来说是令人信服的。
我认为他的回答并不完全正确。如果您在 HTML 页面发送到客户端时知道表单帖子的 URL 参数,则可以将这些 URL 参数附加到表单的 action 属性中,否则 JavaScript 可以在提交表单时设置 URL 参数。
将 xml 文件发布到带有查询参数的 url 怎么样?那可能吗?
另一个例子:请求数据可能在 http 实体中,而请求的响应格式在查询参数(/action?response_format=json)中传递
+1 今天学到了一些东西。删除具有幂等性的技术性。如果对象被实际删除,那么您将得到一个未找到的 404,因此服务器将具有相同的状态,但响应将不同。见牛图片:restapitutorial.com/lessons/idempotency.html
L
Luke Girvin

每个人都是对的:坚持使用 POST 处理非幂等请求。

同时使用 URI 查询字符串和请求内容怎么样?那么它是有效的HTTP(见注1),为什么不呢?!

这也是完全合乎逻辑的:URL,包括它们的查询字符串部分,用于定位资源。而 HTTP 方法动词(POST - 及其可选的请求内容)用于指定操作或如何处理资源。这些应该是正交的问题。 (但是,对于 ContentType=application/x-www-form-urlencoded 的特殊情况,它们并不是完美的正交关注点,请参见下面的注释 2。)

注 1:HTTP 规范 (1.1) 没有规定查询参数和内容对于接受 POST 或 PUT 请求的 HTTP 服务器是互斥的。所以任何服务器都可以自由地接受两者。即,如果您编写服务器,则没有什么可以阻止您选择同时接受两者(除了可能是一个不灵活的框架)。通常,服务器可以根据它想要的任何规则来解释查询字符串。它甚至可以使用条件逻辑来解释它们,该逻辑也引用其他标头(如 Content-Type),这导致注 2:

注意 2:如果 Web 浏览器是人们访问您的 Web 应用程序的主要方式,并且 application/x-www-form-urlencoded 是他们发布的 Content-Type,那么您应该遵循该 Content-Type 的规则。并且 application/x-www-form-urlencoded 的规则更加具体(坦率地说,不寻常):在这种情况下,您必须将 URI 解释为一组参数,而不是资源位置。 [这与Powerlord提出的用处相同;可能很难使用 Web 表单将内容发布到您的服务器。只是解释有点不同。]

注 3:查询字符串最初的用途是什么? RFC 3986 将 HTTP 查询字符串定义为 URI 部分,用作定位资源的非分层方式。

如果提出这个问题的读者想问什么是好的 RESTful 架构:RESTful 架构模式不需要 URI 方案以特定方式工作。 RESTful 架构关注系统的其他属性,例如资源的可缓存性、资源本身的设计(它们的行为、能力和表示),以及是否满足幂等性。或者换句话说,实现与HTTP协议及其HTTP方法动词集高度兼容的设计。 :-) (换句话说,RESTful 架构对资源的定位方式不是很清楚。)

最后一点:有时查询参数会用于其他事情,既不是定位资源也不是编码内容。见过像“PUT=true”或“POST=true”这样的查询参数吗?这些是不允许您使用 PUT 和 POST 方法的浏览器的解决方法。虽然这些参数被视为 URL 查询字符串的一部分(在线上),但我认为它们本质上不是 URL 查询的一部分。


P
Powerlord

你要理由吗?这是一个:

不能使用 Web 表单向混合使用 GET 和 POST 的页面发送请求。如果将表单的方法设置为 GET,则所有参数都在查询字符串中。如果将表单的方法设置为 POST,则所有参数都在请求正文中。

来源:HTML 4.01 标准,第 17.13 Form Submission


这是一个不错的论点,但我认为现代浏览器的 Javascript 实现有点让它成为一个争论点。不过我会考虑一下——它以一种面向未来的方式引人注目。仅仅因为我现在不使用表格并不意味着我以后不想。
将 GET 与 POST 混合是一个非常糟糕的主意 - 非常破坏 HTTP 并且没有充分的理由。
该片段未出现在您链接到的页面上
没错,但是method属性只是定义了“表单数据集”如何包含在请求中。当 method 为 POST 时,没有提及更改表单的 action 中的 URI。当然,任何 URI 都可以包含查询字符串部分。
@Powerlord这是错误的。尝试使用例如操作设置要发布的表单。 /Books?bookCode=1234。 Web 服务器将获取 POST 表单变量和查询字符串。
j
jro

从编程的角度来看,对于客户端,它正在打包参数并将它们附加到 url 并执行 POST 与 GET。在服务器端,它评估来自查询字符串的入站参数,而不是发布的字节。基本上,这是一个洗涤。

可能存在优点/缺点的地方可能在于特定客户端平台如何在其网络堆栈中使用 POST 和 GET 例程,以及 Web 服务器如何处理这些请求。根据您的实施,一种方法可能比另一种更有效。知道这将指导您在这里做出决定。

尽管如此,从程序员的角度来看,我更喜欢允许在正文中包含所有参数的 POST,或者在 url 上包含所有参数的 GET,并在任何 POST 请求中显式忽略 url 参数。它避免了混淆。


s
swizzcheez

我认为在将内容有效负载限制在 POST 正文中的同时,具有标识 URL 上的资源的查询参数仍然可能是相当 RESTful 的。这似乎将“我要发送什么?”的考虑分开。与“我将其发送给谁?”。


问题不在于 REST。
@user359996 并非所有 HTTP API 都是 RESTful。事实上,大多数声称实际上并非如此的 API。此外,有趣的事实是,REST 也不仅仅是 HTTP。
P
Pang

REST 阵营有一些指导原则,我们可以使用这些原则来标准化我们使用 HTTP 动词的方式。这在您构建 RESTful API 时很有帮助。

简而言之:GET 应该是只读的,即对服务器状态没有影响。 POST 用于在服务器上创建资源。 PUT 用于更新或创建资源。 DELETE 用于删除资源。

换句话说,如果您的 API 操作更改了服务器状态,REST 建议我们使用 POST/PUT/DELETE,而不是 GET。

用户代理通常知道执行多个 POST 是不好的,并且会警告它,因为 POST 的目的是改变服务器状态(例如,在结帐时支付商品),您可能不想这样做两次!

与您可以随心所欲地执行的 GET 相比(幂等)。


REST 阵营说您应该使用 HTTP 规范中定义的 HTTP。即 RFC2616 仅此而已,仅此而已。
@Darrel 引用 ibm.com/developerworks/webservices/library/ws-restful:REST 要求开发人员以与协议定义一致的方式明确使用 HTTP 方法。这种基本的 REST 设计原则在创建、读取、更新和删除 (CRUD) 操作与 HTTP 方法之间建立了一对一的映射。根据这个映射: 要在服务器上创建资源,请使用 POST。要检索资源,请使用 GET。要更改资源的状态或更新它,请使用 PUT。要移除或删除资源,请使用 DELETE。
对不起,但这完全是错误的。 REST 要求遵守统一的接口。如果您使用的是 HTTP,则该统一接口部分由 RFC 2616 定义。在该规范中,创建、读取、更新和删除与 HTTP 方法之间没有一对一的映射。
GET 和 DELETE 很好地映射到 CRUD 中的读取和删除,但使用 PUT/POST 进行更新和创建并不那么简单。请参阅stackoverflow.com/questions/630453/put-vs-post-in-rest
6 年后回顾这个问题,鉴于这个问题已经被浏览了大约 10 万次,我觉得值得进行一次小更新。根据 Fielding 对 REST (ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) 的定义,Darrel 是正确的——没有提到将 HTTP 动词映射到 CRUD。 IBM 的开发人员建议(上面评论中的链接)反映了实现 RESTful API 的常见做法,而不是 Fielding 对 REST 的定义。
Y
Ytsen de Boer

我发现在 POST 端点上使用查询参数是完全可以接受的,如果它们引用必须通过 POST 端点更新(未创建)的已经存在的资源。

例如:

POST /user_settings?user_id=4
{
  "use_safe_mode": 1
}

上面的 POST 有一个引用现有资源的查询参数,镜像 GET 端点定义以获取相同的资源。

body 参数定义如何更新现有资源。

编辑:

我更喜欢让端点的路径直接指向已经存在的资源,就像一些人建议的那样,如下所示:

POST /user_settings/4
{
...
}

原因有三:

我发现它具有更好的可读性,因为查询参数被命名,如上面的“user_id”,而不仅仅是“4”。通常,还有一个 GET 端点来获取相同的资源。在这种情况下,端点的路径和查询参数将是相同的,我喜欢这种对称性。如果需要多个参数来定义已经存在的资源,我发现嵌套可能会变得繁琐且难以阅读:

POST /user_settings/{user_id}/{which_settings_id}/{xyz}/{abc}/ ...
{
...
}

我认为端点最好是“users/4/settings”,因为没有用户(顾名思义),这些用户设置永远不会存在。
我尊重。
POST 用于创建资源。如果您有资源,为什么要在查询参数中传递它? POST 不应该有查询参数。您可以实现该服务来尊重查询参数,但这违反了 REST 规范。
“你为什么在查询参数中传递它?”因为,与 GET 请求一样,查询参数用于引用现有资源。 POST 部分用于创建新资源,该资源是根据查询参数中指定的现有资源创建的。这导致了我想在这里分享的答案中列出的 3 个好处。
我最新评论的附录:POST 也用于更新现有资源,而不仅仅是创建它们。在这些特定情况下,我认为查询参数然后指定要更新的现有资源是完全可以接受的。特别是考虑到与 GET 查询检索相同资源的结果对称性。