0x00 前言

HTTP 本身,POST body是不限格式的。

然而没有标准怎么行,于是有好几种 Content-Type 和 encoding 方案给 POST 用。

0x01 HTTP 本身的编码相关问题

首先,是 HTTP 标准的规定

    HTTP Header 必须为 ASCII 编码

规定虽然是规定,还是得看BS两端具体能不能容错,再说 unicode 也兼容 ASCII,所以大部分时候没什么毛病。 不过我们最好还是遵守 spec

然后,与 charset 相关的 header 有 Content-Type 里面的 charset 属性

一般这样写:

Content-Type: text/html;charset=utf-8

似乎是指 Header 虽然要求 ASCII,可是 entity body 却可以指定其他编码(可能会要求兼容 ASCII)。 这部分读 spec 也没看太懂,期待大神指教。

0x02 再看 POST

几种常见/常用的 POST entity body Content-Type

application/x-www-form-urlencoded
multipart/form-data
application/json

然后从 Postman 里面又看到了几种平时没怎么见过的或者不常用的

text/plain
application/javascript
text/xml
application/xml
text/html

看到这里其实就明白了,归根结底还是来源于 MIME (rfc1341)

0x03 常见的 POST 都长什么样子

1.application/x-www-form-urlencoded

大概长这样:

POST /abc HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

最基础的 HTML Form 提交之后,浏览器就会使用x-www-form-urlencoded,是默认的表单格式。

除了 Header 指定的 Content-Type 之外:

  • 格式为 key=value&x=y
  • 使用 urlencode 编码
  • a[]=abc&a[]=123 可以用来表示 array

2.multipart/form-data

大概长这样:

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="submit-name"

Larry
--AaB03x
Content-Disposition: form-data; name="files"
Content-Type: multipart/mixed; boundary=BbC04y

--BbC04y
Content-Disposition: file; name="forminputname" filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--BbC04y
Content-Disposition: file; filename="file2.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary

...contents of file2.gif...
--BbC04y--
--AaB03x--

给 Form 设置 enctype="multipart/form-data" 之后,就可以使用 multipart 格式来上传文件了。

除了 Header 指定的 Content-Type 之外:

  • Content-Type 后面跟了个 boundary 属性来声明 multipart 的边界,作为多个 Form input 的分隔符
  • 每个部分的起始分隔符前面要加--
  • 每个部分的结束分隔符前后都要加--
  • 每个部分都能指定 Content 相关的 header,甚至能继续嵌套 multipart
  • 通过 Content-Disposition 提供 Form input 的 name 属性

这部分比较复杂,可以参考 Stackoverflow - What is http multipart request?

3.application/json

大概长这样:

POST /efg HTTP/1.1
Content-Type: application/json;charset=utf-8

{"title":"test","sub":[1,2,3]}

没什么好说的,简单,好用。

如果BS两端都支持的话,非 ASCII 编码并不需要额外编码,JSON 本身并没有要求。 但通常为了更好的兼容性,会使用编码后的形式,而这里就是跟 javascript 的 escape 走了,并不是urlencode。 而编码后也很简单,直接使用 unicode,如 "\u0421\u043b\u043e\u0432\u043e",或者 "%u0421%u043b%u043e%u0432%u043e"