今天写一篇全面、专业的 Python HTTP CRUD 开发博客文章,这篇文章涵盖了从基础到进阶的内容。
Python HTTP CRUD 完全指南:从入门到实战
用 Python 构建健壮的 RESTful API 客户端,掌握增删改查的核心技能
前言
在现代 Web 开发中,CRUD(Create、Read、Update、Delete)操作是几乎所有应用程序的基础。无论您是开发后端服务、编写自动化脚本,还是构建数据爬虫,掌握如何使用 Python 进行 HTTP CRUD 操作都是一项必备技能。
本文将带您从零开始,系统学习如何使用 Python 的 requests 库实现完整的 HTTP CRUD 操作,并涵盖错误处理、身份验证、性能优化等进阶话题。
你将学到什么
- ✅ Python
requests库的核心用法 - ✅ RESTful API 的设计理念与最佳实践
- ✅ 完整的 CRUD 操作实现(GET、POST、PUT、DELETE)
- ✅ 专业的错误处理与异常捕获
- ✅ 身份验证与安全实践(Token、Basic Auth)
- ✅ 超时设置、重试机制与性能优化
- ✅ 单元测试与代码质量保障
- ✅ 实战项目:构建一个完整的任务管理客户端
第一章:环境准备与基础概念
1.1 安装 requests 库
requests 是 Python 中最流行的 HTTP 客户端库,以其简洁优雅的 API 著称。
bash
# 基础安装
pip install requests
# 若使用虚拟环境(推荐)
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install requests
1.2 验证安装
python
import requests
print(requests.__version__) # 应输出 2.28.0 或更高版本
1.3 RESTful API 基础
在开始编码前,理解 RESTful API 的核心概念至关重要:
| HTTP 方法 | CRUD 操作 | 幂等性 | 安全性 | 典型用途 |
|---|---|---|---|---|
| GET | Read | ✅ 是 | ✅ 是 | 获取资源 |
| POST | Create | ❌ 否 | ❌ 否 | 创建资源 |
| PUT | Update/Replace | ✅ 是 | ❌ 否 | 完整更新 |
| PATCH | Update/Modify | ❌ 否 | ❌ 否 | 部分更新 |
| DELETE | Delete | ✅ 是 | ❌ 否 | 删除资源 |
💡 关键概念:
- 幂等性:多次执行相同请求,效果相同(不会产生副作用)
- 安全性:操作不会修改服务器状态(GET 是安全的)
第二章:基础 CRUD 实现
2.1 完整的 CRUD 类封装
下面是一个生产级的 CRUD 客户端实现:
python
import requests
import json
from typing import Optional, Dict, Any, List, Union
from urllib.parse import urljoin
class RESTClient:
"""
一个健壮的 RESTful API 客户端
支持完整的 CRUD 操作、错误处理和日志记录
"""
def __init__(self, base_url: str, timeout: int = 30,
headers: Optional[Dict[str, str]] = None):
"""
初始化客户端
:param base_url: API 基础 URL
:param timeout: 请求超时时间(秒)
:param headers: 默认请求头
"""
self.base_url = base_url.rstrip('/')
self.timeout = timeout
self.session = requests.Session()
# 设置默认请求头
default_headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'Python-RESTClient/1.0'
}
if headers:
default_headers.update(headers)
self.session.headers.update(default_headers)
def _build_url(self, endpoint: str) -> str:
"""构建完整的 URL"""
return urljoin(self.base_url + '/', endpoint.lstrip('/'))
def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
"""
处理响应,统一解析 JSON 并处理错误
"""
try:
response.raise_for_status()
if response.status_code == 204: # No Content
return {'status': 'success', 'message': '操作成功'}
return response.json() if response.text else {}
except requests.exceptions.HTTPError as e:
error_msg = f"HTTP 错误 {response.status_code}: {response.text}"
raise APIError(error_msg, response.status_code, response)
except json.JSONDecodeError:
raise APIError("响应不是有效的 JSON 格式", response.status_code, response)
# ============ CRUD 方法 ============
def get(self, endpoint: str, params: Optional[Dict] = None,
**kwargs) -> Union[Dict, List]:
"""
GET 请求:获取资源
:param endpoint: API 端点
:param params: 查询参数(分页、过滤等)
:return: 响应数据
"""
url = self._build_url(endpoint)
response = self.session.get(
url,
params=params,
timeout=kwargs.get('timeout', self.timeout)
)
return self._handle_response(response)
def post(self, endpoint: str, data: Optional[Dict] = None,
**kwargs) -> Dict:
"""
POST 请求:创建资源
:param endpoint: API 端点
:param data: 请求体数据
:return: 创建的资源数据
"""
url = self._build_url(endpoint)
response = self.session.post(
url,
json=data,
timeout=kwargs.get('timeout', self.timeout)
)
return self._handle_response(response)
def put(self, endpoint: str, data: Optional[Dict] = None,
**kwargs) -> Dict:
"""
PUT 请求:完整更新资源
:param endpoint: API 端点(通常包含资源 ID)
:param data: 完整的更新数据
:return: 更新后的资源数据
"""
url = self._build_url(endpoint)
response = self.session.put(
url,
json=data,
timeout=kwargs.get('timeout', self.timeout)
)
return self._handle_response(response)
def patch(self, endpoint: str, data: Optional[Dict] = None,
**kwargs) -> Dict:
"""
PATCH 请求:部分更新资源
:param endpoint: API 端点(通常包含资源 ID)
:param data: 需要更新的字段
:return: 更新后的资源数据
"""
url = self._build_url(endpoint)
response = self.session.patch(
url,
json=data,
timeout=kwargs.get('timeout', self.timeout)
)
return self._handle_response(response)
def delete(self, endpoint: str, **kwargs) -> Dict:
"""
DELETE 请求:删除资源
:param endpoint: API 端点(包含资源 ID)
:return: 删除结果
"""
url = self._build_url(endpoint)
response = self.session.delete(
url,
timeout=kwargs.get('timeout', self.timeout)
)
return self._handle_response(response)
# ============ 便捷方法 ============
def get_by_id(self, resource: str, id: Union[int, str]) -> Dict:
"""通过 ID 获取单个资源"""
return self.get(f"{resource}/{id}")
def list_all(self, resource: str, **params) -> List:
"""获取资源列表"""
return self.get(resource, params=params)
def create(self, resource: str, data: Dict) -> Dict:
"""创建资源"""
return self.post(resource, data)
def update(self, resource: str, id: Union[int, str], data: Dict) -> Dict:
"""完整更新资源"""
return self.put(f"{resource}/{id}", data)
def partial_update(self, resource: str, id: Union[int, str], data: Dict) -> Dict:
"""部分更新资源"""
return self.patch(f"{resource}/{id}", data)
def delete_one(self, resource: str, id: Union[int, str]) -> Dict:
"""删除资源"""
return self.delete(f"{resource}/{id}")
class APIError(Exception):
"""自定义 API 异常"""
def __init__(self, message: str, status_code: int = None,
response: requests.Response = None):
self.message = message
self.status_code = status_code
self.response = response
super().__init__(message)
def __str__(self):
if self.status_code:
return f"[{self.status_code}] {self.message}"
return self.message
2.2 使用示例
python
# 初始化客户端
client = RESTClient("https://jsonplaceholder.typicode.com")
# ===== 1. 获取所有文章 =====
try:
posts = client.list_all("posts")
print(f"获取到 {len(posts)} 篇文章")
print(f"第一篇文章标题: {posts[0]['title']}")
except APIError as e:
print(f"获取失败: {e}")
# ===== 2. 创建新文章 =====
new_post = {
"title": "Python HTTP CRUD 完全指南",
"body": "这是一篇关于 Python HTTP CRUD 的全面教程...",
"userId": 1
}
try:
created = client.create("posts", new_post)
print(f"✅ 创建成功,ID: {created['id']}")
except APIError as e:
print(f"创建失败: {e}")
# ===== 3. 获取单个文章 =====
try:
post = client.get_by_id("posts", 1)
print(f"文章标题: {post['title']}")
except APIError as e:
print(f"获取失败: {e}")
# ===== 4. 更新文章 =====
update_data = {
"title": "更新后的标题",
"body": "更新后的内容",
"userId": 1
}
try:
updated = client.update("posts", 1, update_data)
print(f"✅ 更新成功: {updated['title']}")
except APIError as e:
print(f"更新失败: {e}")
# ===== 5. 部分更新 =====
patch_data = {"title": "部分更新后的标题"}
try:
patched = client.partial_update("posts", 1, patch_data)
print(f"✅ 部分更新成功: {patched['title']}")
except APIError as e:
print(f"部分更新失败: {e}")
# ===== 6. 删除文章 =====
try:
result = client.delete_one("posts", 1)
print(f"✅ 删除成功: {result}")
except APIError as e:
print(f"删除失败: {e}")
第三章:高级功能与最佳实践
3.1 身份验证
3.1.1 Token 认证(Bearer Token)
python
class AuthenticatedRESTClient(RESTClient):
"""支持 Token 认证的 REST 客户端"""
def __init__(self, base_url: str, token: str = None, **kwargs):
headers = kwargs.pop('headers', {})
if token:
headers['Authorization'] = f'Bearer {token}'
super().__init__(base_url, headers=headers, **kwargs)
def set_token(self, token: str):
"""更新 Token"""
self.session.headers['Authorization'] = f'Bearer {token}'
def login(self, username: str, password: str, login_endpoint: str = '/auth/login'):
"""
登录获取 Token
"""
response = self.post(login_endpoint, data={
'username': username,
'password': password
})
if 'token' in response:
self.set_token(response['token'])
return response
raise APIError("登录响应中未包含 token")
3.1.2 Basic Auth 认证
python
import requests
from requests.auth import HTTPBasicAuth
class BasicAuthRESTClient(RESTClient):
"""支持 Basic Auth 的 REST 客户端"""
def __init__(self, base_url: str, username: str = None,
password: str = None, **kwargs):
super().__init__(base_url, **kwargs)
if username and password:
self.session.auth = HTTPBasicAuth(username, password)
def set_auth(self, username: str, password: str):
self.session.auth = HTTPBasicAuth(username, password)
3.2 请求重试机制
python
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class RetryRESTClient(RESTClient):
"""支持自动重试的 REST 客户端"""
def __init__(self, base_url: str, retries: int = 3,
backoff_factor: float = 0.5, **kwargs):
super().__init__(base_url, **kwargs)
# 配置重试策略
retry_strategy = Retry(
total=retries,
backoff_factor=backoff_factor,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
3.3 请求日志与调试
python
import logging
import time
from functools import wraps
class LoggingRESTClient(RESTClient):
"""带日志记录的 REST 客户端"""
def __init__(self, base_url: str, log_level: int = logging.INFO, **kwargs):
super().__init__(base_url, **kwargs)
self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
self.logger.setLevel(log_level)
if not self.logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
self.logger.addHandler(handler)
def _log_request(self, method: str, url: str, **kwargs):
"""记录请求信息"""
self.logger.info(f"➡️ {method} {url}")
if 'params' in kwargs and kwargs['params']:
self.logger.debug(f" Params: {kwargs['params']}")
if 'json' in kwargs and kwargs['json']:
self.logger.debug(f" Body: {kwargs['json']}")
def _log_response(self, response: requests.Response, elapsed: float):
"""记录响应信息"""
self.logger.info(
f"⬅️ {response.status_code} {response.url} "
f"({elapsed:.3f}s)"
)
def get(self, endpoint: str, **kwargs):
url = self._build_url(endpoint)
self._log_request('GET', url, **kwargs)
start = time.time()
response = self.session.get(url, **kwargs)
elapsed = time.time() - start
self._log_response(response, elapsed)
return self._handle_response(response)
# 其他方法类似 ...
3.4 并发请求
python
import asyncio
import aiohttp
from concurrent.futures import ThreadPoolExecutor, as_completed
class AsyncRESTClient:
"""异步 REST 客户端(使用 aiohttp)"""
def __init__(self, base_url: str, max_connections: int = 10):
self.base_url = base_url
self.max_connections = max_connections
async def get_async(self, endpoint: str, params: dict = None):
"""异步 GET 请求"""
url = f"{self.base_url}/{endpoint.lstrip('/')}"
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response:
return await response.json()
async def get_many(self, endpoints: list):
"""并发获取多个资源"""
tasks = [self.get_async(endpoint) for endpoint in endpoints]
return await asyncio.gather(*tasks, return_exceptions=True)
# 使用示例
async def fetch_all_posts():
client = AsyncRESTClient("https://jsonplaceholder.typicode.com")
post_ids = [f"posts/{i}" for i in range(1, 11)]
results = await client.get_many(post_ids)
return [r for r in results if not isinstance(r, Exception)]
# 运行异步代码
# asyncio.run(fetch_all_posts())
3.5 文件上传与下载
python
class FileRESTClient(RESTClient):
"""支持文件操作的 REST 客户端"""
def upload_file(self, endpoint: str, file_path: str,
field_name: str = 'file', extra_data: dict = None):
"""
上传文件
:param endpoint: 上传端点
:param file_path: 本地文件路径
:param field_name: 表单字段名
:param extra_data: 额外表单数据
"""
url = self._build_url(endpoint)
files = {field_name: (file_path.split('/')[-1], open(file_path, 'rb'))}
data = extra_data or {}
# 临时移除 JSON 内容类型
original_headers = self.session.headers.copy()
self.session.headers.pop('Content-Type', None)
try:
response = self.session.post(url, files=files, data=data)
return self._handle_response(response)
finally:
self.session.headers.update(original_headers)
def download_file(self, endpoint: str, save_path: str, **kwargs):
"""下载文件"""
url = self._build_url(endpoint)
response = self.session.get(url, stream=True, **kwargs)
response.raise_for_status()
with open(save_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
return save_path
第四章:单元测试
4.1 使用 unittest 测试
python
import unittest
from unittest.mock import Mock, patch
from requests.exceptions import RequestException
class TestRESTClient(unittest.TestCase):
def setUp(self):
"""每个测试方法执行前的准备工作"""
self.client = RESTClient("https://api.example.com")
@patch('requests.Session.get')
def test_get_success(self, mock_get):
"""测试 GET 请求成功"""
# 模拟响应
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"id": 1, "name": "Test"}
mock_response.raise_for_status.return_value = None
mock_get.return_value = mock_response
# 执行测试
result = self.client.get("items/1")
# 断言
self.assertEqual(result, {"id": 1, "name": "Test"})
mock_get.assert_called_once_with(
"https://api.example.com/items/1",
params=None,
timeout=30
)
@patch('requests.Session.get')
def test_get_404(self, mock_get):
"""测试 GET 请求 404 错误"""
mock_response = Mock()
mock_response.status_code = 404
mock_response.text = "Not Found"
mock_response.raise_for_status.side_effect = RequestException("404")
mock_get.return_value = mock_response
with self.assertRaises(APIError):
self.client.get("items/999")
@patch('requests.Session.post')
def test_create_success(self, mock_post):
"""测试 POST 请求成功"""
mock_response = Mock()
mock_response.status_code = 201
mock_response.json.return_value = {"id": 2, "name": "New Item"}
mock_response.raise_for_status.return_value = None
mock_post.return_value = mock_response
data = {"name": "New Item"}
result = self.client.post("items", data)
self.assertEqual(result["id"], 2)
mock_post.assert_called_once_with(
"https://api.example.com/items",
json=data,
timeout=30
)
if __name__ == "__main__":
unittest.main()
4.2 使用 pytest 测试
python
import pytest
from unittest.mock import Mock
@pytest.fixture
def client():
"""提供 REST 客户端实例"""
return RESTClient("https://api.example.com")
def test_get_all_items(client, mocker):
"""测试获取所有项目"""
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = [{"id": 1}, {"id": 2}]
mock_response.raise_for_status.return_value = None
mocker.patch.object(client.session, 'get', return_value=mock_response)
result = client.list_all("items")
assert len(result) == 2
assert result[0]["id"] == 1
def test_delete_item(client, mocker):
"""测试删除项目"""
mock_response = Mock()
mock_response.status_code = 204
mock_response.text = ""
mock_response.raise_for_status.return_value = None
mocker.patch.object(client.session, 'delete', return_value=mock_response)
result = client.delete_one("items", 1)
assert result["status"] == "success"
@pytest.mark.parametrize("status_code,expected", [
(200, True),
(404, False),
(500, False),
])
def test_response_handling(client, status_code, expected):
"""测试不同状态码的响应处理"""
# 测试逻辑...
pass
第五章:实战项目:任务管理客户端
5.1 项目结构
bash
task_manager/
├── __init__.py
├── client.py # REST 客户端
├── models.py # 数据模型
├── service.py # 业务逻辑
├── cli.py # 命令行接口
├── config.py # 配置管理
└── tests/
├── __init__.py
├── test_client.py
└── test_service.py
5.2 数据模型
python
# models.py
from dataclasses import dataclass, asdict
from datetime import datetime
from typing import Optional, List
@dataclass
class Task:
"""任务数据模型"""
id: Optional[int] = None
title: str = ""
description: str = ""
completed: bool = False
priority: int = 1 # 1-5
due_date: Optional[str] = None
created_at: Optional[str] = None
updated_at: Optional[str] = None
def to_dict(self) -> dict:
"""转换为字典"""
return {k: v for k, v in asdict(self).items() if v is not None}
def is_overdue(self) -> bool:
"""检查任务是否过期"""
if not self.due_date:
return False
due = datetime.fromisoformat(self.due_date)
return due < datetime.now() and not self.completed
5.3 业务服务层
python
# service.py
from typing import List, Optional
from client import RESTClient
from models import Task
from config import Config
class TaskService:
"""任务管理服务"""
def __init__(self, config: Config = None):
self.config = config or Config()
self.client = RESTClient(
self.config.api_url,
headers=self.config.headers
)
self._cache = {}
def get_all_tasks(self, filter_completed: Optional[bool] = None) -> List[Task]:
"""获取所有任务,支持过滤"""
params = {}
if filter_completed is not None:
params['completed'] = str(filter_completed).lower()
data = self.client.list_all("tasks", **params)
return [Task(**item) for item in data]
def get_task(self, task_id: int) -> Optional[Task]:
"""获取单个任务"""
try:
data = self.client.get_by_id("tasks", task_id)
return Task(**data)
except APIError as e:
if e.status_code == 404:
return None
raise
def create_task(self, title: str, description: str = "",
priority: int = 1, due_date: str = None) -> Task:
"""创建新任务"""
task = Task(
title=title,
description=description,
priority=priority,
due_date=due_date
)
data = self.client.create("tasks", task.to_dict())
return Task(**data)
def update_task(self, task_id: int, **updates) -> Task:
"""更新任务(部分更新)"""
data = self.client.partial_update("tasks", task_id, updates)
return Task(**data)
def complete_task(self, task_id: int) -> Task:
"""标记任务为已完成"""
return self.update_task(task_id, completed=True)
def delete_task(self, task_id: int) -> bool:
"""删除任务"""
try:
self.client.delete_one("tasks", task_id)
return True
except APIError:
return False
def get_overdue_tasks(self) -> List[Task]:
"""获取过期的任务"""
tasks = self.get_all_tasks(filter_completed=False)
return [t for t in tasks if t.is_overdue()]
def get_statistics(self) -> dict:
"""获取任务统计信息"""
tasks = self.get_all_tasks()
total = len(tasks)
completed = sum(1 for t in tasks if t.completed)
overdue = sum(1 for t in tasks if t.is_overdue())
return {
'total': total,
'completed': completed,
'pending': total - completed,
'overdue': overdue,
'completion_rate': (completed / total * 100) if total > 0 else 0
}
5.4 命令行接口
python
# cli.py
import argparse
import sys
from datetime import datetime
from service import TaskService
from config import Config
class TaskCLI:
"""任务管理命令行界面"""
def __init__(self):
self.service = TaskService()
self.setup_parser()
def setup_parser(self):
"""配置命令行参数解析器"""
self.parser = argparse.ArgumentParser(
description="任务管理命令行工具",
usage="python cli.py <command> [options]"
)
subparsers = self.parser.add_subparsers(dest='command', help='子命令')
# list 命令
list_parser = subparsers.add_parser('list', help='列出所有任务')
list_parser.add_argument('--completed', action='store_true', help='显示已完成的任务')
list_parser.add_argument('--pending', action='store_true', help='仅显示未完成的任务')
list_parser.add_argument('--overdue', action='store_true', help='显示过期的任务')
# add 命令
add_parser = subparsers.add_parser('add', help='添加任务')
add_parser.add_argument('title', help='任务标题')
add_parser.add_argument('--description', '-d', help='任务描述')
add_parser.add_argument('--priority', '-p', type=int, default=1, choices=range(1, 6), help='优先级 (1-5)')
add_parser.add_argument('--due', help='截止日期 (YYYY-MM-DD)')
# get 命令
get_parser = subparsers.add_parser('get', help='查看任务详情')
get_parser.add_argument('id', type=int, help='任务 ID')
# complete 命令
complete_parser = subparsers.add_parser('complete', help='标记任务为已完成')
complete_parser.add_argument('id', type=int, help='任务 ID')
# delete 命令
delete_parser = subparsers.add_parser('delete', help='删除任务')
delete_parser.add_argument('id', type=int, help='任务 ID')
# stats 命令
subparsers.add_parser('stats', help='查看统计信息')
def run(self, args=None):
"""运行 CLI"""
args = self.parser.parse_args(args)
if not args.command:
self.parser.print_help()
return
# 路由到对应的处理方法
handler = getattr(self, f'cmd_{args.command}', None)
if handler:
handler(args)
else:
print(f"未知命令: {args.command}")
def cmd_list(self, args):
"""列出任务"""
if args.overdue:
tasks = self.service.get_overdue_tasks()
print(f"📅 过期任务 ({len(tasks)}):")
elif args.completed:
tasks = self.service.get_all_tasks(filter_completed=True)
print(f"✅ 已完成的任务 ({len(tasks)}):")
elif args.pending:
tasks = self.service.get_all_tasks(filter_completed=False)
print(f"⏳ 未完成的任务 ({len(tasks)}):")
else:
tasks = self.service.get_all_tasks()
print(f"📋 所有任务 ({len(tasks)}):")
for task in tasks:
status = "✅" if task.completed else "⏳"
overdue = "🔥" if task.is_overdue() else ""
print(f" {status} [{task.id}] {task.title} (优先级: {task.priority}) {overdue}")
def cmd_add(self, args):
"""添加任务"""
try:
task = self.service.create_task(
title=args.title,
description=args.description or "",
priority=args.priority,
due_date=args.due
)
print(f"✅ 任务已创建: [{task.id}] {task.title}")
except Exception as e:
print(f"❌ 创建失败: {e}")
def cmd_get(self, args):
"""查看任务详情"""
task = self.service.get_task(args.id)
if task:
print(f"\n📌 任务详情:")
print(f" ID: {task.id}")
print(f" 标题: {task.title}")
print(f" 描述: {task.description or '无'}")
print(f" 状态: {'✅ 已完成' if task.completed else '⏳ 未完成'}")
print(f" 优先级: {task.priority}")
print(f" 截止日期: {task.due_date or '无'}")
print(f" 创建时间: {task.created_at or '未知'}")
if task.is_overdue():
print(" ⚠️ 此任务已过期!")
else:
print(f"❌ 未找到 ID 为 {args.id} 的任务")
def cmd_complete(self, args):
"""标记任务完成"""
try:
task = self.service.complete_task(args.id)
print(f"✅ 任务已完成: [{task.id}] {task.title}")
except Exception as e:
print(f"❌ 操作失败: {e}")
def cmd_delete(self, args):
"""删除任务"""
confirm = input(f"⚠️ 确认删除任务 {args.id}? (y/N): ")
if confirm.lower() == 'y':
if self.service.delete_task(args.id):
print(f"✅ 任务 {args.id} 已删除")
else:
print(f"❌ 删除失败")
else:
print("操作已取消")
def cmd_stats(self, args):
"""显示统计信息"""
stats = self.service.get_statistics()
print("\n📊 任务统计:")
print(f" 总任务数: {stats['total']}")
print(f" 已完成: {stats['completed']}")
print(f" 未完成: {stats['pending']}")
print(f" 已过期: {stats['overdue']}")
print(f" 完成率: {stats['completion_rate']:.1f}%")
# 显示进度条
bar_len = 20
filled = int(stats['completion_rate'] / 100 * bar_len)
bar = "█" * filled + "░" * (bar_len - filled)
print(f" 进度: [{bar}] {stats['completion_rate']:.0f}%")
if __name__ == "__main__":
cli = TaskCLI()
cli.run()
5.5 配置文件
python
# config.py
import os
from typing import Dict, Optional
class Config:
"""配置管理"""
def __init__(self):
self.api_url = os.getenv('API_URL', 'https://api.example.com/v1')
self.api_key = os.getenv('API_KEY', '')
self.timeout = int(os.getenv('REQUEST_TIMEOUT', '30'))
self.max_retries = int(os.getenv('MAX_RETRIES', '3'))
@property
def headers(self) -> Dict[str, str]:
"""请求头"""
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
if self.api_key:
headers['Authorization'] = f'Bearer {self.api_key}'
return headers
5.6 使用示例
bash
# 添加任务
python cli.py add "完成项目报告" --description "撰写年度项目总结报告" --priority 3 --due 2026-09-30
# 列出所有任务
python cli.py list
# 列出未完成任务
python cli.py list --pending
# 查看任务详情
python cli.py get 1
# 标记任务完成
python cli.py complete 1
# 查看统计信息
python cli.py stats
# 删除任务
python cli.py delete 1
第六章:常见问题与解决方案
6.1 SSL 证书验证
python
# 跳过 SSL 验证(不推荐生产环境)
response = requests.get(url, verify=False)
# 使用自定义证书
response = requests.get(url, verify='/path/to/cert.pem')
# 在企业环境中,可能需要禁用警告
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
6.2 处理大文件响应
python
def download_large_file(url, save_path):
"""流式下载大文件"""
response = requests.get(url, stream=True)
response.raise_for_status()
with open(save_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
return save_path
6.3 处理重定向
python
# 允许重定向(默认)
response = requests.get(url, allow_redirects=True)
# 禁止重定向
response = requests.get(url, allow_redirects=False)
6.4 会话管理(保持 Cookie)
python
# 使用 Session 自动管理 Cookie
session = requests.Session()
session.get('https://example.com/login') # 设置 Cookie
response = session.get('https://example.com/dashboard') # 自动携带 Cookie
6.5 代理配置
python
proxies = {
'http': 'http://proxy.example.com:8080',
'https': 'https://proxy.example.com:8080',
}
response = requests.get(url, proxies=proxies)
第七章:性能优化与监控
7.1 连接池配置
python
class OptimizedRESTClient(RESTClient):
"""优化性能的 REST 客户端"""
def __init__(self, base_url: str, **kwargs):
super().__init__(base_url, **