上一篇我们已经解决了 Token 的产生和验证。
现在又出现了一个现实问题:
如果我们的项目有 20 个接口,是不是每次都要手动写:
js
headers: {
Authorization: `Bearer ${token}`
}
当然不是。
所以这篇来理解 Axios 的请求拦截器。
一、先看一个非常麻烦的写法
假设现在有三个 API:
js
axios.get('/repo', {
headers: {
Authorization: `Bearer ${token}`
}
})
js
axios.get('/user', {
headers: {
Authorization: `Bearer ${token}`
}
})
js
axios.post('/order', data, {
headers: {
Authorization: `Bearer ${token}`
}
})
你会发现:
每个请求都在重复处理 Token。
而 Token 本来就是整个登录系统的公共逻辑。
所以应该把它统一处理。
二、我们需要一个"请求前统一处理"的地方
我们之前已经创建了:
text
src/api/config.js
现在它不只是负责:
js
baseURL
timeout
还负责:
在请求发出去之前,把 Token 自动放进 Header。
Axios 提供的就是:
js
interceptors.request
也就是:
请求拦截器。
三、添加请求拦截器
修改:
text
src/api/config.js
代码:
js
import axios from 'axios';
const instance = axios.create({
baseURL: '/api',
timeout: 5000
});
instance.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
});
export default instance;
这里最关键的是:
js
instance.interceptors.request.use(...)
可以理解成:
"以后凡是使用这个 Axios 实例发出的请求,都先让我检查一下。"
四、拦截器到底在什么时候执行?
假设页面调用:
js
getRepo()
然后:
js
axios.get('/repo')
请求不会直接飞出去。
而是先经过:
text
页面调用 getRepo()
↓
repo.js 调用 axios.get()
↓
Axios 请求拦截器
↓
读取 localStorage 中的 Token
↓
添加 Authorization
↓
真正发送请求
所以拦截器最大的意义就是:
统一处理所有请求的公共逻辑。
五、为什么从 localStorage 里取 Token?
因为登录成功以后,我们要先把 Token 保存起来。
例如:
js
localStorage.setItem('token', token);
以后:
js
localStorage.getItem('token');
就能拿出来。
所以整个关系是:
text
登录成功
↓
Token 保存到 localStorage
↓
发送任意 API 请求
↓
拦截器读取 Token
↓
添加 Authorization
六、为什么写成 Bearer?
最终我们希望 Header 是:
text
Authorization: Bearer eyJ...
所以代码:
js
config.headers['Authorization'] = `Bearer ${token}`;
注意这里的空格:
text
Bearer + 空格 + Token
这就是标准的 Bearer Token 写法。
如果写成:
js
`Bearer${token}`
就会变成:
text
BearereyJ...
后端再按:
js
authorization.startsWith('Bearer ')
检查时就会失败。
七、这样 API 层会变得非常干净
user.js:
js
import axios from './config';
export const login = async (data) => {
const res = await axios.post('/login', data);
return res.data;
}
repo.js:
js
import axios from './config';
export const getRepo = async () => {
const res = await axios.get('/repo');
return res.data;
}
你会发现:
这两个文件根本不需要关心 Token。
它们只需要表达:
"我要调用哪个接口。"
至于:
"有没有 Token?"
"Token 放到哪个 Header?"
这些事情统一交给 Axios。
这就是职责分离。
八、请求拦截器为什么很重要?
因为很多项目都会有一些"所有请求都要做"的事情:
text
Token
统一 Header
请求日志
请求参数处理
Loading
统一错误处理
这些都不适合散落到业务代码里。
所以:
text
业务代码
负责业务
拦截器
负责公共逻辑
这样项目会清晰很多。
九、服务器收到请求以后
现在浏览器发出去的请求类似:
text
GET /api/repo
Authorization: Bearer eyJ...
Mock 后端:
js
const authorization = req.headers?.authorization;
然后:
js
const token = authorization.slice('Bearer '.length);
最后:
js
jwt.verify(token, secret);
整个请求链已经完整了:
text
页面调用 API
↓
Axios 请求拦截器自动添加 Token
↓
请求发送到 /api/repo
↓
Mock 读取 Authorization
↓
取出 Token
↓
jwt.verify()
↓
返回数据
下一篇要解决的就不是"服务器怎么验证",而是:
React 自己怎么知道当前用户有没有登录?
这就轮到 Zustand 出场了。