Pre-request脚本实战:提升API测试效率的关键技巧

程序猿焕焕 工具 阅读 1,538
赞 81 收藏
二维码
手机扫码查看
反馈

Pre-request脚本?其实就那几种写法,但坑不少

最近项目里又遇到接口鉴权的问题,每次调用 API 都要带个临时 token,而且这个 token 5 分钟就过期。一开始我手动在 Postman 里复制粘贴,结果三天后自己都烦死了。于是开始研究 Pre-request 脚本——说白了就是在请求发出去前自动跑一段代码,把需要的参数准备好。

Pre-request脚本实战:提升API测试效率的关键技巧

折腾了一圈,发现主流方案其实就三种: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、动态切换环境等),后续会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论