ApiPost 中配置自动获取 Token 并调用业务接口完整指南

ApiPost 中配置自动获取 Token 并调用业务接口完整指南

一、目标

在 ApiPost 中实现:每次发送业务请求时,自动判断 Token 是否过期,过期则自动调用认证接口获取新 Token,无需手动操作。


二、整体流程

复制代码
点击"发送"业务请求
       ↓
全局预执行脚本自动运行
       ↓
检查环境变量中 token 是否存在且未过期
       ↓
 过期/为空 → 自动调用认证接口 → 解析响应 → 存入环境变量
 未过期   → 跳过,使用缓存 token
       ↓
全局 Header 自动拼装 Authorization: Bearer {{token}}
       ↓
业务请求正常发送

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

三、配置步骤

3.1 配置环境变量

位置: 右上角「环境管理」→ 新建/选择环境

添加以下变量(初始值留空):

变量名 初始值 说明
base_url http://127.0.0.1:8080 本地服务地址
token (空) 脚本自动填充
token_time (空) 记录获取时间,用于过期判断

3.2 配置全局请求头

位置: 全局参数 → Header 标签页

添加一条:

参数名 参数值
Authorization Bearer {``{token}}

{``{token}} 是环境变量的引用语法,ApiPost 会在发送时自动替换为实际值。


3.3 配置全局预执行脚本

位置: 全局参数 → 预执行脚本(Pre-request Script)标签页


四、预执行脚本(通用模板)

4.1 OAuth2 Password Grant 模式

适用于通过 OAuth2 认证服务获取 Token 的场景。

javascript 复制代码
// ===== 配置区域(替换为你的实际值)=====
var AUTH_URL = "https://your-auth-server.com/oauth/token";
var CLIENT_ID = "your_client_id";
var CLIENT_PASSWORD = "your_client_password";
var USERNAME = "your_username";
var PASSWORD = "your_password";
var TOKEN_EXPIRE_MS = 14400000; // token 有效期(毫秒),此处为 4 小时
// ===== 配置区域结束 =====

let token = apt.variables.get("token");
let tokenTime = apt.variables.get("token_time");
let now = Date.now();

// 判断 token 是否存在且未过期
if (!token || !tokenTime || (now - Number(tokenTime)) > TOKEN_EXPIRE_MS) {
    let base64 = btoa(CLIENT_ID + ":" + CLIENT_PASSWORD);

    apt.sendRequest({
        url: AUTH_URL,
        method: "POST",
        header: {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": "Basic " + base64
        },
        body: {
            mode: "urlencoded",
            urlencoded: [
                { key: "grant_type", value: "password" },
                { key: "username", value: USERNAME },
                { key: "password", value: PASSWORD }
            ]
        }
    }, function(err, res) {
        if (err) {
            console.log("获取token失败: " + err);
            return;
        }

        // 兼容多种响应格式解析
        let data;
        try {
            if (res.json && typeof res.json === 'function') {
                data = res.json();
            } else if (res.body) {
                data = JSON.parse(res.body);
            } else if (res.text && typeof res.text === 'function') {
                data = JSON.parse(res.text());
            } else if (typeof res === 'string') {
                data = JSON.parse(res);
            } else {
                data = res;
            }
        } catch(e) {
            console.log("解析响应失败: " + e.message);
            console.log("原始响应: " + String(res));
            return;
        }

        if (data && data.access_token) {
            apt.variables.set("token", data.access_token);
            apt.variables.set("token_time", String(Date.now()));
            console.log("Token已自动刷新");
        } else {
            console.log("响应中没有access_token: " + JSON.stringify(data).substring(0, 200));
        }
    });
}

4.2 普通登录接口模式

适用于通过常规登录接口(POST JSON body)获取 Token 的场景。

javascript 复制代码
// ===== 配置区域 =====
var LOGIN_URL = "https://your-server.com/api/login";
var LOGIN_BODY = {
    username: "your_username",
    password: "your_password"
};
// token 在响应中的路径,如 data.token 或 data.access_token
var TOKEN_FIELD = "data.token";
var TOKEN_EXPIRE_MS = 7200000; // 2 小时
// ===== 配置区域结束 =====

