当前位置: 首页 > news >正文

apipost 8.x 脚本循环调用接口

apipost 8.x 脚本循环调用接口

  • 背景
  • 实现
    • 先说整体逻辑:
  • 最后

背景

上周为了找某OA 偶尔出现的诡异现象,需要用测试工具来压测,看看这个问题能否重现。以前用过Jmeter,但是没有装,正好有个国产的apipost看看如何?刚开始使用界面调用接口,还挺顺利。但是到脚本调用接口,就是token校验不通过,这还是问deepseek,这里还有有趣地方:百度查询apipost 脚本给出例子,然后给deepseek发过去,它说那是postman 例子,随后给我“正确”的apipost 脚本,我暗自赞叹,真厉害!但是执行就是校验不通过,老报301,重定向到登录界面。后来我就问APIPOST客服,它apipost 里面ai助手,扔给它搞定就行。结果它给我就是postman脚本。我就诧异,我问客服,为何不给apipost 的脚本,她回答:兼容postman。结果一试,真能跑通。太神奇了!这是不是侧面说明:apipost自身脚本还不够成熟。

实现

先说整体逻辑:

在这里插入图片描述
前面1-4 就是模拟浏览器操作,发起流程一个过程。因为这个只做一遍,不需要循环。
第5步:因为需要循环多个接口,不能使用界面定义接口。它里面又分为
5.1 查询当前流程最后处理人员
5.2登录
5.3 保存流程
5.4 移动到下一个操作人员
5.5 提交流程
5.6 登出
重复5.1-5.5 直到5.1 查询不到最后处理人员为止
主函数如下:

// 定义一个异步函数来处理循环
async function loopRequests() {try {let baseUrl = pm.environment.get("base_url");if (!baseUrl) {throw new Error('base_url 环境变量未设置');}let jsessionId = pm.environment.get("JSESSIONID");let cwbbsAuth = pm.environment.get("cwbbs.auth");let flowId = pm.environment.get('flowId');console.log('jsessionId:' + jsessionId)if (!jsessionId) {throw new Error('jsessionId 环境变量未设置');}if (!flowId) {throw new Error('flowId 环境变量未设置');} else {console.info('开始处理流程flowId:' + flowId)}let hasMore = true;let count = 0;while (hasMore) {console.log(`正在发起第 ${count + 1} 次请求`);try {// 1. 查询当前流程最后处理人员const url = baseUrl + '/flow/getLastHandlingPersonnel';const headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8','Cookie': 'JSESSIONID=' + jsessionId + ';cwbbs.auth=' + cwbbsAuth};const body = `flowId=${flowId}`;const res = await pm.sendRequest({url,method: 'POST',header: headers,body: {mode: 'raw',raw: body}});const responseText = String(res.stream);console.log('responseText:' + responseText)const responseData = JSON5.parse(responseText);console.log(`${count + 1} 次请求成功:`, responseData);// 4. 根据最后处理人登录->保存->提交->登出if (responseData.ret == 1) {console.log('开始处理流程...');await login(responseData);await saveFlow(responseData);let nextInternalName = await moveNextPerson(responseData);await finish(responseData, nextInternalName);await logout();console.log('流程处理完成');} else {console.log('ret不为1,停止循环。响应:', responseData);hasMore = false;}count++;if (count >= 20) {console.log('达到最大循环次数20,停止循环');hasMore = false;}} catch (innerError) {console.error(`${count + 1} 次请求失败:`, innerError.message);console.error('错误堆栈:', innerError.stack);hasMore = false; // 出错时停止循环}}} catch (outerError) {console.error('loopRequests 外部错误:', outerError.message);console.error('外部错误堆栈:', outerError.stack);throw outerError;}
}

5.2 登录接口:

async function login(loginData) {try {console.log('开始登录,用户:', loginData.userName);const baseUrl = pm.environment.get("base_url");if (!baseUrl) {throw new Error('base_url 环境变量未设置');}if (!loginData.userName) {throw new Error('loginData 中缺少 userName');}const url = baseUrl + '/doLogin.do';const headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'};const body = `name=${loginData.userName}&wdmm=TY%2BD6EZV1gPtH3dQJJkwrA%3D%3D&signature=&op=&mainTitle=&mainPage=&form_token=17560851885711572739&isSavePwd=on`;const res = await pm.sendRequest({url,method: 'POST',header: headers,body: {mode: 'raw',raw: body}});const responseText = String(res.stream);console.log('responseText:' + responseText)const responseData = JSON5.parse(responseText);console.log('after JSON5.parse')if (responseData.ret != '1') {throw new Error(`登录失败,ret: ${responseData.ret}, msg: ${responseData.msg}`);}// cookies 保存到环境变量const headers1 = res.headers;// 遍历所有响应头headers1.each(header1 => {//console.log(header1.key + ': ' + header1.value);// 专门处理Set-Cookie头if (header1.key.toLowerCase() === 'set-cookie') {const setCookieValue = header1.value;console.log('Set-Cookie原始值:', setCookieValue);// 解析JSESSIONIDconst jsessionIdMatch = setCookieValue.match(/JSESSIONID=([^;]+)/);if (jsessionIdMatch && jsessionIdMatch[1]) {const jsessionId = jsessionIdMatch[1];pm.environment.set("JSESSIONID", jsessionId);console.log('✅ 已设置JSESSIONID:', jsessionId);}// 解析cwbbs.authconst cwbbsAuthMatch = setCookieValue.match(/cwbbs\.auth=([^;]*)/);if (cwbbsAuthMatch) {const cwbbsAuth = cwbbsAuthMatch[1] || ''; // 如果是空值也保存pm.environment.set("cwbbs.auth", cwbbsAuth);console.log('✅ 已设置cwbbs.auth:', cwbbsAuth);}}});} catch (error) {console.error('登录过程中出错:', error.message);console.error('登录错误堆栈:', error.stack);throw error;}
}

5.3 保存流程

async function saveFlow(flowResponse) { // 重命名参数避免冲突try {let jsessionId = pm.environment.get("JSESSIONID"); // 获取cookie值let cwbbsAuth = pm.environment.get("cwbbs.auth");   // 获取cookie值let flowId = pm.environment.get('flowId');let baseUrl = pm.environment.get("base_url"); // 获取base_urlif (!baseUrl) {throw new Error('base_url 环境变量未设置');}console.log('---saveFlow开始-----')console.log('jessionId:' + jsessionId + ',cwbbsAuth:' + cwbbsAuth)const url = baseUrl + '/flow/finishAction.do';// 使用FormData对象,让Postman自动处理multipart格式// 构建URL编码的表单数据// 定义边界符const boundary = '----WebKitFormBoundary' + Date.now().toString(16);const crlf = '\r\n';// 构建multipart/form-data bodylet multipartBody = '';// 辅助函数:添加表单字段const addFormField = (name, value) => {multipartBody += `--${boundary}${crlf}`;multipartBody += `Content-Disposition: form-data; name="${name}"${crlf}${crlf}`;multipartBody += `${value}${crlf}`;};// 添加所有字段addFormField('isUseMsg', 'true');addFormField('flowAction', '');addFormField('cwsWorkflowTitle', 'xxx发布test');addFormField('cwsWorkflowResult', '');addFormField('att1', '');addFormField('flowId', flowId);addFormField('actionId', flowResponse.actionId);addFormField('myActionId', flowResponse.myActionId);addFormField('XorNextActionInternalNames', '');addFormField('op', 'saveformvalueBeforeXorCondSelect');addFormField('isAfterSaveformvalueBeforeXorCondSelect', '');addFormField('formReportContent', '');addFormField('gzhlx', 'xxx平台');addFormField('wzbt', '111');addFormField('tgr', 'test');addFormField('ngr', 'test');addFormField('wzlx', '一般信息');addFormField('tgbm', flowResponse.deptCode); // 不需要手动encodeURIComponent,边界符会自动处理addFormField('zzlj', '');addFormField('bqsm', '1');addFormField('mgxxjc', '1');addFormField('wznr', '<p>test</p>');addFormField('bmjlyj', '同意');addFormField('gzfzryj', '');addFormField('jycldyj', '');addFormField('dgbfgldyj', '');addFormField('zjlyj', '');addFormField('dszyj', '');addFormField('cws_textarea_sf', '是');addFormField('cws_textarea_ngr', 'test');addFormField('cws_textarea_tgr', 'test');addFormField('cws_textarea_bqsm', '1');addFormField('cws_textarea_tgbm', flowResponse.deptCode);addFormField('cws_textarea_wzbt', '111');addFormField('cws_textarea_wzlx', '一般信息');addFormField('cws_textarea_wznr', '<p>test</p>');addFormField('cws_textarea_zzlj', '');addFormField('cws_textarea_dszyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_gzhlx', 'xxx平台');addFormField('cws_textarea_zjlyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_bmjlyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_mgxxjc', '1');addFormField('cws_textarea_gzfzryj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_jycldyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_dgbfgldyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('myReplyTextareaContent', '');addFormField('isSecret0', '0');addFormField('myActionId0', flowResponse.myActionId);addFormField('discussId0', '0');addFormField('flow_id0', flowId);addFormField('action_id0', '0');addFormField('user_name0', flowResponse.userName);addFormField('userRealName0', flowResponse.userRealName);addFormField('reply_name0', flowResponse.userName);addFormField('parent_id0', '-1');addFormField('returnBack', 'true');// 添加结束边界multipartBody += `--${boundary}--${crlf}`;const headers = {'Content-Type': `multipart/form-data; boundary=${boundary}`,'Cookie': 'JSESSIONID=' + jsessionId + ';cwbbs.auth=' + cwbbsAuth};console.log('headers:' + headers);const res = await pm.sendRequest({url,method: 'POST',header: headers,body: {mode: 'raw',raw: multipartBody}});const responseText = String(res.stream);console.log('保存流程,请求:' + multipartBody);console.log('responseText:' + responseText)const responseData = JSON5.parse(responseText);if (responseData.ret == '1') {console.log('✅ 保存成功');return responseData;} else {console.warn('❌ 保存失败,返回值:', responseData.ret);throw new Error(`保存失败: ${responseData.msg || '未知错误'}`);}} catch (error) {console.error('保存流程时出错:', error.message);throw error; // 重新抛出错误}
}

5.4 移动到下一个操作人员

async function moveNextPerson(flowResponse) {try {console.info('开始处理移动到下一个操作人员')const baseUrl = pm.environment.get("base_url");if (!baseUrl) {throw new Error('base_url 环境变量未设置');}const url = baseUrl + '/flow/moveNextPersonnels';const headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'};const body = `op=matchNextBranch&actionId=${flowResponse.actionId}&myActionId=${flowResponse.myActionId}`;console.log('moveNextPerson 请求:' + body);const res = await pm.sendRequest({url,method: 'POST',header: headers,body: {mode: 'raw',raw: body}});const responseText = String(res.stream);console.log('responseText:' + responseText)const responseData = JSON5.parse(responseText);console.log('after JSON5.parse')if (responseData.ret != '1') {throw new Error(`移动下一个操作人员失败,ret: ${responseData.ret}, msg: ${responseData.msg}`);} else {return responseData.nextInternalName;}} catch (error) {console.error('移动到下一个操作人员出错:', error.message);console.error('移动到下一个操作人员错误堆栈:', error.stack);throw error;}
}

5.5 提交流程

async function finish(flowResponse,nextInternalName) {try {let jsessionId = pm.environment.get("JSESSIONID"); // 获取cookie值let cwbbsAuth = pm.environment.get("cwbbs.auth");   // 获取cookie值let flowId = pm.environment.get('flowId');let baseUrl = pm.environment.get("base_url"); // 获取base_urlif (!baseUrl) {throw new Error('base_url 环境变量未设置');}const boundary = '----WebKitFormBoundaryuUyGuZA3IOGaIBch' + Date.now().toString(16);const crlf = '\r\n';let multipartBody = '';// 定义添加表单字段的方法const addFormField = (name, value) => {multipartBody += `--${boundary}${crlf}`;multipartBody += `Content-Disposition: form-data; name="${name}"${crlf}${crlf}`;multipartBody += `${value}${crlf}`;};// 添加所有字段addFormField('deptOfUserWithMultiDept0', '');addFormField('XorActionSelected', nextInternalName);addFormField(`WorkflowAction_${flowResponse.actionId}`, flowResponse.userName);addFormField('isUseMsg', 'true');addFormField('flowAction', '');addFormField('cwsWorkflowTitle', 'xxx发布test');addFormField('cwsWorkflowResult', '');addFormField('att1', '');addFormField('flowId', flowId);addFormField('actionId', flowResponse.actionId);addFormField('myActionId', flowResponse.myActionId);addFormField('XorNextActionInternalNames', nextInternalName);addFormField('op', 'finish');addFormField('isAfterSaveformvalueBeforeXorCondSelect', 'true');addFormField('formReportContent', '');addFormField('gzhlx', 'xxx平台');addFormField('wzbt', '111');addFormField('tgr', 'test');addFormField('ngr', 'test');addFormField('wzlx', '一般信息');addFormField('tgbm', flowResponse.deptCode);addFormField('zzlj', '');addFormField('bqsm', '1');addFormField('mgxxjc', '1');addFormField('wznr', '<p>test</p>');addFormField('bmjlyj', '同意');addFormField('gzfzryj', '');addFormField('jycldyj', '');addFormField('dgbfgldyj', '');addFormField('zjlyj', '');addFormField('dszyj', '');addFormField('cws_textarea_sf', '是');addFormField('cws_textarea_ngr', 'test');addFormField('cws_textarea_tgr', 'test');addFormField('cws_textarea_bqsm', '1');addFormField('cws_textarea_tgbm', flowResponse.deptCode);addFormField('cws_textarea_wzbt', '111');addFormField('cws_textarea_wzlx', '一般信息');addFormField('cws_textarea_wznr', '<p>test</p>');addFormField('cws_textarea_zzlj', '');addFormField('cws_textarea_dszyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_gzhlx', 'xxx平台');addFormField('cws_textarea_zjlyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_bmjlyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_mgxxjc', '1');addFormField('cws_textarea_gzfzryj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_jycldyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('cws_textarea_dgbfgldyj', '<?xml version="1.0" encoding="utf-8"?><myactions></myactions>');addFormField('myReplyTextareaContent', '');addFormField('isSecret0', '0');addFormField('myActionId0', flowResponse.myActionId);addFormField('discussId0', '0');addFormField('flow_id0', flowId);addFormField('action_id0', '0');addFormField('user_name0', flowResponse.userName);addFormField('userRealName0', flowResponse.userRealName);addFormField('reply_name0', flowResponse.userName);addFormField('parent_id0', '-1');addFormField('returnBack', 'true');// 添加结束边界multipartBody += `--${boundary}--${crlf}`;const url = baseUrl + '/flow/finishAction.do';const headers = {'Content-Type': `multipart/form-data; boundary=${boundary}`,'Cookie': 'JSESSIONID=' + jsessionId + ';cwbbs.auth=' + cwbbsAuth};const res = await pm.sendRequest({url,method: 'POST',header: headers,body: {mode: 'raw',raw: multipartBody}});const responseText = String(res.stream);console.log('提交流程,请求:' + multipartBody);console.log('responseText:' + responseText)const responseData = JSON5.parse(responseText);if (responseData.ret == '1') {console.log('✅ 提交成功');return responseData;} else {console.warn('❌ 提交失败,返回值:', responseData.ret);throw new Error(`提交失败: ${responseData.msg || '未知错误'}`);}} catch (error) {console.error('提交流程时出错:', error.message);throw error; // 重新抛出错误}
}

5.6 登出

async function logout() {try {let jsessionId = pm.environment.get("JSESSIONID"); // 获取cookie值let cwbbsAuth = pm.environment.get("cwbbs.auth");   // 获取cookie值    let baseUrl = pm.environment.get("base_url"); // 获取base_urlif (!baseUrl) {throw new Error('base_url 环境变量未设置');}const url = baseUrl + '/logout?skincode=lte';const headers = {'Cookie': 'JSESSIONID=' + jsessionId + ';cwbbs.auth=' + cwbbsAuth};const res = await pm.sendRequest({url,method: 'GET',header: headers});const responseText = String(res.stream);console.log('登出成功:' + responseText)} catch (error) {console.error('登出出错:', error.message);throw error; // 重新抛出错误}
}

// 执行函数

try {console.log('开始执行主循环...');loopRequests().then(() => {console.log('主循环执行完成');}).catch(error => {console.error('主循环执行失败:', error.message);console.error('主循环错误堆栈:', error.stack);});
} catch (error) {console.error('执行过程中出错:', error.message);console.error('执行错误堆栈:', error.stack);

因为一个流程发起后,经过多少人处理是动态的,不能指定循环多少次。

最后

apipost 脚本写到这里,这个工具感觉比国外的postman,jemeter 要轻量一些。
另外,它的写接口和用例分开的
在这里插入图片描述

如需沟通,联系:lita2lz

http://www.dtcms.com/a/360793.html

相关文章:

  • 9月1日
  • WhatsApp 漏洞与 Apple 零日漏洞一起被利用于间谍软件攻击
  • LangChain VectorStores核心:多向量数据库统一交互层与RAG存储中枢
  • 深度学习——速问速答
  • Java视觉跟踪入门:使用OpenCV实现实时对象追踪
  • Vue2存量项目国际化改造踩坑
  • pyside6小项目:进制转换器
  • 《架构师手记:SpringCloud整合Nacos实战·一》
  • 2.MySQL库的操作
  • Spark实现推荐系统中的相似度算法
  • 【LeetCode】19、删除链表的倒数第N个结点
  • P1803 凌乱的yyy / 线段覆盖
  • 802.11 和 802.1X
  • 计算机毕设选题:基于Python+Django的健康饮食管理系统设计【源码+文档+调试】
  • 网络原理——TCP/UDP/IP
  • 【面试场景题】如何快速判断几十亿个数中是否存在某个数
  • 【面试场景题】100M网络带宽能不能支撑QPS3000
  • (3dnr)多帧视频图像去噪 (一)
  • 第六章 Vue3 + Three.js 实现高质量全景图查看器:从基础到优化
  • 站在巨人的肩膀上:gRPC通过HTTP/2构建云原生时代的通信标准
  • Goframe 框架下HTTP反向代理并支持MCP所需的SSE协议的实现
  • 【深度学习基础】深度学习中的早停法:从理论到实践的全面解析
  • 【php反序列化字符串逃逸】
  • word运行时错误‘53’,文件未找到:MathPage.WLL,更改加载项路径完美解决
  • Android原生HttpURLConnection上传图片方案
  • mysql导出csv中字段里有换行符的处理办法及hive导出处理办法
  • 印度数据源 Java 对接文档
  • 【DeepSeek】蓝耘元生代 | 蓝耘MaaS平台与DeepSeek-V3.1重构智能应用开发
  • 打造智能写作工作流:n8n + 蓝耘MaaS平台完整实战指南
  • 20.30 QLoRA微调终极指南:Hugging Face参数优化实战,24GB显存直降50%性能不减