HTTP POST 请求中如何发送参数?

Posted

技术标签:

【中文标题】HTTP POST 请求中如何发送参数?【英文标题】:How are parameters sent in an HTTP POST request? 【发布时间】:2013-01-11 03:38:17 【问题描述】:

在 HTTP GET 请求中,参数作为 查询字符串 发送:

http://example.com/page?parameter=value&also=another

在 HTTP POST 请求中,参数不与 URI 一起发送。

值在哪里?在请求标头中?在请求正文中?它看起来像什么?

【问题讨论】:

"在 HTTP POST 请求中,参数不与 URI 一起发送。" - 虽然它可以(只是理论上),但不要混淆其他人。根据规范,POST 必须服务于非幂等请求,但您可以使用请求正文(通过 ONE 空行与 Headers 分隔)以及请求参数。 【参考方案1】:

HTTP POST 中的表单值在请求正文中发送,格式与查询字符串相同。

有关详细信息,请参阅spec。

【讨论】:

“相同格式”有点模棱两可。例如,它们是否以? 开头? @PeterWooster 是的,但没有提供示例。在这方面,就像一个回答说“看,在应用程序的博客 (link) 中有你的问题的答案”。 @PeterWooster 它不是必需的,但是当你忘记某些东西时它非常好,谷歌它,转到第一个链接,它有一个清晰,简洁的例子告诉你你需要什么而不是让您咀嚼过于详细的规格,即使是全面的,也可能不适合复习。想一想:这个网站上的大多数 QA 可以归结为“去阅读规范/手册/API/etc (link)”。会有用吗?不超过谷歌。 仅当内容类型为application/x-www-form-urlencoded时,并非总是如此。 GET查询字符串的格式与application/x-www-form-urlencoded不同。例如,空格的编码方式不同(%20 vs +)。答案在这方面具有误导性。【参考方案2】:

内容放在 HTTP 标头之后。 HTTP POST 的格式是具有 HTTP 标头,后跟一个空行,然后是请求正文。 POST 变量以键值对的形式存储在正文中。

您可以在 HTTP Post 的原始内容中看到这一点,如下所示:

POST /path/script.cgi HTTP/1.0
From: frog@jmarshall.com
User-Agent: HTTPTool/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32

home=Cosby&favorite+flavor=flies

您可以使用Fiddler 之类的工具查看这一点,您可以使用该工具查看通过网络发送的原始 HTTP 请求和响应负载。

【讨论】:

仅当内容类型为application/x-www-form-urlencoded时,并非总是如此。 @Camilo Martin .... [+1] 提出了很好的问题,@Joe Alfano .... [+1] 提出了很好的答案......我现在有了一个清晰的想法关于 POST 请求 .... 但是如果图像带有键值对数据信息 ..... POST 的结构是什么样的? @Joe,现在你为什么会有From 标题? @Joe,我喜欢随机包含 From 标头。 IMO 它带有 418 HTTP 状态代码。 如何添加用户和密码认证?【参考方案3】:

您不能直接在浏览器的 URL 栏上键入它。

例如,您可以使用Live HTTP Headers 来查看 POST 数据是如何在 Internet 上发送的。 结果会是这样的

http://127.0.0.1/pass.php
POST /pass.php HTTP/1.1

Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://127.0.0.1/pass.php
Cookie: passx=87e8af376bc9d9bfec2c7c0193e6af70; PHPSESSID=l9hk7mfh0ppqecg8gialak6gt5
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
username=zurfyx&pass=password

上面写着

Content-Length: 30
    username=zurfyx&pass=password

将是帖子值。

【讨论】:

澄清:Content-Length 应该是29 吗?这是字符串username=zurfyx&pass=password的实际长度。 @Hippo 是一个换行符吗? @vikingsteve 我明白你的意思。所以我猜内容的末尾总是有一个换行符。 标题与正文用额外的换行符分开【参考方案4】:

