Post请求中form-data和x-www-form等格式的区别
使用post请求发送数据时候,数据会放在body部分而不是头信息中,而数据放在body中传输时候,我们可以将这个body中的信息看作是一个长长的单个字符串,而我们使用不同的编码就是将这个数据用不同的形式进行发送。
application/x-www-form-urlencoded
原理
它是post的默认格式,它默认使用了一些特殊的符号作为分割符,例如&(ascii码为0x26)
, =(0x3D)
,
(空格)等,我们input中的name、value信息会发送时使用=
连接,不同的input之间使用&
连接;在接收端使用相同方式进行解码,一旦遇到&
,=
等字符将会按照规定将这个长字节断开,还原为原来key:value 形式的数据,以供我们使用。所以使用这种格式解码时,可以看作是一个个字节进行扫描的,并使用特殊的字节将其不同的部分区分。
问题
这种方式发送一个非特殊字符的单字节字符时没有问题的,当我们需要发送一个多字节字符,例如中文的中
字,如果直接使用utf-8编码发送,其编码为0xE4B8AD
,会使用三个字节传输,分别为0xE4
,0xB8
,0xAD
,当某些中文或者多字节的字符发送时,恰好有一个字节解码为0x26
或者0x3D
时,这个多字节字符中一个字节将会被接收段解码为=
或&
符号这中特殊字节,从而将数据从此处断开,产生数据的错误。
为了解决这个问题,使用了JS中URLencode转码方法,也就是URL编码,我们经常看到一个URL类似于https://cn.bing.com/search?q=ascii%E7%A0%81%E8%A1%A8
, %E7%A0%81%E8%A1%A8
就是经过这种编码方式产生的。我们发送上面的中
,utf-8编码为0xE4B8AD
(这是3个字节的16进制表示法,它表示的字符是中
一个字符),不能直接进行发送。需要使用URLencode进行转码,使用这个编码的16进制形式的字符串为基础,每两个字符前添加一个%,得到字符串%E4%B8%AD
(这是一个字符串,每一个字符都是一个字节,这里总共9个字节),再将这个字节发送。这样就解决了上面的问题。
使用这中方式发送的数据,一个中文将会被编码为9个字节进行发送,在发送大量的中文及多字符数据时会严重的浪费网络带宽资源,一般在发送少量的数据使用。例如下面是在一个表单中发送{country: 中国, city:北京}信息时候,浏览器发送的部分信息
POST /users/ HTTP/1.1
Host: localhost:8000
Content-Type: application/x-www-form-urlencoded // 头中指定格式
country=%E4%B8%AD%E5%9B%BD&city=%E5%8C%97%E4%BA%AC // 编码后的数据
form-data
application/x-www-form-urlencoded
必须进行多字节字符的编码,这是由于多字节字符的单个字节信息可能与特殊字节信息冲突。而form-data的解决方法就是, 不在使用这些单个字节的特殊字符作为特殊分割,而是告诉对方使用分割字符串。上面的{country: 中国, city:北京}表单信息使用这种格式发送时,浏览器发送的数据为。
POST /users/ HTTP/1.1
Host: localhost:8000
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW--,
Content-Disposition: form-data; name="country"
中国
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Content-Disposition: form-data; name="city"
北京
------WebKitFormBoundary7MA4YWxkTrZu0gW--
在header头信息中,Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
分别指定了格式和 boundary(分割字符串),在body中使用了这个boundary指定的字符串作为分割,从而可以轻易的还原为key:value的形式。这里的中国
和北京
中,每一个字符l例如中
,都是直接使用utf-8编码(大部分网络传输都使用该编码)后进行传输的,也就是说每个中文只需要3个字节。
总结
application/x-www-form-urlencoded 他能简洁的将key:value的数据进行分割。这一点相比form-data使用长长的字符串作为分割符优势巨大,虽然对于多字节的数据会因为编码造成字节量剧增(可以看到一个中文字符增加了2倍字节量),但是在单字节或者是少量的数据时,这种方式是划算的。
form-data 是一种重视数据的方式,通常我们在value值中会发送大量的文本信息,或者直接的传送一个文件,数据直接编码为二进制发送,不会产生多余的字节,适合大文本的传输。