第 3 章:HTTP 协议详解
📌 本章摘要:HTTP 是 Web 开发的基石,本章从 HTTP 请求与响应的结构讲起,带你认识请求行、请求头、请求体与状态码;随后对比 GET 与 POST 的差异、详解 Content-Type 的常见格式,并介绍 Cookie 与 Session 如何解决 HTTP 的无状态问题;最后用 Python 的 requests 库动手模拟真实请求,帮你直观理解一次完整的请求-响应过程。
3.1 HTTP 是什么?
HTTP(HyperText Transfer Protocol,超文本传输协议)是 Web 开发的基石。浏览器和服务器之间的每一次通信,都遵循 HTTP 协议。
理解 HTTP,就是理解 Web 开发的本质。Flask 和 Django 的所有功能,说到底都是在处理 HTTP 请求和生成 HTTP 响应。
3.2 HTTP 请求结构
一个 HTTP 请求由四部分组成:
POST /api/users HTTP/1.1 ← 请求行 Host: localhost:5000 ← 请求头 Content-Type: application/json Authorization: Bearer abc123 ← 空行(分隔头和体) {"name": "小明", "age": 18} ← 请求体请求行包含三个信息:
- 方法:
GET、POST、PUT、DELETE等 - 路径:
/api/users - 协议版本:
HTTP/1.1
常见请求方法:
| 方法 | 用途 | 示例 |
|---|---|---|
GET | 获取资源 | 打开网页、查询数据 |
POST | 创建资源 | 提交表单、新建用户 |
PUT | 更新资源(整体替换) | 修改用户信息 |
DELETE | 删除资源 | 删除文章 |
PATCH | 部分更新 | 只修改用户名 |
常见请求头:
| 请求头 | 含义 | 示例 |
|---|---|---|
Host | 服务器地址 | localhost:5000 |
Content-Type | 请求体数据格式 | application/json |
Authorization | 认证信息 | Bearer <token> |
User-Agent | 客户端信息 | Mozilla/5.0... |
Cookie | 会话信息 | session_id=abc123 |
Accept | 期望的响应格式 | application/json |
3.3 HTTP 响应结构
服务器返回的响应也有固定结构:
HTTP/1.1 200 OK ← 状态行 Content-Type: application/json ← 响应头 Content-Length: 32 ← 空行 {"message": "success", "id": 1} ← 响应体状态码分类:
| 范围 | 含义 | 常见状态码 |
|---|---|---|
| 1xx | 信息 | 100 Continue |
| 2xx | 成功 | 200 OK、201 Created |
| 3xx | 重定向 | 301 永久重定向、302 临时重定向 |
| 4xx | 客户端错误 | 400 Bad Request、401 Unauthorized、404 Not Found |
| 5xx | 服务端错误 | 500 Internal Server Error、503 Service Unavailable |
| 常见响应头: |
| 响应头 | 含义 | 示例值 |
|---|---|---|
Content-Type | 响应体格式 | application/json |
Content-Length | 响应体长度(字节) | 32 |
Set-Cookie | 设置 Cookie | session_id=abc123 |
Location | 重定向地址 | /login |
Cache-Control | 缓存策略 | no-cache |
你一定要记住的状态码:
200# OK —— 请求成功201# Created —— 资源创建成功(POST 创建新资源时返回)400# Bad Request —— 请求参数有误401# Unauthorized —— 未登录/未认证403# Forbidden —— 已登录但无权限404# Not Found —— 资源不存在405# Method Not Allowed —— 请求方法不允许(如对 GET 接口发 POST)500# Internal Server Error —— 服务器内部错误💡 记忆技巧
- 2xx = 成功(“好的”)
- 4xx = 你的问题(客户端搞错了)
- 5xx = 我的问题(服务器出### 3.4 RESTful API 设计规范规范
RESTful 是一种基于 HTTP 方法的接口设计风格,核心思想是把 URL 当作资源,用 HTTP 方法表达对资源的操作。同一个 URL 配合不同方法,就能完成增删改查。
方法与 URL 的对应关系:
| HTTP 方法 | 操作 | URL 示例 | 说明 |
|---|---|---|---|
GET | 查询资源 | GET /users | 获取用户列表 |
GET | 查询单个资源 | GET /users/1 | 获取 id 为 1 的用户 |
POST | 创建资源 | POST /users | 新建一个用户 |
PUT | 整体更新 | PUT /users/1 | 整体替换 id 为 1 的用户 |
PATCH | 部分更新 | PATCH /users/1 | 只修改 id 为 1 的用户的某个字段 |
DELETE | 删除资源 | DELETE /users/1 | 删除 id 为 1 的用户 |
状态码使用规范:
| 状态码 | 适用场景 | 示例 |
|---|---|---|
200 OK | 查询成功 | GET /users返回用户列表 |
201 Created | 创建成功 | POST /users新建用户成功 |
204 No Content | 删除成功(无返回体) | DELETE /users/1删除成功 |
400 Bad Request | 参数错误 | 缺少必填字段、格式不对 |
404 Not Found | 资源不存在 | GET /users/999用户不存在 |
用户管理接口设计示例:
| 功能 | 方法 + URL | 成功状态码 | 失败状态码 |
|---|---|---|---|
| 获取用户列表 | GET /users | 200 | 400 |
| 获取单个用户 | GET /users/1 | 200 | 404 |
| 新建用户 | POST /users | 201 | 400 |
| 整体更新用户 | PUT /users/1 | 200 | 400、404 |
| 部分更新用户 | PATCH /users/1 | 200 | 400、404 |
| 删除用户 | DELETE /users/1 | 204 | 404 |
💡 提示:RESTful 的 URL 只描述资源(名词),操作交给 HTTP 方法表达。例如用
POST /users而不是POST /createUser,用DELETE /users/1而不是 `GET /deleteUser?id=1### 3.5 GET 与 POST 的区别的区别
这是初学者最常混淆的概念:
| 对比项 | GET | POST |
|---|---|---|
| 用途 | 获取数据 | 提交数据 |
| 参数位置 | URL 中(?name=小明) | 请求体中 |
| 长度限制 | 有(URL 长度限制,约 2KB) | 无(理论上无限制) |
| 可见性 | 参数显示在地址栏 | 参数不在地址栏显示 |
| 缓存 | 可被浏览器缓存 | 不缓存 |
| 安全性 | 较低(参数暴露在 URL) | 较高(参数在体中) |
GET 请求示例:
GET /search?q=python&page=1 HTTP/1.1 Host: localhost:5000POST 请求示例:
POST /login HTTP/1.1 Host: localhost:5000 Content-Type: application/x-www-form-urlencoded username=admin&password=1### 3.6 Content-Type 详解详解解 `Content-Type` 告诉服务器/浏览器数据的格式是什么。Web 开发中最常用的几种: | Content-Type | 格式 | 示例 | | --- | --- | --- | | `text/html` | HTML 页面 | `<html>...</html>` | | `application/json` | JSON 数据 | `{"name": "小明"}` | | `application/x-www-form-urlencoded` | 表单数据 | `name=小明&age=18` | | `multipart/form-data` | 文件上传 | 二进制数据 | | `text/plain` | 纯文本 | `Hello### 3.7 Cookie 与 SessiononnHTTP 是无状态协议——每次请求都是独立的,服务器不会自动"记住"你是谁。但很多时候我们需要记住用户的状态(比如登录状态),这就需要 Cookie 和 Session。
Cookie
- 存储在浏览器端的小段数据
- 每次请求自动带上
- 大小限制约 4KB
- 可设置过期时间
Session
- 存储在服务器端的数据
- 通过 Cookie 中的
session_id关联 - 更安全,数据不暴露给用户
登录流程:
- 用户提交用户名+密码
- 服务器验证成功后,创建 Session,存储用户信息
- 服务器返回响应,Set-Cookie: session_id=abc123
- 浏览器保存 Cookie
- 后续请求自动带上 Cookie: session_id=abc123
- 服务器根据 session_id 找到 Session,识别用户身份
下面是完整的登录交互时序图:
在学习写 Web 服务器之前,先用 Python 当"客户端"发送 HTTP 请求,直观感受 HTTP 协议。
# 先安装 requests 库pipinstallrequestsimportrequests# GET 请求response=requests.get("https://httpbin.org/get",params={"name":"小明","age":18})print(f"状态码:{response.status_code}")print(f"响应头:{response.headers['Content-Type']}")print(f"响应体:{response.json()}")输出:
状态码:200 响应头:application/json 响应体:{'args': {'age': '18', 'name': '小明'}, 'headers': {...}, ...}# POST 请求(提交 JSON 数据)response=requests.post("https://httpbin.org/post",json={"username":"admin","password":"123456"})print(f"状态码:{response.status_code}")print(f"服务器收到的数据:{response.json()['json']}")输出:
状态码:200 服务器收到的数据:{'username': 'admin', 'password': '123456'}# 处理不同的响应response=requests.get("https://httpbin.org/status/404")print(f"状态码:{response.status_code}")# 404response=requests.get("https://httpbin.org/status/500")print(f"状态码:{response.status_code}")# 500# 获取响应文本response=requests.get("https://httpbin.org/html")print(response.text)# 返回 HTML 内容``**模拟带 Cookie 的登录流程:**```pythonimportrequests# 1. 创建 Session 对象(自动保存并携带 Cookie)session=requests.Session()# 2. 登录:POST /login 获取 session_idlogin_url="https://httpbin.org/post"login_data={"username":"admin","password":"123456"}login_resp=session.post(login_url,json=login_data)print(f"登录状态码:{login_resp.status_code}")print(f"登录响应体:{login_resp.json()['json']}")# 3. 查看服务器通过 Set-Cookie 下发的 session_idprint(f"服务器下发的 Cookie:{session.cookies.get_dict()}")# 4. 携带 Cookie 访问需要认证的接口profile_resp=session.get("https://httpbin.org/cookies")print(f"访问个人资料状态码:{profile_resp.status_code}")print(f"服务器识别到的 Cookie:{profile_resp.json()['cookies']}")# 5. 对比:不使用 Session 时,Cookie 不会自动携带no_session_resp=requests.get("https://httpbin.org/cookies")print(f"不带 Cookie 的响应:{no_session_resp.json()['cookies']}")输出:
登录状态码:200 登录响应体:{'username': 'admin', 'password': '123456'} 服务器下发的 Cookie:{} 访问个人资料状态码:200 服务器识别到的 Cookie:{} 不带 Cookie 的响应:{}💡 说明:
httpbin.org是公开测试接口,不会真正下发session_id。实际开发中,服务器会在登录成功后通过Set-Cookie响应头下发session_id,而requests.Session()会自动保存该 Cookie,并在后续请求中自动携带——这正是浏览器处理 Cookie 的方式,也是 Session 与 Cookie 配合工作### 3.9 动手试一试一试一试
用 Python 写一个脚本,向测试 API 发送请求并打印完整信息:
importrequests url="https://httpbin.org/get"params={"course":"python","chapter":3}headers={"User-Agent":"MyPythonBot/1.0"}response=requests.get(url,params=params,headers=headers)print("="*50)print(f"请求 URL:{response.url}")print(f"状态码:{response.status_code}")print(f"响应时间:{response.elapsed.total_seconds()}秒")print("="*50)print("响应头:")forkey,valueinresponse.headers.items():print(f"{key}:{value}")print("="*50)print("响应体(JSON):")data=response.json()print(f" 服务器收到的参数:{data['args']}")print(f" 服务器收到的请求头:{data['headers']}")print(f" 你的 IP:{data['origin']}")运行这个脚本,你会清晰地看到一次完整的 HTTP 请求-响应过程。这也是后面调试 Web API 时### 3.10 常见问题排查排查排查
Q1:为什么 POST 请求的参数不在 URL 中?
因为 POST 请求的参数放在请求体里,而不是 URL 中。URL 只包含请求行中的路径和查询字符串,而 POST 的数据通过请求体传输,这样既避免了 URL 长度限制,也不会把敏感信息暴露在地址栏。如果你用浏览器开发者工具查看,POST 请求的参数会显示在 Payload 或 Form Data 标签页中。
Q2:如何查看浏览器发送的 HTTP 请求?
按F12打开开发者工具,切换到Network(网络)标签页,刷新页面或执行操作后,点击任意一条请求即可查看详细信息:
- Headers:查看请求行、请求头和响应头
- Payload / Request:查看 POST 请求体内容
- Preview / Response:查看响应体内容
这是调试 Web 应用最常用的手段,建议熟练掌握。
Q3:Cookie 和 Session 哪个更安全?
Session 更安全。Session 数据存储在服务器端,客户端只能拿到一个随机的session_id,无法直接看到或篡改服务器上的用户数据。而 Cookie 存储在浏览器端,用户可以直接查看甚至修改内容,因此不要把密码等敏感信息直接放进 Cookie。实际开发中通常两者配合使用:Session 保存核心数据,Cookie 只保存关联用的session_id。
Q4:Content-Type 设置错误会怎样?
服务器会按错误的格式解析请求体,导致数据接收失败。例如:前端用application/json发送 JSON,但后端按application/x-www-form-urlencoded解析,就会拿不到参数。解决方法是前后端约定一致:发送 JSON 时设置Content-Type: application/json,发送表单时设置application/x-www-form-urlencoded,并在后端用对应的解析方式处理。
Q5:请求返回 404 或 500 分别代表什么?
- 404 Not Found:请求的 URL 路径不存在,通常是路由写错或资源已被删除。检查请求路径是否与后端路由一致。
- 500 Internal Server Error:服务器内部出错,通常是后端代码抛异常。查看服务器日志定位具体错误行。
如果遇到 405,则说明请求方法不对,例如对只支持 GET 的接口发送了 POST 请求。
。