
1. 为什么要做关联接口测试的“传递链条”做接口测试的人十有八九都会遇到同一个场景登录接口返回了一个token后面查询订单、修改资料、提交支付全都要带上这个token。你当然可以手动复制粘贴一次两次没问题但当你跑完一条完整的业务链路几十个接口串在一起每次都手抄token既慢又容易抄错。更别提回归测试的时候token过期了、重新登录了整个集合又得手动改一遍。这就是接口关联要解决的问题。所谓Postman关联本质就是让接口之间能够自动传递数据——前一个接口的响应结果被提取出来存成变量后一个接口再通过变量引用的方式自动取值。这套机制用好了你的测试脚本就真正“活”了起来不再是孤立的单个请求而是一条能自我流转的数据链。我之前带团队做电商项目的接口自动化最核心的几条主链路——登录拿token、下单拿订单号、支付拿流水号、查询拿状态——全部靠关联串起来。一开始新同学总是手动复制跑到第三步就经常因为漏改一个参数而失败。后来统一改成用Postman的关联机制整个集合跑一遍只需一键稳定性和效率都上来了。这篇文章就系统梳理一下我在实际项目中常用的数据提取方式以及背后那些文档里不会写明白的细节。适合谁来读刚接触Postman接口测试、想搞明白“怎么把一个接口的数自动传给下一个接口”的测试新人以及已经会用Postman但每次都是手动复制参数、想提升自动化水平的同学。读完你至少能掌握四五种提取方式并且知道在什么场景下用哪一种最合适。2. 数据提取方式的完整视图先分清三大维度再动手在动手写提取脚本之前我建议先建立一张地图。数据提取这个事表面上看是一行代码的事实际背后牵扯到三个维度从哪提取、用什么语法提取、提取完存到哪。这三个维度搞不清楚你很容易陷入一种很尴尬的状态在网上找到一段提取代码粘贴进来能跑通换个接口又不灵了然后就开始瞎试。我见过太多这样的同事花了一下午在各种正则表达式里翻滚最后发现其实是提取位置选错了。2.1 按响应位置划分Body、Headers、CookiePostman的响应数据分三个主要区域对应三种不同的提取目标Body响应体绝大多数业务数据都在这里JSON格式居多。比如登录接口返回的access_token、用户信息接口返回的user_id、订单接口返回的order_no。这是最常打交道的区域。Headers响应头有些服务端习惯把token、session id放在响应头的自定义字段里比如Authorization、X-Token、Set-Cookie。我做过一个老系统的接口token既不放在Body也不放在Cookie而是塞在一个叫X-Auth-Token的响应头里不知道的人找半天都找不到。Cookie基于会话的验证方式在传统项目中仍然很常见。登录成功后服务端下发一个Session ID到Cookie后续请求自动携带。Postman对Cookie有自动管理机制但某些场景仍需手动提取。2.2 按提取语法划分JSONPath、正则表达式、文本处理JSONPath专门用于从JSON结构里取数据写法类似data.token、data.user_info.id。这是最推荐、最直观的方式前提是响应体是合法JSON。正则表达式用于从任意文本中提取匹配模式的内容。当响应体是HTML、XML、纯文本或者你想从一段JSON字符串中“硬抠”某个值时正则就是兜底方案。文本处理函数Postman的Tests脚本里还可以用JavaScript的字符串方法比如split()、substring()、indexOf()组合处理虽然笨拙但在某些脏数据场景下反而最稳。2.3 按存放位置划分环境变量、全局变量、集合变量提取出来的数据得有个“容器”装着Postman提供了三类变量容器变量类型作用范围典型使用场景全局变量Globals所有集合、所有环境都能访问通用配置比如基础URL、公共测试账号环境变量Environment仅在指定环境下生效区分开发、测试、生产环境的token、域名集合变量Collection Variables仅当前集合内生效与具体业务链路强相关的数据比如订单号、用户ID我的使用习惯是运行环境相关配置放环境变量业务链路的中间数据放集合变量真正全局通用的东西才放全局变量。太多人图省事一股脑塞Globals结果多环境并行测试时各种串数据排查起来非常酸爽。3. 五种主流数据提取方式实战拆解这五种方式基本覆盖了我在实际项目中遇到的95%的场景。我会逐个讲清楚适用场景、标准写法、注意事项以及我踩过的坑。3.1 JSON响应提取最推荐、最稳的方案这是Postman中最常用、也最友好的提取方式。只要接口返回的是标准JSON用一行pm.response.json()就能把响应体解析成JavaScript对象然后像操作普通对象一样取值。// 获取响应JSON对象 const jsonData pm.response.json(); // 提取token假设结构{ data: { token: abc123 } } const token jsonData.data.token; // 存入环境变量供后续请求使用 pm.environment.set(token, token);这里有几个细节值得强调第一pm.response.json()的底层实现是先把响应体字符串做JSON.parse如果响应体不是合法JSON这一步会直接抛异常导致测试脚本中断。所以稳妥的写法是加个try-catch或者先判断响应头里的Content-Type是否为application/json。try { const jsonData pm.response.json(); pm.environment.set(token, jsonData.data.token); } catch (e) { console.log(响应体不是合法JSON, e); }第二如果返回的是数组结构需要通过索引取值。比如获取用户列表第一个用户的IDconst jsonData pm.response.json(); // 假设结构{ data: [ { id: 1, name: 张三 }, ... ] } const userId jsonData.data[0].id; pm.environment.set(userId, userId);第三当JSON层级很深时逐层点属性容易因某个中间层为null而报错。比如jsonData.data.user.profile.id如果user字段缺了整行就崩了。可以用可选链写法Postman的Node版本支持来防御const userId jsonData?.data?.user?.profile?.id; if (userId) { pm.environment.set(userId, userId); }另外可以用变量来动态指定JSONPath的键名。比如这个例子中字段名本身是从上一个接口动态获取的你就可以先取出键名再通过[]语法取属性const keyName pm.environment.get(fieldName); const value jsonData.data[keyName];3.2 正则表达式提取兜底万能方案正则提取是稳定性仅次于JSON提取的方案适用于任何文本内容。Postman里提供pm.response.text()方法拿原始响应字符串然后配合JavaScript的match()方法提取目标内容。场景一响应体是JSON但结构不规范比如某个字段值里混了多余字符你想硬取。const responseText pm.response.text(); // 提取token:xxx中的xxx const matchResult responseText.match(/token\s*:\s*([^])/); if (matchResult) { pm.environment.set(token, matchResult[1]); }场景二响应体是HTML或XMLJSON提取完全没法用。比如一个老系统返回的是一段XML里面包含一个sessionIdabc123/sessionId。const responseText pm.response.text(); const matchResult responseText.match(/sessionId(.*?)\/sessionId/); if (matchResult) { pm.environment.set(sessionId, matchResult[1]); }正则提取的坑主要体现在两个方面一是贪婪匹配。默认情况下.*会尽量匹配更多内容这在某些场景下会“多吃”字符。比如字符串a1b2c3正则a.*b贪婪模式下会匹配到a1b而不是a1b看起来一样但如果目标字符串是a1b2b贪婪模式会匹配整个a1b2b。所以提取时建议用非贪婪模式.*?// 错误示例贪婪可能匹配过多 /id:\s*(\d).*name:\s*([^])/ // 正确示例用?限制范围 /id:\s*(\d).*?name:\s*([^])/二是转义问题。正则里的特殊字符如点号、括号、反斜杠都需要用\转义。很多人写正则提取JSON中的邮箱时写成/.*(.?)(.?).*/结果匹配不到就是因为漏了转义。我自己的习惯是能不用正则就不用正则。正则的可读性差、调试成本高、还容易因服务端返回格式微调而全部失效。只有碰到JSON提取解决不了的场景才祭出正则这把“大锤”。3.3 响应头提取拿Token的另一种姿势有些系统不像常规RESTful API那样把token放在响应体里而是放在响应头的自定义字段中。比如我之前对接过一个内部平台登录接口返回后token放在名为X-Access-Token的响应头里Body里只有一堆用户资料不少新手第一次接这种接口都懵了。Postman提供pm.response.headers对象来访问响应头// 方式一通过all()遍历 const headers pm.response.headers.all(); let accessToken ; headers.forEach(item { if (item.key X-Access-Token) { accessToken item.value; } }); // 方式二直接按key查更简洁 const accessToken pm.response.headers.get(X-Access-Token); pm.environment.set(token, accessToken);注意headers.get()对大小写不敏感所以X-Access-Token和x-access-token都能查到。但如果响应头里同一个key出现多次这种情况在Set-Cookie上比较常见get()只返回第一个匹配值需要遍历才能拿到完整的。另一个容易被忽略的细节是响应头的值可能带前缀比如Authorization: Bearer eyJhbGciOi...你需要先拆掉前缀再存const authHeader pm.response.headers.get(Authorization); // 去掉Bearer 前缀 const token authHeader.replace(/^Bearer\s/i, ); pm.environment.set(token, token);3.4 Cookie提取与自动管理的双轨制Postman对Cookie有一套自动管理机制如果你在请求配置里启用了“Enable Cookie Jar”登录接口返回的Set-Cookie会被自动存储后续同域请求会自动携带不需要手动提取。但自动管理在以下场景会失效你要拿Cookie里的某个具体值比如JSESSIONID传给不同域名的请求或者放进请求体里。接口的Cookie策略比较特殊Postman的Cookie Jar没有正确捕获。多个测试环境并行时Cookie相互干扰。这时候就需要手动提取。Postman里没有专门的pm.response.cookies但可以从响应头的Set-Cookie字段里解析const setCookieHeader pm.response.headers.get(Set-Cookie); if (setCookieHeader) { // 格式类似JSESSIONIDabc123; Path/; HttpOnly const matchResult setCookieHeader.match(/JSESSIONID([^;])/); if (matchResult) { pm.environment.set(sessionId, matchResult[1]); } }这里有个坑Set-Cookie可能不止一个比如一次响应里同时下发SESSIONID和USER_COOKIE每个都是独立的响应头值。如果headers.get()只拿到第一个你可能提取不到想要的那个。稳妥做法是遍历所有头再拼接匹配const headers pm.response.headers.all(); headers.forEach(item { if (item.key.toLowerCase() set-cookie) { const matchResult item.value.match(/USER_COOKIE([^;])/); if (matchResult) { pm.environment.set(userCookie, matchResult[1]); } } });3.5 处理嵌套结构与动态字段名接口返回的数据不会永远是扁平的{ token: xxx }更多时候是嵌套好几层的对象和数组。比如订单详情接口返回{ code: 0, data: { order_list: [ { order_id: ORD20250101001, items: [ { sku_id: SKU001, price: 99.00 } ] } ] } }你想提取第一个订单的ID或者第一个商品的SKU写法就是const jsonData pm.response.json(); const orderId jsonData.data.order_list[0].order_id; const skuId jsonData.data.order_list[0].items[0].sku_id; pm.environment.set(orderId, orderId);这种逐层访问的写法最大的风险是某个中间层null了。服务端大促期间数据异常order_list可能为空数组或者接口直接返回错误对象取值的时候就会报Cannot read properties of undefined。我写提取脚本时都会加一层空值判断宁可多写两行也不要跑挂了之后半夜被报警电话吵醒。另外有一种比较骚的场景字段名是动态的。比如按时间戳返回数据的接口响应结构是{ data: { 20250101120000: { count: 10 }, 20250101130000: { count: 20 } } }你需要取出第一个时间戳对应的count值。这时就得用Object.keys()动态获取属性名const jsonData pm.response.json(); const keys Object.keys(jsonData.data); if (keys.length 0) { const firstKey keys[0]; const count jsonData.data[firstKey].count; pm.environment.set(count, count); }这种写法我在做数据看板类接口时经常用到普通JSON取值方式完全推不动。4. 关联落地全流程从登录Token到一条完整业务链提取方式掌握得再多最终也要落到完整流程里才能发挥作用。下面我用最典型的“登录—创建订单—查询订单”三个接口完整演示一遍关联的搭建过程。这是接口测试里最基础也最核心的链路你能把这条链路走通后面遇到复杂的业务场景只是同样的套路换个变量名。4.1 第一步规划变量命名与环境很多新手一上来就乱写变量名token、Token、TOKEN混着用跑到后面都不知道自己存的到底是哪个。我的建议是建立一套统一的命名规范最好带前缀区分用途。变量名含义示例baseUrl环境基础地址https://api.test.comauthToken登录令牌eyJhbGciOi...userId当前用户ID10086orderId创建的订单号ORD20250101001requestId流水号REQ123456然后在Postman里先建好环境点击右上角眼睛图标 → Environments → Create New Environment填入环境名和基础变量。我这里创建了一个叫Test的环境先放baseUrl和公共账号信息。4.2 第二步登录接口中提取并存储Token登录接口的请求体是账号密码发送后响应体通常会返回token。我们在Tests标签页里写提取脚本const jsonData pm.response.json(); // 断言接口返回成功 pm.test(登录返回code为0, function () { pm.expect(jsonData.code).to.eql(0); }); // 提取token并存入环境变量 pm.environment.set(authToken, jsonData.data.token); pm.environment.set(userId, jsonData.data.user_info.id);注意Tests标签页里的脚本是在接口响应返回后自动执行的所以可以在这里做断言取值二合一。但如果取值失败比如接口返回了错误信息而不是tokenjsonData.data.token会报错而且整个脚本中断。更稳妥的方式是断言和提取分开处理或者用上一节提到的try-catch包裹。我个人的习惯是断言单独写一个pm.test提取脚本用try-catch包一层这样即使提取失败测试结论里也能看到是断言挂了还是提取挂了排查起来不用从头翻日志。4.3 第三步创建订单接口中引用Token创建订单接口请求头需要带上登录拿到的token请求体里可能需要用到用户ID。在请求的Headers标签页里把token字段的值写成Authorization Bearer {{authToken}}在BodyJSON格式里把用户ID写为{ user_id: {{userId}}, product_id: SKU001, quantity: 1 }Postman的{{变量名}}语法会在发送请求时从当前环境变量中自动取值替换。这里有个容易踩的大坑环境选错了。如果当前没选中Test环境{{authToken}}就替换不了请求头发出去是个空值服务端直接401。所以我每次在Postman右上角都要确认当前环境是哪套尤其是多环境并行测试的时候更是要小心。创建订单接口成功之后我们还得把响应里的orderId提取出来存好留给下一个接口用。所以在创建订单接口的Tests里再写const jsonData pm.response.json(); pm.test(创建订单返回成功, function () { pm.expect(jsonData.code).to.eql(0); }); if (jsonData?.data?.order_id) { pm.environment.set(orderId, jsonData.data.order_id); }4.4 第四步查询订单接口中验证关联结果查询订单接口请求参数或路径里引用上一步存的订单号GET {{baseUrl}}/order/{{orderId}}同时请求头里依然带上{{authToken}}。发送请求后如果返回的订单号正是刚才创建的那一个说明整条链路的关联已经跑通了。到这里一条简单的数据链路就闭环了登录提取token → 创建订单提取orderId → 查询订单引用orderId。你手动把这三个接口按顺序点一遍每一步的取值都在自动进行。如果再配合Postman Collection Runner或者Newman命令行工具这条链路就能一键自动化跑回归。4.5 高阶用法在Tests里做二次加工有时提取出来的数据不能直接用需要加工。最常见的场景就是对token过期时间的处理。有些登录接口返回的token里带了过期时间戳你存变量时可以同时用当前时间判断有效期如果快过期了就先重新登录再取值const jsonData pm.response.json(); const token jsonData.data.token; const expiresIn jsonData.data.expires_in; // 秒为单位 // 计算过期时间并写入日志 const expireAt Date.now() expiresIn * 1000; console.log(Token将在, new Date(expireAt).toLocaleString(), 过期); pm.environment.set(authToken, token);这种做法在自动化回归测试里很有价值因为token过期是导致接口测试批量失败的“第一大杀手”。很多团队一跑半夜的定时任务就收到一堆401报错就是因为没做token有效期管理。你可以在校验请求前先做一次token有效性的简单判断思路比死等报错再处理要主动得多。5. 关联测试中的常见问题与排查技巧这部分我整理了几个最典型的问题都是我在实际项目中踩过的坑做成速查表方便你对照排查。5.1 变量提取不到请求里显示原样{{xxx}}这种是最常见的现象是发送请求后{{authToken}}没有被替换服务端收到的是字面量字符串。原因一环境变量没有存上。可能提取脚本里赋值失败了常见原因是JSON路径写错。排查方法是在Tests里加一行console.log(jsonData)打开Postman的Console快捷按钮在左下角看响应体结构再回去改路径。原因二当前没选中环境。右上角环境选择器没选对或者选的是另一个环境。排查方法点开环境变量的值看是否为空。这个原因占了至少三成别笑真的很多人被它卡住过。原因三变量名拼写不一致。提取时存的是authToken引用时写的{{auth_Token}}少个下划线天然匹配不上。建议一律用小写下划线风格命名并养成用自动补全的习惯。5.2 Token失效导致批量请求401原因登录接口和业务接口之间间隔时间太长或者token刷新机制没处理好。解决方案有三层第一层在业务集合最前面加一个“前置请求脚本”执行前先重新登录并刷新token保证token是最新的。第二层在Tests里检查响应状态码发现401就调用登录接口重新提取token再重发本请求重发逻辑可以借助pm.sendRequest实现。第三层利用Postman的setNextRequest()控制执行顺序让登录请求始终优先执行。// 集合级别前置脚本示例开始前先保证token有效 const requestOptions { url: pm.environment.get(baseUrl) /login, method: POST, header: Content-Type: application/json, body: { mode: raw, raw: JSON.stringify({ username: pm.environment.get(username), password: pm.environment.get(password) }) } }; pm.sendRequest(requestOptions, function (err, res) { if (err) { console.log(登录失败, err); return; } const jsonData res.json(); pm.environment.set(authToken, jsonData.data.token); });5.3 关联数据在循环迭代时串号用Collection Runner跑多组数据时如果每组数据都创建了订单但orderId一直存的是同一个变量那么下一次迭代时上一次的orderId会被覆盖。如果某个接口执行失败保存的orderId可能还是上一轮的导致后续请求查错了数据。解决方案是在迭代开始前清空或重置关键变量或者用更有辨识度的变量名比如追加迭代序号。我习惯在集合的“Pre-request Script”里重置变量pm.environment.unset(orderId); pm.environment.unset(requestId);这样每轮迭代开始时上一轮的“脏数据”就被清掉了避免串号。5.4 响应体是JSON但pm.response.json()报错出现这个问题的原因通常是响应体不是纯JSON而是带BOM头、或者夹杂了HTML内容。比如某些网关超时返回的是一段HTML错误页但HTTP状态码还是200。排查方法先用console.log(pm.response.text())看原始内容确认里面的结构。如果夹杂了非JSON字符就需要先用正则把JSON部分截出来再解析const responseText pm.response.text(); // 从HTML或包装结构中截取JSON const jsonStart responseText.indexOf({); const jsonEnd responseText.lastIndexOf(}); const jsonStr responseText.substring(jsonStart, jsonEnd 1); const jsonData JSON.parse(jsonStr);5.5 不同环境切换时变量值错乱开发、测试、生产环境共用一套变量名但值不同切换环境后引用的值还是旧的。根本原因把本该放环境变量的放到了全局变量。排查思路检查你存的变量到底在哪个作用域里。右键点击变量名可以查看在哪个容器里。建议环境相关变量一律放环境变量全局变量只放公共静态配置。我个人的小技巧是在变量名里带环境前缀比如test_authToken、prod_authToken。虽然看起来冗余但多环境并行调试时非常清晰永远不会拿错。6. 一些让关联更顺手的扩展技巧正文的核心内容已经覆盖最后分享几个我日常工作中总结的顺手技巧帮助你快速判断和排查问题。6.1 用Postman Console实时盯变量Postman左下角有个Console图标点开之后能看到每个请求的详细日志包括请求头、请求体、响应数据以及你在脚本里打的console.log。排查关联问题时第一步永远是打开Console跑一遍流程看每个接口的提取脚本是否执行成功、变量是否赋值成功。这比盲目改脚本高效得多。6.2 利用集合变量做业务快照如果你需要在测试过程中随时查看某条业务数据比如当前订单的状态可以把关键业务数据同时写入集合变量然后在Postman界面右侧快速查看不需要再手动发查询请求。pm.collectionVariables.set(currentOrderStatus, jsonData.data.status);这个变量在集合内所有请求中都能引用适合快速传递状态数据。6.3 用pm.sendRequest做数据预置有些业务链路需要前置数据比如创建订单前必须先有商品库存。你可以在集合的“Pre-request Script”里用pm.sendRequest动态调用接口来准备数据。比如先查库存如果不足就调用补库存接口最后再把商品ID存入变量供后续请求使用。这样整个集合跑起来更稳定不会因为前置数据缺失而中断。6.4 关联与数据驱动的搭配如果关联变量需要同时支持多组测试数据可以配合Postman的Data文件CSV/JSON一起使用。数据文件里的字段用{{字段名}}引用而接口之间关联的数据用环境变量或集合变量引用。这样既能做参数化批量测试又能维持接口之间的数据传递。我举个实际例子订单接口的测试数据文件里有一列product_id数据驱动跑每一行时orderId都是从关联提取的新值两者互不冲突。这是目前做接口自动化最推荐的组合方式既灵活又规范。7. 最后的几句个人体会从我这几年的实际经验来看把关联做好是接口测试自动化从“能跑”到“跑得稳”的关键分水岭。很多人一开始只会在单个接口里手动调参数觉得自动化很玄学其实核心就两件事会提取变量、会引用变量。这两件事打通之后剩下的就是重复工作。这套方法最奇妙的地方在于当你把一条业务链路完整串起来之后后续的日常回归、冒烟测试都变得非常简单。只需要配置好定时任务整个Postman集合就能自动跑起来你只要看最后的测试报告就行。我亲眼见过一个测试团队以前回归十几个接口要一个多小时改成自动化之后十分钟内跑完接口问题还能第一时间报出来效率和信心都在成倍增长。最后送你一个小技巧真正遇到提取不到数据的时候先不要急着改脚本打开Console看原始响应结构用肉眼确认字段路径再动手写提取。这个顺序能帮你省掉至少一半的试错时间。