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 脚本复制到剪贴板 零配置 每次手动操作 临时使用
相关推荐
我不想名字重复3 小时前
redis缓存和数据库数据保持一致
数据库·redis·缓存
独隅3 小时前
IntelliJ IDEA 接入多种AI大模型插件终极指南(2026.1 企业合规版)
java·人工智能·intellij-idea
宇宙第一小趴菜3 小时前
三、Oracle 核心原理
数据库·oracle
还是奇怪3 小时前
Simon Willison 用 DSPy 优化 Datasette Agent 提示词:提示工程正在变成可测试的软件工程
java·开发语言·软件工程
zfoo-framework4 小时前
1.ansible安装 2.虚拟机克隆
java
码上解惑4 小时前
从 Dify 工作流说起:常用节点怎么选、怎样组合?
java·人工智能·ai·agent·dify·智能体·spring ai
无足鸟ICT4 小时前
【RHCA+】查看变量
linux·运维·服务器
码出钞能力4 小时前
apache-shardingsphere-5.5.3自定义分片算法
数据库
Kina_C4 小时前
Linux DNS 服务器-从高速缓存到辅助 DNS 部署指南
linux·运维·服务器·dns