feat(web基础): 新增零基础Web开发课程

This commit is contained in:
zhiye.sun
2026-08-20 16:14:29 +08:00
parent bf1f7042a9
commit 0c3d6402ea
19 changed files with 2005 additions and 8 deletions
+189
View File
@@ -0,0 +1,189 @@
# 第5-2课:URL与HTTP请求
## 一、本课目标
完成本课后,你能够:
1. 拆解统一资源定位符(Uniform Resource Locator,URL);
2. 解释HTTP请求行、请求头和请求体;
3. 理解路径参数与查询参数的用途差异;
4. 为常见操作选择GET、POST、PUT、PATCH或DELETE;
5. 使用Python标准库安全编码查询参数。
## 二、前置知识
- 已理解客户端与服务器;
- Python字典、函数和字符串;
- 不要求记忆HTTP报文的所有字段。
## 三、URL的组成
示例:
```text
https://api.example.com:443/books/10?detail=true#summary
```
| 部分 | 示例 | 作用 |
|---|---|---|
| 协议 | `https` | 约定通信方式 |
| 主机 | `api.example.com` | 定位服务器 |
| 端口 | `443` | 定位服务器中的服务 |
| 路径 | `/books/10` | 定位资源 |
| 查询字符串 | `detail=true` | 提供筛选或选项 |
| 片段 | `summary` | 通常只供客户端定位页面位置,不发送给服务器 |
HTTPS是经过传输层安全协议(Transport Layer Security,TLS)保护的HTTP。它保护传输过程,但不能自动修复错误的业务权限。
浏览器真正访问时还会涉及以下概念:
- 域名(Domain Name):便于人记忆的服务器名称,例如`api.example.com`;
- IP地址(Internet Protocol Address):网络用于定位设备或服务入口的地址;
- 域名系统(Domain Name System,DNS):把域名查询为可用于连接的IP地址;
- 端口(Port):同一台计算机上用于区分不同网络服务的编号。
可以把IP地址理解成办公楼地址,把端口理解成楼内具体窗口。这个类比不代表端口是物理接口,它只是操作系统管理网络连接时使用的数字。
HTTP默认端口通常是80,HTTPS默认端口通常是443。使用默认端口时,URL可以省略端口;使用开发服务器的8000等端口时通常需要明确写出。
## 四、浏览器如何根据URL定位请求目标
以`https://api.example.com:443/books/10?detail=true`为例:
1. 浏览器看到`https`,知道要使用受TLS保护的HTTP;
2. 通过DNS查询`api.example.com`对应的IP地址;
3. 向该地址的443端口建立连接;
4. 把`/books/10?detail=true`作为请求目标;
5. 在连接中发送HTTP请求;
6. 等待服务器返回HTTP响应。
实际网络还涉及缓存、代理、网关和连接复用。本阶段只建立主干认识,不展开网络工程细节。
## 五、HTTP请求结构
```http
POST /books HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{"title": "Python入门"}
```
- 请求行:请求方法、请求目标和HTTP版本;
- 请求头:描述内容类型、认证信息和客户端能力;
- 空行:分隔头部与请求体;
- 请求体:提交给服务器的数据,GET通常不依赖请求体。
逐行阅读这份请求:
1. `POST /books HTTP/1.1`表示使用POST方法访问`/books`;
2. `Host`说明目标主机和端口;
3. `Content-Type`说明请求体是JSON;
4. 空行表示请求头结束;
5. 最后一行JSON是提交给服务器的数据。
HTTP报文在网络中最终按字节传输。这里用文本形式展示,是为了让结构更容易阅读。
## 六、常用请求方法
| 方法 | 常见含义 | 图书示例 |
|---|---|---|
| GET | 查询 | 查询图书 |
| POST | 创建或触发处理 | 新增图书 |
| PUT | 整体替换 | 替换图书全部可修改信息 |
| PATCH | 局部修改 | 只修改价格 |
| DELETE | 删除 | 删除图书 |
方法表达意图,最终行为仍由服务器代码决定。GET应当是安全方法,即正常调用不应修改业务数据;重复执行PUT或DELETE通常应具有幂等性,即最终效果与执行一次相同。
安全和幂等是HTTP语义,不等同于权限安全:
- “GET是安全方法”表示它不应改变业务状态;
- “DELETE通常幂等”表示重复删除后最终仍是资源不存在;
- 它们都不表示接口无需登录或权限控制。
## 七、路径参数与查询参数
- `/books/10`中的`10`通常是路径参数,用于标识某一本书;
- `/books?keyword=Python&page=2`中的值是查询参数,用于筛选、排序或分页。
请求体通常承载新增或修改时的结构化数据,例如书名、价格和作者。不要把大量结构化内容全部塞进URL。
## 八、URL编码
URL中的空格、中文、`&`和`=`可能与URL结构规则冲突,因此需要百分号编码(Percent-encoding)或表单风格编码。
```python
urlencode({"keyword": "Python Web", "page": 1})
```
结果中的空格可能显示为`+`:
```text
keyword=Python+Web&page=1
```
客户端负责编码,服务器负责解码。不要先手工替换一次,再交给工具重复编码。
## 九、完整示例
`url_request_example.py`使用`urlsplit()`拆分URL,使用`parse_qs()`读取查询参数,使用`urlencode()`处理空格等特殊字符。
## 十、运行方法
```powershell
cd D:\Code\Python\05_web基础\5_2_URL与HTTP请求
python url_request_example.py
```
关键输出包括协议、主机、端口、路径、查询参数,以及编码后的GET请求行。
## 十一、代码执行顺序
1. `main()`准备一条完整URL;
2. `parse_url()`使用`urlsplit()`拆分结构;
3. `parse_qs()`把查询字符串转换为字典;
4. 程序分别输出协议、主机、端口、路径和查询参数;
5. `build_request_target()`使用`urlencode()`编码参数;
6. 程序输出一条简化请求行和请求头。
`api.example.com`是教学示例域名,程序只解析字符串,不会访问该网站。
## 十二、常见错误
### 12.1 手工拼接查询字符串
空格、中文、`&`和`=`具有特殊含义,应交给`urlencode()`等工具编码。
### 12.2 用POST处理所有操作
程序可能运行,但接口意图模糊,缓存、重试、权限和文档也更难设计。
### 12.3 在URL中传密码
URL容易进入浏览历史和日志。密码及令牌不应放入查询字符串。
### 12.4 混淆域名、IP和端口
域名需要经过DNS解析,IP用于网络定位,端口用于区分服务。它们有关联,但不是同一个值。
### 12.5 认为HTTPS代表接口一定可信
HTTPS主要保护传输过程。客户端仍要确认访问的域名,服务器仍要进行身份认证、权限和输入校验。
## 十三、课堂练习
打开`practice.py`,完成URL解析和请求方法选择练习。
## 十四、本课小结
URL回答“向哪里请求”,HTTP方法回答“想做什么”,请求头描述消息,请求体携带提交的数据。
## 十五、验收标准
- 能拆解完整URL;
- 能解释域名、DNS、IP和端口的基本关系;
- 能解释HTTP请求的主要部分;
- 能区分路径参数和查询参数;
- 能为常见增删改查选择请求方法;
- 示例运行结果符合预期。
@@ -0,0 +1,37 @@
# 第5-2课练习:URL与HTTP请求
#
# 本文件只提供题目、预期结果、自查清单和验收标准,不包含参考答案或代码骨架。
# 练习一:URL分析器
# 1. 使用urllib.parse.urlsplit()解析一个完整URL。
# 2. 分别输出协议、主机、端口、路径、查询字符串和片段。
# 3. 使用parse_qs()解析查询参数。
# 4. 测试包含中文关键字的URL编码结果。
# 练习二:为业务选择请求方法
# 为图书查询、新增、整体修改、局部修改和删除分别选择HTTP方法。
# 在注释中解释每种选择,不需要发送真实网络请求。
# 预期关键输出:
# 协议:https
# 主机:localhost
# 端口:8000
# 路径:/books/10
# 查询参数:{'detail': ['true']}
# 自查清单:
# 1. 是否区分主机、端口、路径和查询参数?
# 2. 是否使用工具编码查询参数,而不是手工拼接特殊字符?
# 3. 是否理解GET通常读取资源,POST通常创建资源?
# 4. 是否知道请求头和请求体不是同一部分?
# 最终验收标准:
# 1. practice.py通过语法检查并可独立运行;
# 2. URL每部分解析正确;
# 3. 中文和空格经过正确编码;
# 4. 能为五种图书操作选择合理请求方法。
@@ -0,0 +1,38 @@
"""第5-2课标准示例:解析URL并构造HTTP请求信息。"""
from urllib.parse import parse_qs, urlencode, urlsplit
def parse_url(url: str) -> None:
"""拆分URL,并把查询字符串转换成便于使用的字典。"""
parts = urlsplit(url)
query_parameters = parse_qs(parts.query)
print(f"协议:{parts.scheme}")
print(f"主机:{parts.hostname}")
print(f"端口:{parts.port}")
print(f"路径:{parts.path}")
print(f"查询参数:{query_parameters}")
def build_request_target(path: str, parameters: dict) -> str:
"""把路径和查询参数编码成请求目标。"""
return f"{path}?{urlencode(parameters)}"
def main() -> None:
"""展示URL各部分以及一份简化的GET请求。"""
url = "https://api.example.com:443/books?keyword=Python&page=2"
parse_url(url)
request_target = build_request_target(
"/books",
{"keyword": "Python Web", "page": 1},
)
print(f"请求行:GET {request_target} HTTP/1.1")
print("请求头:Accept: application/json")
if __name__ == "__main__":
main()