值以内容类型指定的格式在请求正文中发送。

通常内容类型为application/x-www-form-urlencoded,所以请求正文使用与查询字符串相同的格式:

parameter=value&also=another

当您在表单中使用文件上传时,您使用的是multipart/form-data 编码,它具有不同的格式。它更复杂,但您通常不需要关心它的外观,所以我不会展示示例,但知道它存在会很好。

【讨论】:

我忘记了文件上传是不同的(+1/接受)。你的回答就足够了,如果它有更多关于multipart/form-data的信息会更好。不过,对于那些感兴趣的人,这里是a question about it。 注意:正文与标题仅由 一个空行分隔。 您解释了我们在 HTTPBody 中放置的内容,但我们在 HTTPHeader 中放置/写入了什么?它有什么用途? @Honey:post 的 HTTP 标头看起来像 get 的标头,但使用动词 POST 而不是 GET,以及请求的内容类型值(和可选的内容长度值)内容(正文)。每种类型的请求都有一个标头,有些类型也有一个主体。 @KennethWorden 不,任何方法都不会正确发送 JSON。但是,您可以以multipart/form-data 编码的格式上传 json 文件,或者如果您负责请求构造,请将 content-type 更改为 application/json 并将 json 文本直接粘贴到 http 正文中【参考方案5】:

POST 请求中的默认媒体类型是application/x-www-form-urlencoded。这是一种编码键值对的格式。密钥可以重复。每个键值对由& 字符分隔,每个键与其值由= 字符分隔。

例如:

Name: John Smith
Grade: 19

编码为:

Name=John+Smith&Grade=19

这被放置在 HTTP 标头之后的请求正文中。

【讨论】:

您解释了我们在 HTTPBody 中放置了什么,但我们在 HTTPHeader 中放置/写入了什么? 您提到密钥可以重复,那么重复的结果是什么?最后一个会自动覆盖以前的值吗?谢谢。 @JinghuiNiu 如果键是重复的,它应该被解析为一个数组。这很晚,但可能对其他人有所帮助。【参考方案6】:

简短回答: 在 POST 请求中,值在请求的“正文”中发送。对于网络表单,它们最有可能使用application/x-www-form-urlencodedmultipart/form-data 的媒体类型发送。设计用于处理网络请求的编程语言或框架通常对此类请求执行“正确的事情™”,并为您提供对易于解码的值的轻松访问(如 PHP 中的 $_REQUEST$_POST 或 @987654334 @, flask.request.form 在 Python 中)。


现在让我们离题一点,这可能有助于理解其中的区别;)

GETPOST 请求之间的区别主要是语义上的。它们的“使用”方式也不同,这解释了值传递方式的差异。

GET (relevant RFC section)

在执行GET 请求时,您向服务器请求一个或一组实体。为了允许客户端过滤结果,它可以使用 URL 的所谓“查询字符串”。查询字符串是? 之后的部分。这是URI syntax 的一部分。

因此,从您的应用程序代码(接收请求的部分)的角度来看,您将需要检查 URI 查询部分以访问这些值。

请注意,键和值是 URI 的一部分。浏览器可能对 URI 长度施加限制。 HTTP 标准声明没有限制。但在撰写本文时,大多数浏览器确实限制了 URI(我没有具体的值)。 GET 请求应该永远用于向服务器提交新信息。特别是不是更大的文件。这就是你应该使用POSTPUT 的地方。

POST (relevant RFC section)

当执行POST 请求时,客户端实际上是在向远程主机提交一个新的文档。因此,query 字符串(在语义上)没有意义。这就是您无法在应用程序代码中访问它们的原因。

POST 稍微复杂一点(方式更灵活):