let token = apt.variables.get("token");
let tokenTime = apt.variables.get("token_time");
let now = Date.now();

if (!token || !tokenTime || (now - Number(tokenTime)) > TOKEN_EXPIRE_MS) {
    apt.sendRequest({
        url: LOGIN_URL,
        method: "POST",
        header: {
            "Content-Type": "application/json"
        },
        body: {
            mode: "raw",
            raw: JSON.stringify(LOGIN_BODY)
        }
    }, function(err, res) {
        if (err) {
            console.log("登录失败: " + err);
            return;
        }

        let data;
        try {
            if (res.json && typeof res.json === 'function') {
                data = res.json();
            } else if (res.body) {
                data = JSON.parse(res.body);
            } else if (typeof res === 'string') {
                data = JSON.parse(res);
            } else {
                data = res;
            }
        } catch(e) {
            console.log("解析失败: " + e.message);
            return;
        }

        // 按路径提取 token(支持 data.token 这种嵌套路径)
        let tokenValue = TOKEN_FIELD.split('.').reduce(function(obj, key) {
            return obj ? obj[key] : null;
        }, data);

        if (tokenValue) {
            apt.variables.set("token", tokenValue);
            apt.variables.set("token_time", String(Date.now()));
            console.log("Token已自动刷新");
        } else {
            console.log("找不到token字段: " + JSON.stringify(data).substring(0, 200));
        }
    });
}

适用于登录后 Token 放在响应头 Cookie 中的场景。

javascript 复制代码
// ===== 配置区域 =====
var LOGIN_URL = "https://your-server.com/login";
var LOGIN_BODY = {
    username: "your_username",
    password: "your_password"
};
var COOKIE_NAME = "your_jwt_cookie_name"; // 如 coc_jwt、session_token 等
var TOKEN_EXPIRE_MS = 7200000;
// ===== 配置区域结束 =====

let token = apt.variables.get("token");
let tokenTime = apt.variables.get("token_time");
let now = Date.now();

if (!token || !tokenTime || (now - Number(tokenTime)) > TOKEN_EXPIRE_MS) {
    apt.sendRequest({
        url: LOGIN_URL,
        method: "POST",
        header: {
            "Content-Type": "application/json"
        },
        body: {
            mode: "raw",
            raw: JSON.stringify(LOGIN_BODY)
        }
    }, function(err, res) {
        if (err) {
            console.log("登录失败: " + err);
            return;
        }

        // 从响应头中提取 Set-Cookie
        let cookies = "";
        if (res.headers) {
            // headers 可能是数组或对象
            if (Array.isArray(res.headers)) {
                res.headers.forEach(function(h) {
                    if (h.key && h.key.toLowerCase() === 'set-cookie') {
                        cookies += h.value + ";";
                    }
                });
            } else if (res.headers['set-cookie']) {
                cookies = res.headers['set-cookie'];
            } else if (res.headers['Set-Cookie']) {
                cookies = res.headers['Set-Cookie'];
            }
        }

        // 正则提取指定 cookie 的值
        let regex = new RegExp(COOKIE_NAME + "=([^;]+)");
        let match = cookies.match(regex);

        if (match && match[1]) {
            apt.variables.set("token", match[1]);
            apt.variables.set("token_time", String(Date.now()));
            console.log("Token已从Cookie中提取并刷新");
        } else {
            console.log("Cookie中未找到" + COOKIE_NAME);
            console.log("实际Cookie: " + cookies.substring(0, 200));
        }
    });
}

五、脚本关键 API 说明

5.1 全局对象兼容性

ApiPost 版本 全局对象 说明
ApiPost 7+ apt 新版本推荐使用
ApiPost 旧版 pm 兼容 Postman 风格

如果 apt 报错就换 pm,反之亦然。

