一剑化三清:
全真教上乘剑术,通过手腕抖动,能使每一剑均可化为三招。
为 杨过 和 小龙女 一同参研领悟所学,
因剑法传到古墓,故为 王重阳 所创,并且为 林朝英 所得

Content-Type 是指 http/https 发送信息至服务器时的内容编码类型,用于 表明发送数据流的类型,服务器根据编码类型使用特定的解析方式,获取数据流中的数据。

下面详细介绍一下常用的类型及如何使用 Python 模拟:

application/x-www-form-urlencoded

最常见的 POST 提交数据的方式了。浏览器原生 <form> 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。

首先,Content-Type 被指定为 application/x-www-form-urlencoded,其次,提交的数据按照 key1=val1&key2=val2 的方式进行编码,keyval 都进行了 url 编码。

form 表单

<form action="/" method="POST">
    ...
</form>

Fiddler 抓包分析

Python requests 模拟请求

import requests

data = {
   
	'username': 'admin',
	'password': 'admin'
}

response = requests.post('http://httpbin.org/post', data=data)
Requests 库支持以 form表单形式发送 post请求,只需要将请求的参数构造成一个字典,然后传递给 `requests.post()` 的 `data` 参数即可

multipart/form-data

又是一个常见的 POST 数据提交的方式。我们使用表单上传文件的时候,必须将 <form> 表单的 enctype 设置为 multipart/form-data

  • multipart/form-data 的基础是 post 请求,即基于 post 请求来实现的

  • multipart/form-data 形式的 post 与普通 post 请求不同之处体现在 请求头请求体 两部分

请求头

必须包含 Content-Type 信息,且其值也必须规定为 multipart/form-data,同时还需要规定一个内容分割符用于分割请求体中不同参数的内容。具体的信息格式如下:

Content-Type: multipart/form-data; boundary=${bound}

其中 ${bound} 是一个占位符,代表我们规定的具体分隔符,可以自己任意规定,但为了避免和正常文本重复,尽量使用复杂一点的内容。如:

Content-Type: multipart/form-data; boundary=e0722295b17a922fdd0c86a3f2cdd47d

  • 请求体

    --${bound}
    Content-Disposition: form-data; name="username"
    
    admin
    --${bound}
    Content-Disposition: form-data; name="password"
    
    admin
    --${bound}
    Content-Disposition: form-data; name="file"; filename="cookie_me.py"
    
    ...
    --${bound}--
    

其中 ${bound} 就是之前请求头信息中的分隔符,两者需要保持一致。

另外,请求体被 分隔符 划分为 3 个部分,每个部分就是一个参数的键值描述(类似普通post请求中 k1=v1 部分),但对参数信息的描述可以比普通post请求更加丰富,这就是为什么 multipart/form-data 能发送文件的原因。

每一个部分都是以 --${bound} 开始的,接着是该部分内容的描述信息,然后是一个回车,最后是描述信息的具体内容,最后的分隔符会以 -- 结尾,表示请求体结束

form 表单

<form action="/" method="POST" enctype="multipart/form-data">
	...
</form>

Fiddler & Chrome Network 抓包分析

Python requests 模拟请求

# 仅传递文件
files = {
   'file': open('./test.py', 'r')}
response = requests.post('http://127.0.0.1', files=files)

# 仅传递参数
data = {
   
    'username': (None, 'admin'),
    'password': (None, 'admin')
}
response = requests.post('http://127.0.0.1', files=data)  # 此处为 files


# 文件 + 参数
data = {
   
    'username': 'admin',
    'password': 'admin'
}

files = {
   'file': open('./test.py', 'r')}
response = requests.post('http://127.0.0.1', files=files, data=data)

注意:不需要设置 Content-Type: multipart/form-data 请求头

此外,还可以采用 requests-toolbelt 库构建参数

application/json

由于 JSON 规范的流行,越来越多程序把 application/json 作为请求头,用来告诉服务端消息主题是序列化后的 JSON 字符串。除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数

JSON 格式支持比键值对复杂得多的结构化数据,可以视为 application/x-www-form-urlencoded 的升级版,特别适合 RESTful 接口

Fiddler & Chrome Network 抓包分析

Python requests 模拟请求

# 第一种方式
data = {
   
    'username': 'zzzzls',
    'password': 'zzzzls'
}
response = requests.post('http://127.0.0.1', json=data)

# 第二种方式
headers = {
   'Content-Type': 'application/json'}
data = {
   
    'username': 'zzzzls',
    'password': 'zzzzls'
}
response = requests.post('http://127.0.0.1', data=json.dumps(data), headers=headers)