当接收到一个 POST 请求时,您应该总是期待一个“有效负载”,或者,用 HTTP 术语来说:message body。消息体本身没什么用,因为没有标准(据我所知。也许是application/octet-stream?)格式。正文格式由Content-Type 标头定义。当使用带有method="POST" 的HTML FORM 元素时,这通常是application/x-www-form-urlencoded。如果您使用文件上传,另一种非常常见的类型是multipart/form-data。但它可能是任何东西,范围从text/plainapplication/json 甚至是自定义的application/octet-stream

在任何情况下,如果 POST 请求是由应用程序无法处理的 Content-Type 发出的,它应该返回一个 415 status-code。

大多数编程语言(和/或网络框架)都提供了一种将消息正文从最常见类型(如application/x-www-form-urlencodedmultipart/form-dataapplication/json)解码/编码的方法。所以这很容易。自定义类型可能需要更多的工作。

以标准的 HTML 表单编码文档为例,应用程序应执行以下步骤:

    阅读Content-Type 字段 如果该值不是受支持的媒体类型之一,则返回带有415 状态代码的响应 否则,解码消息正文中的值。

同样,PHP 等语言或其他流行语言的网络框架可能会为您处理这个问题。对此的例外是415 错误。没有框架可以预测您的应用程序选择支持和/或不支持哪些内容类型。这取决于你。

PUT (relevant RFC section)

PUT 请求的处理方式与POST 请求的处理方式几乎相同。最大的区别在于POST 请求应该让服务器决定如何(如果有的话)创建新资源。从历史上看(从现在已经过时的 RFC2616 开始,它是创建一个新资源作为请求发送到的 URI 的“从属”(子)。

相比之下,PUT 请求应该将资源准确地“存放”在该 URI 处,并且准确地 使用该内容。不多也不少。这个想法是 client 负责在“PUT”之前制作 complete 资源。服务器应该在给定的 URL 上按原样接受它。

因此,POST 请求通常不用于替换现有资源。 PUT 请求可以同时创建替换。

旁注

还有“path parameters”可以用来向远程发送额外的数据,但它们太少见了,我在这里不再赘述。但是,作为参考,这里是 RFC 的摘录:

除了层次路径中的点段之外,还考虑了路径段 通过通用语法不透明。生成 URI 的应用程序经常使用 段中允许的保留字符来分隔特定于方案的或 取消引用处理程序特定的子组件。例如,分号 (";") 和等号 ("=") 保留字符通常用于分隔参数和 适用于该段的参数值。逗号 (",") 保留 字符通常用于类似目的。例如,一个 URI 生产者 可以使用诸如“name;v=1.1”之类的段来指示对版本的引用 "name" 的 1.1,而另一个可能使用诸如 "name,1.1" 之类的段来 表示相同。参数类型可以由特定于方案的定义 语义,但在大多数情况下,参数的语法是特定的 到 URI 解引用算法的实现。

【讨论】:

我可能确实有点偏离了方向。我在答案的顶部添加了一个“tl;dr”,这应该更清楚。 我刚刚还编辑了它以引用 RFC7231 而不是 RFC2616(它已经过时了一段时间)。除了更新的链接之外,此答案的主要区别在于“PUT”部分。 我认为 PUT 的处理方式与 POST 不同,因为它应该是幂等的? ***.com/questions/611906/… @rogerdpack 你没有错。如果您阅读PUT 部分的第二段,您会发现它 幂等的。 POST 相反可以 - 根据定义 - 不是。 POST 将始终创建一个新资源。如果存在相同的资源,PUT 将替换它。所以如果你调用POST 10 次,你将创建 10 个资源。如果你调用PUT 10 次,它(可能)只会创建一个。这能回答你的问题吗?【参考方案7】:

一些网络服务要求您分别放置请求数据元数据。例如,远程函数可能期望已签名的元数据字符串包含在 URI 中,而数据则发布在 HTTP 正文中。

POST 请求在语义上可能如下所示:

POST /?AuthId=YOURKEY&Action=WebServiceAction&Signature=rcLXfkPldrYm04 HTTP/1.1
Content-Type: text/tab-separated-values; charset=iso-8859-1
Content-Length: []
Host: webservices.domain.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)

