Pre-request脚本实战:提升API测试效率的关键技巧
Pre-request脚本?其实就那几种写法,但坑不少
最近项目里又遇到接口鉴权的问题,每次调用 API 都要带个临时 token,而且这个 token 5 分钟就过期。一开始我手动在 Postman 里复制粘贴,结果三天后自己都烦死了。于是开始研究 Pre-request 脚本——说白了就是在请求发出去前自动跑一段代码,把需要的参数准备好。
折腾了一圈,发现主流方案其实就三种:Postman 自带的 Pre-request Script、用 Axios 拦截器、还有在业务代码里硬编码。今天就来聊聊这三者的实际体验,不讲虚的,只说我踩过的坑和真实感受。
谁更灵活?谁更省事?
先说结论:如果是调试阶段,我闭眼选 Postman 的 Pre-request Script;如果是上线项目,我直接用 Axios 拦截器。至于硬编码?能不用就别用,除非你老板逼你明天上线还不给时间改架构。
先看 Postman 的方案。它的好处是完全独立于你的前端代码,改脚本不影响业务逻辑,特别适合快速验证接口。比如我要从 jztheme.com 拿一个 access_token,然后塞进 header:
// Postman Pre-request Script
const getTokenUrl = 'https://jztheme.com/api/auth/token';
pm.sendRequest({
url: getTokenUrl,
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: {
mode: 'raw',
raw: JSON.stringify({ client_id: 'my-app', secret: 'xxx' })
}
}, function (err, res) {
if (!err) {
const token = res.json().access_token;
pm.environment.set('auth_token', token);
}
});
然后在主请求的 Headers 里直接引用 {{auth_token}} 就行。亲测有效,而且改起来快。但问题也很明显:这玩意儿只能在 Postman 里跑,没法带到生产环境。你总不能让 QA 或后端同事都装 Postman 并同步你的脚本吧?所以它本质上是个“调试玩具”。
拦截器才是正经方案
上线项目我基本都用 Axios 拦截器。逻辑清晰,复用性强,还能配合 Vuex/Pinia 管理 token 状态。核心代码就这几行:
// axios interceptor
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://jztheme.com/api',
});
// 请求拦截器
apiClient.interceptors.request.use(
async (config) => {
let token = localStorage.getItem('auth_token');
const expiresAt = localStorage.getItem('token_expires');
// 如果 token 不存在或已过期,重新获取
if (!token || Date.now() > parseInt(expiresAt)) {
const res = await axios.post('https://jztheme.com/api/auth/token', {
client_id: 'my-app',
secret: 'xxx'
});
token = res.data.access_token;
const newExpires = Date.now() + 5 * 60 * 1000; // 5分钟过期
localStorage.setItem('auth_token', token);
localStorage.setItem('token_expires', newExpires.toString());
}
config.headers.Authorization = `Bearer ${token}`;
return config;
},
(error) => Promise.reject(error)
);
这个方案我用了快两年,稳定性没问题。但这里有个大坑:**如果多个请求同时触发,而 token 刚好过期,会并发请求多次获取 token**。我之前就因为这个被后端骂了,他们日志里看到同一秒十几条 /auth/token 请求。
解决办法是加个锁:
let isRefreshing = false;
let refreshSubscribers = [];
function addSubscriber(callback) {
refreshSubscribers.push(callback);
}
apiClient.interceptors.response.use(
response => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
// 等待 token 刷新完成后再重试
return new Promise((resolve) => {
addSubscriber(() => resolve(apiClient(originalRequest)));
});
}
originalRequest._retry = true;
isRefreshing = true;
try {
const res = await axios.post('https://jztheme.com/api/auth/token', {
client_id: 'my-app',
secret: 'xxx'
});
const token = res.data.access_token;
localStorage.setItem('auth_token', token);
// 通知所有等待的请求
refreshSubscribers.forEach(cb => cb());
refreshSubscribers = [];
return apiClient(originalRequest);
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
}
);
这段代码看起来有点啰嗦,但亲测能解决并发刷新问题。虽然复杂度上来了,但为了线上稳定,值得。
硬编码?别闹了
有些老项目或者 demo 项目,我见过有人直接在每个 API 调用前手写 token 获取逻辑:
fetch('https://jztheme.com/api/data', { headers: { Authorization: `Bearer ${getFreshToken()}` } })
这种写法短期看很快,但长期就是灾难。一旦 token 逻辑变了(比如换成 OAuth2),你得全局搜索替换,漏掉一个就 401。而且没法处理 token 刷新、错误重试这些场景。我以前接手过一个这样的项目,光是修 token 相关 bug 就花了两天,血泪教训。
我的选型逻辑
总结一下我的选择标准:
- 调试/联调阶段:用 Postman Pre-request Script,快、独立、不影响代码库
- 正式项目:Axios 拦截器 + 并发控制,虽然代码多点,但可维护性高
- 临时 Demo 或 POC:实在赶时间可以硬编码,但一定要加 TODO 注释,后续必须重构
另外提醒一点:不管用哪种方案,别把 secret 写死在前端代码里!上面示例里的 secret: 'xxx' 其实是不安全的,正确做法是通过后端中转,或者用 PKCE 流程。不过这是另一个话题了,今天先不展开。
最后说两句
Pre-request 脚本本质上是个“前置处理”问题,技术上没那么复杂,但细节特别多。我见过太多人只考虑“能跑就行”,结果上线后各种 token 失效、并发冲突、安全漏洞。花半小时把拦截器写扎实,比后期 debug 三天强。
以上是我个人对 Pre-request 脚本方案的完整对比和实战经验,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多(比如结合 refresh token、动态切换环境等),后续会继续分享这类博客。

暂无评论