本文从前端视角切入,核心对比 Python requests 与 JS axios 在同步/异步模型、拦截器、取消请求及错误处理上的差异,加深理解。
作为前端开发者,我们每天都在用 axios 发请求,觉得它已经是 HTTP 请求的"标准答案"了。但如果你接触过 Python 后端,或者自己写过爬虫、脚本,大概率会碰到 requests 这个库。
一、它们是什么
| Python requests | JS axios | |
|---|---|---|
| 语言 | Python | JavaScript |
| 运行环境 | Python 环境(服务端/脚本 | 浏览器 + Node.js |
| 编程模型 | 同步阻塞 | 异步非阻塞(基于 Promise) |
| 安装 | pip install requests |
npm install axios |
最核心的区别就一句话:requests 是同步的,axios 是异步的。 这决定了它们在使用方式上的根本差异。
二、最基础的请求:写法对比
先看最简单的 GET 请求:
python
# Python requests
import requests
response = requests.get('https://api.example.com/data')
print(response.json()) # 直接拿到解析好的数据
javascript
// JS axios
import axios from 'axios'
const response = await axios.get('https://api.example.com/data')
console.log(response.data) // 数据在 .data 里
看起来很像对吧?但注意两个细节:
- requests 不需要
await,因为它本身就是同步的,代码执行到这一行会"卡住",等响应回来才继续 - 响应数据的位置不同 :requests 用
response.json()方法解析,axios 直接挂在response.data上
再看 POST 请求:
python
# Python requests
response = requests.post(
'https://api.example.com/users',
json={"name": "张三", "age": 25} # 自动序列化 + 设置 Content-Type
)
javascript
// JS axios
const response = await axios.post(
'https://api.example.com/users',
{ name: '张三', age: 25 } // 同样自动序列化
)
两者都支持自动将对象序列化为 JSON 并设置 Content-Type: application/json,这点体验几乎一致。
三、同步 vs 异步:最本质的区别
这是前端同学最需要转变思维的地方。
requests 是同步阻塞的,意味着:
python
import requests
# 这行代码执行完之前,后面的代码不会执行
response = requests.get('https://slow-api.com/data', timeout=10)
print('这行要等上面请求完成才会执行')
axios 是异步非阻塞的,基于 Promise:
javascript
// 请求发出后,主线程不会被阻塞
const response = await axios.get('https://slow-api.com/data')
console.log('等待请求完成后才执行')
这对前端来说是天经地义的------浏览器主线程不能被阻塞,否则页面就卡死了。但 Python 的 requests 天然就是"等完了再继续",写起来反而更直观,不需要理解 Promise、async/await 这些概念。
四、并发请求:体验差异明显
当你需要同时发多个请求时,差异就体现出来了。
axios 天然擅长并发 ,用 Promise.all 一行搞定:
javascript
const [users, posts, comments] = await Promise.all([
axios.get('/api/users'),
axios.get('/api/posts'),
axios.get('/api/comments')
])
requests 要实现并发,需要借助多线程或多进程:
python
import requests
from concurrent.futures import ThreadPoolExecutor
urls = ['/api/users', '/api/posts', '/api/comments']
with ThreadPoolExecutor(max_workers=3) as executor:
responses = list(executor.map(requests.get, urls))
或者使用 Python 的异步库(如 aiohttp、httpx),但那就不是 requests 了。
五、拦截器 vs 没有拦截器
这是 axios 的一大杀手锏,也是前端工程化中非常常用的能力。
axios 的拦截器可以在请求发出前和响应返回后统一处理:
javascript
// 请求拦截器:统一加 token
axios.interceptors.request.use(config => {
config.headers.Authorization = `Bearer ${getToken()}`
return config
})
// 响应拦截器:统一处理错误
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// 跳转登录页
}
return Promise.reject(error)
}
)
requests 没有原生的拦截器机制。要实现类似功能,需要自己封装:
python
import requests
class APIClient:
def __init__(self, base_url, token=None):
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {token}',
'Accept': 'application/json'
})
def get(self, path, **kwargs):
response = self.session.get(f'{base_url}{path}', **kwargs)
response.raise_for_status()
return response.json()
用 Session 对象可以实现"全局配置"的效果,但"拦截器"这种 AOP(面向切面)的能力,requests 确实不如 axios 优雅。
六、取消请求
前端场景中,取消请求很常见(比如用户快速切换页面时,取消上一个未完成的请求).
axios 通过 AbortController(早期版本用 CancelToken)实现:
javascript
const controller = new AbortController()
axios.get('/api/data', { signal: controller.signal })
// 需要取消时
controller.abort()
requests 没有内置的取消机制。由于它是同步阻塞的,"取消"这个概念本身就不太适用------你只能通过设置 timeout 来控制最长等待时间。
七、错误处理:风格迥异
axios 基于 Promise,错误通过 .catch() 或 try/catch 捕获:
javascript
try {
const response = await axios.get('/api/data')
} catch (error) {
if (error.response) {
console.log(`HTTP 错误: ${error.response.status}`)
} else if (error.request) {
console.log('请求已发出,但没有收到响应')
} else {
console.log('请求配置出错:', error.message)
}
}
requests 基于异常(Exception),通过 try/except 捕获:
python
try:
response = requests.get('/api/data', timeout=5)
response.raise_for_status() # 4xx/5xx 时主动抛出异常
except requests.exceptions.Timeout:
print('请求超时')
except requests.exceptions.ConnectionError:
print('连接失败')
except requests.exceptions.HTTPError as e:
print(f'HTTP 错误: {e}')
except requests.exceptions.RequestException as e:
print(f'其他异常: {e}')
两者的异常体系都是分层的(子类继承父类),但 requests 的异常层级更丰富,细分程度更高。
八、Session 机制对比
两者都支持"会话"概念,但实现方式不同:
| requests.Session | axios.create | |
|---|---|---|
| 连接复用 | ✅ 底层连接池复用 TCP 连接 | ✅ 浏览器端复用 HTTP 连接 |
| Cookie 自动管理 | ✅ 自动保存和携带 | ✅ 浏览器端自动处理 |
| 全局默认配置 | ✅ session.headers.update() |
✅ instance.defaults.headers |
| 自定义传输层 | ✅ 可挂载 HTTPAdapter | ❌ 不支持 |
requests 的 Session + HTTPAdapter 组合非常强大,可以精细控制连接池大小、重试策略等,这是 axios 做不到的。
九、功能特性速查表
| 特性 | requests | axios |
|---|---|---|
| 同步/异步 | 同步 | 异步(Promise) |
| 自动 JSON 序列化 | ✅ json= 参数 |
✅ 默认行为 |
| 请求拦截器 | ❌ 需自行封装 | ✅ 原生支持 |
| 响应拦截器 | ❌ 需自行封装 | ✅ 原生支持 |
| 取消请求 | ❌ | ✅ AbortController |
| 并发请求 | 需多线程/多进程 | Promise.all |
| 连接池管理 | ✅ HTTPAdapter | ❌ |
| 自动重试 | ✅ HTTPAdapter max_retries |
❌ 需自行封装 |
| 文件上传 | ✅ files= 参数 |
✅ FormData |
| 浏览器环境 | ❌ | ✅ |
| XSRF 防御 | ❌ 需手动处理 | ✅ 浏览器端自动处理 |
十、什么时候用哪个
用 axios 的场景(前端开发):
- 浏览器端发请求,这是它的"主场"
- 需要拦截器统一处理 token、错误码
- 需要取消请求(如页面跳转时)
- 需要并发请求(
Promise.all)
用 requests 的场景(Python 开发):
- 写脚本、爬虫、自动化任务
- 后端服务间调用
- 对代码可读性要求高(同步写法更直观)
- 需要精细控制连接池和重试策略