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

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

注意事项:

  • “好的网页设计”;需要通过POST发送非幂等动作。这是非幂等的。
  • 当请求参数出现在URL中时,更容易开发和调试这个应用程序。
  • 该API并不打算广泛使用。
  • 似乎做一个没有正文的POST请求需要更多的工作,例如,必须显式添加Content-Length: 0报头。
  • 在我看来,没有正文的POST有点违背大多数开发人员和HTTP框架的期望。

在POST请求中通过URL查询而不是通过请求体发送参数还有什么缺点或优点吗?

编辑:考虑这一点的原因是操作不是幂等的,除了检索之外还有其他副作用。看到# EYZ0:

特别地,惯例已经 建立了GET和HEAD 方法不应该有 采取行动的意义 比检索。这些方法应该 被认为“安全”。这允许用户 代表其他方法的代理, 例如POST, PUT和DELETE 特殊的方式,使用户做出来 意识到一个可能的事实 请求不安全操作

...

方法也可以具有属性 “idempotence"(除了…… 错误或过期问题) N >的副作用;0相同 请求与单个请求相同 请求。方法GET, HEAD, PUT 和DELETE共享此属性。同时, 方法OPTIONS和TRACE应该 没有副作用,所以 幂等。< / p >
598048 次浏览

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

可能存在的优点/缺点可能是特定的客户端平台如何在其网络堆栈中处理POST和GET例程,以及web服务器如何处理这些请求。根据您的实现,一种方法可能比另一种更有效。知道了这一点你才会做出决定。

尽管如此,从程序员的角度来看,我更倾向于允许在主体中包含所有参数的POST,或者在url上包含所有参数的GET,并且显式地忽略任何POST请求的url参数。这样可以避免混淆。

你想要理由吗?这里有一个:

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

来源:HTML 4.01标准,章节17.13提交表格

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

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

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

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

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

. DELETE用于删除资源

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

用户代理通常知道做多个POST是不好的,并会对此发出警告,因为POST的目的是改变服务器状态(例如。在收银台付款),你可能不想这样做第二次!

与GET相比,你可以经常这样做(幂等)。

每个人都是对的:对于非幂等的请求,坚持使用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应用程序的主要方式,应用程序/ x-www-form-urlencoded是他们发布的内容类型,那么应该遵循该内容类型的规则。application/x-www-form-urlencoded的规则要具体得多(坦白地说,不常见):在这种情况下,必须将URI解释为一组参数,而不是资源位置。[这与Powerlord提出的有用性观点相同;使用web表单将内容POST到服务器可能会很困难。只是解释得有点不同。]

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

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

最后注意:有时查询参数用于其他事情,既不是定位资源也不是编码内容。是否见过类似“PUT=true”或“POST=true”的查询参数?这些是不允许使用PUT和POST方法的浏览器的变通方法。虽然这些参数被视为URL查询字符串的一部分(在连线中),但我认为它们不是URL查询在精神上的一部分。

我认为使用查询参数来标识URL上的资源,同时将内容有效负载限制在POST主体中,仍然是非常RESTful的。这似乎把“我要发送什么?”和“我要发送给谁?”的考虑分开了。

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

例如:

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

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

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

编辑:

我更喜欢这样,而不是像有些人建议的那样,让终点直接指向已经存在的资源:

POST /user_settings/4
{
...
}

原因有三:

  1. 我发现它有更好的可读性,因为查询参数是命名的,像"user_id"在上面,而不是仅仅“;4;;”。
  2. 通常,还有一个GET端点来获取相同的资源。在这种情况下,端点的路径和查询参数将是相同的,我喜欢这种对称性。
  3. 我发现,如果需要多个参数来定义已经存在的资源,嵌套会变得很麻烦,很难读取:
POST /user_settings/{user_id}/{which_settings_id}/{xyz}/{abc}/ ...
{
...
}