name    id
John    G12N
Sarah   J87M
Bob     N33Y

这种方法在逻辑上使用单个 Content-Type 将 QueryString 和 Body-Post 组合在一起,这是 Web 服务器的“解析指令”。

请注意:HTTP/1.1 被包装,左侧是#32(空格),右侧是#10(换行符)。

【讨论】:

/user/john/?user=john 之间的区别只是语义上的区别(HTTP 并没有真正对查询字符串进行特殊处理),所以我认为这是合理的预期。但是“被左边的空格包裹”是什么意思? HTTP 方法之前没有空格。你的意思是帖子正文的空行? 上述代码中...Ym04HTTP/1.1 之间有一个空格(ASCII #32)。所以 QueryString 只是位于动词和协议版本之间。 您的注释听起来像是出乎意料的和特定于版本的东西。坦率地说,那里似乎很明显有一个空间。换行也适用于其他行,就像所有的 unix 一样。 我只是强调了我无法在代码中标出的内容。这可能看起来很明显,但有时并非如此。 确实,我们可以通过使用? 分隔URI 和参数来将查询参数作为URL 的一部分传递,就像我们对GET 请求所做的那样。【参考方案8】:

首先,让我们区分GETPOST

获取: 这是向服务器发出的默认HTTP 请求,用于从服务器检索数据以及URI? 之后的查询字符串用于检索唯一资源。

这是格式

GET /someweb.asp?data=value HTTP/1.0

这里data=value是传递的查询字符串值。

POST:它用于安全地向服务器发送数据,所以需要任何东西,这是POST请求的格式

POST /somweb.aspHTTP/1.0
Host: localhost
Content-Type: application/x-www-form-urlencoded //you can put any format here
Content-Length: 11 //it depends
Name= somename

为什么 POST 优于 GET?

GET 中,发送到服务器的值通常附加到查询字符串中的基本 URL,现在有两个后果

GET 请求与参数一起保存在浏览器历史记录中。因此,您的密码在浏览器历史记录中保持未加密状态。在过去,这对 Facebook 来说是一个真正的问题。 通常服务器对URI 的长度有限制。如果发送的参数过多,您可能会收到414 Error - URI too long

在发布请求的情况下,您来自字段的数据将被添加到正文中。计算请求参数的长度,并添加到内容长度的头部,没有重要数据直接附加到URL。

您可以使用 Google 开发者工具的网络部分查看有关如何向服务器发出请求的基本信息。

您可以随时在 Request Headers 中添加更多值,例如 Cache-ControlOriginAccept

【讨论】:

有关安全性的假设仅在HTTPS 连接的上下文中成立,而不是HTTPHTTPS 加密URL(包括查询参数)和Request Body,而HTTP 既不加密/保护也不加密。所描述的问题来自许多浏览器将URIs(包括URLs)存储在其历史数据库中(通常未加密)的事实。因此,对于任何敏感内容,请仅使用 Request Body+HTTPS @PetruZaharia 我同意你的解释。您也可以建议将此作为编辑,我很乐意接受! :)【参考方案9】:

post参数有多种方式/格式

表单数据 原始数据 json 编码数据 文件 xml

它们由表示为 mime 类型的 Header 中的内容类型控制。

【讨论】:

您的答案如何扩展或澄清此问题的任何其他答案?

以上是关于HTTP POST 请求中如何发送参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过远程方法发送 json-rpc http post 请求并在 java 中传递加密参数

asp 发送post参数

php如何通过get方法发送http请求,并且得到返回的参数

Apache HTTP 客户端,POST 请求。如何正确设置请求参数?

如何从 Firefox 或 Chrome 浏览器手动发送 HTTP POST 请求

如何在没有表单的POST请求中发送url参数