常见的 POST 请求体编码方式

Posted by Matt Reach on April 10, 2018

前面介绍过了 HTTP 请求,现在看下 POST 这种请求方式,根据 HTTP 协议的规定,通过 POST 方式提交的数据必须放在请求体中,但协议没有规定数据采用什么编码方式,所以请求体的消息格式其实可以自动决定,但要让服务器能够解析才有意义。

请求体编码方式

一般情况下主流的有以下 4 种编码方式,编码方式通过 Content-Type 告知服务器 :

  • application/x-www-form-urlencoded

    如果是 HTML 里的 form 表单的话,在不设置 enctype 时,也是采用这种编码格式,key 和 value 都进行了 URL 转码,然后按照 k=v&k1=v1 的形式拼接。

    1
    
      ts=1521518926&plat=iPhone&uid=sh-upeadqawgz&signature=3de948ae93767a2382c6a3a4f389ca9b&bundleId=com.sohu.live.demo
    
  • application/json

    有个别情况,服务器要求我们给他 POST 过去一个 json 格式的参数,这时这种编码格式就排上用场了!还是刚才那些参数,使用这种方式编码结果如下:

    1
    
      {"ts":1521518926,"plat":"iPhone","uid":"sh-upeadqawgz","signature":"3de948ae93767a2382c6a3a4f389ca9b","bundleId":"com.sohu.live.demo"}
    
  • application/x-plist

    这种编码其实就是按xml格式编码,特别少见,工作好几年了一次也没用过。还是刚才那些参数,使用这种方式编码结果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
      	<key>bundleId</key>
      	<string>com.sohu.live.demo</string>
      	<key>plat</key>
      	<string>iPhone</string>
      	<key>signature</key>
      	<string>3de948ae93767a2382c6a3a4f389ca9b</string>
      	<key>ts</key>
      	<integer>1521518926</integer>
      	<key>uid</key>
      	<string>sh-upeadqawgz</string>
      </dict>
      </plist>
    
  • application/form-data

    还是刚才那些参数,使用这种方式编码结果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
      POST /upload-file HTTP/1.1
      Host: localhost:3000
      Content-Type: multipart/form-data; charset=utf-8; boundary=0xKhTmLbOuNdArY
      Connection: keep-alive
      Accept: */*
      User-Agent: SCNDemo/1.0 (iPhone; iOS 11.3; Scale/3.00)
      Content-Length: 448
      Accept-Language: en-us
      Accept-Encoding: gzip, deflate
        
      --0xKhTmLbOuNdArY
      Content-Disposition: form-data; name="ts"
        
      1521518926
      --0xKhTmLbOuNdArY
      Content-Disposition: form-data; name="plat"
        
      iPhone
      --0xKhTmLbOuNdArY
      Content-Disposition: form-data; name="uid"
        
      sh-upeadqawgz
      --0xKhTmLbOuNdArY
      Content-Disposition: form-data; name="signature"
        
      3de948ae93767a2382c6a3a4f389ca9b
      --0xKhTmLbOuNdArY
      Content-Disposition: form-data; name="bundleId"
        
      com.sohu.live.demo
      --0xKhTmLbOuNdArY--
    

需要提下的是 Content-Type 的值多了一个 boundary ,这个是边界的意思,用来分割参数用的,服务器端也根据这个去解析数据!

再看一个上传文件的 POST 请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
```
POST /upload-file HTTP/1.1
Host: localhost:3000
Content-Type: multipart/form-data; charset=utf-8; boundary=0xKhTmLbOuNdArY
Connection: keep-alive
Accept: */*
User-Agent: SCNDemo/1.0 (iPhone; iOS 11.3; Scale/3.00)
Content-Length: 50311
Accept-Language: en-us
Accept-Encoding: gzip, deflate

--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="k1"

v1
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="name"

Matt Reach
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="k2"

v2
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="date"

2018-04-19 15:13:40 +0000
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="test.jpg"; filename="node.jpg"
Content-Type: image/jpeg

图片内容.....
--0xKhTmLbOuNdArY--
```

文件那块可以指定 filename 和 Content-Type !filename 指的是上传文件的原始名称,name 是告诉服务器当做新的文件名。