我正在设计一个通过 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
。如果你不这样做,你只是在自找麻烦。 GET
、PUT
和 DELETE
方法要求是幂等的。想象一下,如果客户端为您的服务预取每一个可能的 GET
请求,您的应用程序会发生什么——如果这会导致客户端可见的副作用,那么就有问题了。
我同意发送带有查询字符串但没有正文的 POST
似乎很奇怪,但我认为在某些情况下它可能是合适的。
将 URL 的查询部分视为对资源的命令,以限制当前请求的范围。通常,查询字符串用于对 GET
请求(如 ?page=1&sort=title
)进行排序或过滤,但我认为在 POST
上也可以限制范围(可能如 ?action=delete&id=5
)。
每个人都是对的:坚持使用 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 查询的一部分。
你要理由吗?这是一个:
不能使用 Web 表单向混合使用 GET 和 POST 的页面发送请求。如果将表单的方法设置为 GET,则所有参数都在查询字符串中。如果将表单的方法设置为 POST,则所有参数都在请求正文中。
来源:HTML 4.01 标准,第 17.13 Form Submission 节
method
为 POST 时,没有提及更改表单的 action
中的 URI。当然,任何 URI 都可以包含查询字符串部分。
/Books?bookCode=1234
。 Web 服务器将获取 POST 表单变量和查询字符串。
从编程的角度来看,对于客户端,它正在打包参数并将它们附加到 url 并执行 POST 与 GET。在服务器端,它评估来自查询字符串的入站参数,而不是发布的字节。基本上,这是一个洗涤。
可能存在优点/缺点的地方可能在于特定客户端平台如何在其网络堆栈中使用 POST 和 GET 例程,以及 Web 服务器如何处理这些请求。根据您的实施,一种方法可能比另一种更有效。知道这将指导您在这里做出决定。
尽管如此,从程序员的角度来看,我更喜欢允许在正文中包含所有参数的 POST,或者在 url 上包含所有参数的 GET,并在任何 POST 请求中显式忽略 url 参数。它避免了混淆。
我认为在将内容有效负载限制在 POST 正文中的同时,具有标识 URL 上的资源的查询参数仍然可能是相当 RESTful 的。这似乎将“我要发送什么?”的考虑分开。与“我将其发送给谁?”。
REST 阵营有一些指导原则,我们可以使用这些原则来标准化我们使用 HTTP 动词的方式。这在您构建 RESTful API 时很有帮助。
简而言之:GET 应该是只读的,即对服务器状态没有影响。 POST 用于在服务器上创建资源。 PUT 用于更新或创建资源。 DELETE 用于删除资源。
换句话说,如果您的 API 操作更改了服务器状态,REST 建议我们使用 POST/PUT/DELETE,而不是 GET。
用户代理通常知道执行多个 POST 是不好的,并且会警告它,因为 POST 的目的是改变服务器状态(例如,在结帐时支付商品),您可能不想这样做两次!
与您可以随心所欲地执行的 GET 相比(幂等)。
我发现在 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}/ ...
{
...
}
/action?response_format=json
)中传递