5.2 核心 API

API 作用
apt.variables.get("key") 获取环境变量
apt.variables.set("key", "value") 设置环境变量
apt.sendRequest(options, callback) 在脚本中发送 HTTP 请求
btoa(string) Base64 编码(用于 Basic Auth)
console.log(msg) 控制台输出调试信息

5.3 sendRequest 参数结构

javascript 复制代码
apt.sendRequest({
    url: "完整URL",
    method: "POST",          // GET / POST / PUT / DELETE
    header: {                // 请求头(对象格式)
        "Content-Type": "application/json",
        "Authorization": "Basic xxx"
    },
    body: {
        mode: "urlencoded",  // urlencoded | raw | formdata
        urlencoded: [        // mode 为 urlencoded 时
            { key: "name", value: "value" }
        ],
        raw: '{"key":"val"}' // mode 为 raw 时
    }
}, function(err, res) {
    // err: 错误信息,成功时为 null
    // res: 响应对象
});

5.4 响应解析兼容写法

由于不同版本 res 的结构不同,推荐使用兼容写法:

javascript 复制代码
let data;
try {
    if (res.json && typeof res.json === 'function') {
        data = res.json();           // 方式1: res.json()
    } else if (res.body) {
        data = JSON.parse(res.body); // 方式2: res.body 是字符串
    } else if (res.text && typeof res.text === 'function') {
        data = JSON.parse(res.text()); // 方式3: res.text()
    } else if (typeof res === 'string') {
        data = JSON.parse(res);      // 方式4: res 直接是字符串
    } else {
        data = res;                  // 方式5: res 本身就是对象
    }
} catch(e) {
    console.log("解析失败: " + e.message);
}

六、调试技巧

6.1 查看脚本控制台输出

发送请求后,在 ApiPost 底部找到 控制台(Console) 面板,查看 console.log 的输出。

6.2 调试响应结构

如果不确定 res 的实际结构,先加上:

javascript 复制代码
console.log("响应类型: " + typeof res);
console.log("响应内容: " + JSON.stringify(res).substring(0, 300));

根据打印结果确定正确的解析方式。

6.3 强制刷新 Token

手动清空环境变量中的 token 值,下次发送请求时脚本会重新获取。


七、业务接口配置示例

配置好全局脚本和 Header 后,新建业务接口只需关注业务本身:

  • URL{``{base_url}}/api/your-endpoint
  • Method:POST
  • Body(raw JSON):
json 复制代码
{
    "pageNum": 1,
    "pageSize": 10
}

不需要手动填写 Authorization,全局配置会自动处理。


八、常见问题

问题 原因 解决
Unexpected token u in JSON at position 0 res 是 undefined 或解析方式不对 用兼容写法逐个尝试,先打印 typeof res
apt is not defined 版本不支持 apt 换成 pm
btoa is not defined 环境不支持 btoa Buffer.from(str).toString('base64') 替代
Token 获取成功但接口仍 401 Header 没生效 检查全局 Header 是否写了 Bearer {``{token}}(注意空格)
每次都重新获取 Token token_time 变量没设置上 检查变量名拼写,确认 apt.variables.set 可用
认证接口返回 401 client_id 或密码错误 单独新建一个接口手动测试认证地址是否可达

九、方案对比

方案 优点 缺点 适用场景
全局预执行脚本 全自动,一次配置永久生效 需要版本支持 sendRequest 日常开发首选
单独 Token 接口 + 后执行脚本 所有版本都支持 过期后需手动点一次 版本不支持 sendRequest
bat 脚本复制到剪贴板 零配置 每次手动操作 临时使用
相关推荐
小羊没烦恼!1 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里1 天前
java中的继承和多态的区别
java
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕1 天前
JSONObject与JSONArray封装数据格式区别
java·json
虎头金猫1 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
胡写代码1 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖1 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时1 天前
01-06-A-JVM排查实战详解
java·jvm
这个DBA有点耶1 天前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
vipxieliang1 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot