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"
。