首先, 数据要在网络上传输, 就会涉及到数据序列化的问题, 因为双边数据在内存中的形式极有可能不一样, 这个时候需要约定一种双边都认可并且能实现的序列化方式, 使得数据能够被发送方序列化, 并且在网络中传输, 最终被接收方正确的反序列化.
为了适应不同的数据以及客户端环境, HTTP协议约定通过请求头中的Content-Type字段来确定数据序列化协议, 常用的有:application/x-www-form-urlencoded
multipart/formdata
application/json
其中第一个和第二个是几乎所有浏览器和服务器都支持的协议, 第一个用来传输文本信息, 而二个用来传输文本信息或者二进制数据, 比如文件上传等. 在没有二进制数据时, 通常使用第一种方式, 因为其压缩比例相对较高. 而第三个就是我们熟悉的JSON序列化.
无论是第一种还是第二种协议, 其所能处理的数据都是只有一层的简单key-value形式. 并且第一种的值只能是字符串, 第二种除了是字符串之外还可以是二进制数据. 为了表达多层的数据结构, 这两种协议都使用root[first][second]的形式作为key, 接收方在收到这样的key之后应该将其解析成三层结构(似乎并不是协议约定的事情). 比如在PHP中, 应该解析成三层的关联数组:
$data = array(
'root' => array(
'first' => array(
'second' => 'value'
)
)
)
而对于root[][]或者root[0][0]等格式, 应当解释成JS中的数组, 也就是PHP中的非关联数组:$data = array(array(array('value')))
fetch是一个whatwg推出的基于Promise的对XMLHttpRequest的封装接口标准(这个标准还没有被W3C采纳, 所以浏览器中的实现都不是最终的版本, 浏览器也没有被要求去实现这个接口). 这个标准提供了一个全局的函数fetch, 其签名如下:
partial interface WindowOrWorkerGlobalScope {
[NewObject] Promise fetch(RequestInfo input, optional RequestInit init);
};
其中RequestInit中包含了请求所需的参数, 其结构如下:
dictionary RequestInit {
ByteString method;
HeadersInit headers;
BodyInit? body;
USVString referrer;
ReferrerPolicy referrerPolicy;
RequestMode mode;
RequestCredentials credentials;
RequestCache cache;
RequestRedirect redirect;
DOMString integrity;
boolean keepalive;
any window; // can only be set to null
};
其中的body字段即是放到请求body中的数据, 其结构BodyInit定义如下:typedef (Blob or BufferSource or FormData or URLSearchParams or ReadableStream orUSVString) BodyInit
对于不同的body类型, fetch会自动设置请求头中的Content-Type, 具体请参考: https://fetch.spec.whatwg.org....如果传入的body是一个JS中的普通Object, 会被忽略, 如果是一个string, 则会设置头为text/plain, 这个时候服务端根据Content-Type只能将请求的body解析为字符串, 这个时候在$_POST为空数组, 所以都会为空.
当传入一个已经序列化的字符串时, 可以通过在请求参数的headers中手动设置Content-Type来指定序列化协议. 比如以JSON为序列化格式:
fetch('/', {
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify({first: {second: '1'}})
})
对于你的疑问, 也就是如何把JS中的Object按照application/x-www-form-urlencoded的格式进行序列化, 现在npm中有很多包可以做这样的事情, 比如qs等:
import {stringify} from 'qs'
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: stringify({root: {first: {second: 1}}})
})