Jmeter提取器实战:正则与JSON提取器实现接口数据关联
刚入行做接口测试的时候我卡得最久的不是怎么发请求而是怎么把上一个接口返回的数据传给下一个接口。登录接口返回了一个token下一个接口必须带着这个token才能访问我当时的做法是手动复制粘贴到请求头里。单个接口这么干没问题一旦跑批量、上压测、做业务流程串联这种方式直接就废了。后来我学会了Jmeter的提取器——正则表达式提取器和JSON提取器这两个工具基本解决了日常工作中九成以上的数据关联问题。这篇内容就是讲这两个提取器的基础用法。适合刚刚接触Jmeter没多久、对参数提取和接口关联还不熟练的人看也适合那些已经会用但经常提取失败、不知道问题出在哪的人参考。我会把原理、配置项、手写规则的方法、实际案例和踩坑记录都梳理一遍尽量做到看完就能用起来。1. 为什么需要提取器接口之间的数据传递1.1 没有提取器时接口关联怎么做实际接口测试中很少有接口是孤立存在的。登录接口返回一个会话ID下单接口要带上这个ID创建订单接口返回一个订单号支付接口要用这个订单号去发起支付。接口与接口之间的数据传递专业说法叫“关联”。没有提取器的时候最原始的方式就是手动复制粘贴。这个做法在调试单个接口时还能忍但只要进入以下场景就撑不住了接口有几十个每个都需要从上一个响应里拿不同字段压测时要模拟大量并发用户每个虚拟用户都有自己的token不可能手动给每个用户配一个业务流程串起来以后接口之间的依赖层级很深参数链路很长靠肉眼复制完全不现实。还有一个更隐蔽的问题手动粘贴的数据是静态的token是会过期的。你把写死的token放到脚本里跑半小时后token失效了整个测试全部失败。这时候你会非常需要一个机制——让Jmeter在请求执行过程中自动从响应结果里掏出想要的数据再自动塞进后续请求里。1.2 提取器的本质后置处理器在Jmeter里正则表达式提取器和JSON提取器都属于“后置处理器”Post-Processor。所谓后置指的是取样器比如HTTP请求执行完、拿到响应结果之后Jmeter会再去执行提取器从响应内容里按规则取出目标数据存入一个变量。这个变量可以被同线程组中后续的取样器引用引用的方式就是标准的Jmeter变量语法${变量名}。用一个最简单的例子说明处理流程HTTP请求1登录 ↓ 响应{token: abc123xyz} ↓ 后置处理器提取响应中的token存入变量 ${mytoken} HTTP请求2查用户信息 ↓ 请求头Authorization: ${mytoken}整个过程是自动的、动态的。每个线程执行的时候都会先去跑登录请求然后用自己的响应结果去提取token再拿着这个token去请求后面的接口。多线程并发时每个线程拿到的都是自己的token互不干扰。这才是接口关联的正确玩法。后置处理器是Jmeter里一个大的组件分类里面还有CSS/JQuery提取器、XPath提取器、边界提取器等但日常用得最多、通用性最强的就是正则表达式提取器和JSON提取器。1.3 两种提取器的适用场景速判很多人纠结我到底该学正则可以了还是直接学JSON提取器我的建议是两个都要会并且要学会判断什么时候用哪个。下面是我自己根据多年的使用经验整理的选型对照表判断维度正则表达式提取器JSON提取器响应格式要求不挑格式文本、JSON、HTML、JS都能处理只适用于标准JSON格式核心原理按字符串模式匹配本质是找“特征”按JSONPath路径定位本质是找“结构”嵌套复杂响应需要精确写表达式层级越深越难维护路径写起来直观一层层进即可响应中出现同名文本容易误匹配需要靠上下文限定结构定位不存在误匹配问题兼容性Jmeter所有版本通用较老版本3.x以下支持有限学习门槛正则语法本身有门槛JSONPath规则简单容易上手从这个表能看出来如果接口返回的是标准JSON优先用JSON提取器因为它精准、易写、好维护。如果接口返回的是HTML页面片段、字符串文本、或者一堆乱七八糟的非标准内容那就只能用正则表达式提取器。我在实际项目中经常遇到返回结构是类似JSON但里面夹杂了多余内容的接口这种时候JSON提取器反而容易出错正则倒是更稳。所以我个人的使用习惯是先判断响应是不是标准JSON是就上JSON提取器不是或者不确定就直接上正则提取器。下面分别展开讲。2. 正则表达式提取器把“虫子”装进变量2.1 正则基础核心元字符速查正则表达式提取器的基础是正则语法。很多人一听正则就头大觉得那是高深莫测的东西。其实做Jmeter提取器用到的正则知识非常有限核心就那么几个符号完全不用把整个正则体系啃完。日常做接口提取高频用到的元字符就这些元字符含义用法示例.匹配任意单个字符换行符除外a.c可以匹配abc、a1c*前面的字符重复0次或多次ab*c匹配ac、abc、abbc前面的字符重复1次或多次abc匹配abc、abbc不匹配ac?前面的字符重复0次或1次跟在*、后表示非贪婪ab?c匹配ac、abc()捕获组把匹配到的内容单独存起来(token)就是把token存进变量|或运算cat|dog匹配cat或dog[]字符集合[0-9]匹配任意数字[a-zA-Z]匹配任意字母\d任意数字等价于[0-9]\d{5}匹配连续5位数字\w单词字符字母、数字、下划线\w匹配一串字母数字下划线^匹配开头在[]里表示非^abc匹配以abc开头的字符串$匹配结尾abc$匹配以abc结尾的字符串\转义符让特殊字符变成普通字符\.匹配真正的点号对提取器来说最常用的组合就是(.*?)。这组符号的意思是非贪婪匹配任意字符。举个例子响应内容是token:abc123我想取 abc123 这个值正则写法是token:(.*?)那括号里捕获到的就是abc123。2.2 提取器界面字段逐个说Jmeter的正则表达式提取器界面字段不算多但每个字段都有讲究很多新手设置不对就是因为没搞懂字段含义。我逐个说一遍尽量用大白话Apply to选择提取范围。默认是“主样本”Main sample only大多数情况保持默认就行。如果你请求有子请求比如页面里的JS、图片请求才需要考虑其他选项。做接口测试时基本不用改。Field to check从哪个字段里提数据。常用的是“Body”响应体对应界面上的“主体”。如果数据在响应头里就选“响应头”Response Headers想要URL里就有“URL”等。这个字段选错是提取不到内容的常见原因之一。Name of created variable变量名。自己起个有含义的名字比如token、orderId。后续就用${token}、${orderId}来引用。Regular expression正则表达式。这是核心中的核心表达式写在括号里的部分会被提取出来。界面设计上一个表达式可以写多个捕获组对应多个变量后面会说。Template占位符模板。$1$表示取第一个捕获组的值$2$取第二个捕获组的值。如果你只写了一个括号那这里就写$1$。多个捕获组时还可以自由拼接比如$1$- $2$。Match No.匹配序号。0表示随机取一个匹配结果1表示取第一个匹配到的-1表示取所有匹配结果用于遍历。如果响应中有多个符合条件的值这个参数决定你到底拿哪一个。大多数情况下填1。Default Values默认值。如果提取失败变量会被赋成这个值。我强烈建议都填上这样排查问题时能明显看出是哪一步提取失败了而不是变量传到一个locate的不明值调试难度会翻倍。你可能已经注意到一个正则表达式提取器里写的正则不是只能写一对括号它可以写多对括号。比如表达式code:(\d),msg:(\w)那 Template 填$1$就取code填$2$就取msg填$1$- $2$就把两个值用横线拼在一起。这样可以一个提取器同时取多个字段。2.3 手写正则让表达式“只咬住”想要的部分正则提取器最容易翻车的地方是正则写得太“宽”或者太“窄”。太宽匹配到一堆不需要的内容太窄匹配不到任何内容。我自己的写正则方法论只有三步第一步打开“查看结果树”看接口的实际响应内容找到目标数据所在的上下文片段。第二步以目标数据为中心往左、往右各找到一段足够独特的“锚点”文本。所谓独特就是这个文本在响应里最好只出现一次不会引起歧义。第三步把目标数据的位置用(.*?)或者([0-9])之类的表达式替代锚点部分原样保留。用一个实际案例说明。假设登录接口返回的响应体是{ code: 0, message: success, data: { token: eyJhbGciOiJIUzI1NiJ9.abc123.def456, userId: 1024, nickname: tester } }我要提取token正则可以写成token:(.*?)这个表达式匹配的是先原样找到token:这个片段然后非贪婪地捕获后面所有字符直到碰到下一个为止。所以捕获到的就是eyJhbGciOiJIUzI1NiJ9.abc123.def456。我想提取userId这个数字可以写成userId:(\d)\d连续匹配数字捕获到1024。如果有人问你这里的点号、引号要不要转义答案是引号不需要因为引号在正则里没有特殊含义但点号在正则里是有特殊含义的任意字符所以要匹配真正的点号得写成\.。比如你要匹配一个IP地址192.168.1.1直接写192.168.1.1也能匹配到但严谨的写法是192\.168\.1\.1防止192x168y1z1这种串也被匹配上。再说说贪婪和非贪婪的区别。正则里默认是贪婪匹配(.*)会尽可能多地匹配内容。还是拿上面的token响应举例如果用token:(.*)去匹配会从 token 后面一直抓到最后一个字符也就是把整个JSON串的尾巴都吞进去了。加上问号变成(.*?)以后就变成了非贪婪匹配匹配到满足条件的最小内容就停下。在提取器里我建议默认用非贪婪写法除非你有特别的把握。token:(.*) # 贪婪会一直吞到响应末尾 token:(.*?) # 非贪婪匹配到第一个 就停2.4 典型示例从登录响应中提取token并通过调试器验证讲了理论来一个从头到尾的完整操作流程。假设你已经新建了一个线程组线程组下有一个登录接口的取样器并且执行过一次确认能拿到正确的响应。操作步骤在登录这个HTTP请求上点击右键选择“添加” - “后置处理器” - “正则表达式提取器”。配置提取器参考如下Name of created variable填入loginTokenRegular expression填入token:(.*?)Template填入$1$Match No.填入1Default Values填入NOT_FOUND保存后在线程组下添加一个“调试取样器”Debug Sampler再添加一个“查看结果树”监听器。执行测试在“查看结果树”里点开调试取样器的响应就能看到loginTokeneyJhbGciOiJIUzI1NiJ9.abc123.def456这样的变量输出。这个调试步骤非常重要。我在带新人时发现很多人配好了提取器就直接去下一个接口里引用变量结果下一个接口报错就不知道是提取的问题还是请求本身的问题。用调试取样器把变量打出来看一眼排查效率能提高很多。Debug Sampler可以在线程组上右键添加 - 取样器 - 调试取样器执行后它会列出当前线程所有变量及其值。确认变量值正确后在下一个HTTP请求里比如请求头或请求体里填入${loginToken}这样Jmeter就会在跑请求之前自动替换成提取出来的真实值。整个关联就通了。这个方法适配范围很广token、订单号、验证码、ID都可以这么提。唯一的差别就是正则表达式的内容怎么组织。3. JSON提取器结构清晰时的首选3.1 JSONPath语法基础JSON提取器是基于JSONPath表达式来定位数据的。JSONPath可以理解为JSON格式专用的路径查询语言它比正则更贴合JSON的结构特点写起来也更直观。如果接触过Linux文件路径JSONPath的上手会非常快。Linux路径是/usr/local/bin从根目录一层层往下走JSONPath类似用点号把层级连接起来。比如这个JSON{ code: 0, data: { user: { id: 1001, name: 张三 }, list: [ {goodsId: A001, price: 9.9}, {goodsId: A002, price: 19.9} ] } }JSONPath表达式是这样写的$.code—— 取code字段结果是0$.data.user.id—— 逐层往下取id结果是1001$.data.list[0].goodsId—— 取list数组第一个元素的goodsId结果是A001$.data.list[*].goodsId—— 取list数组所有元素的goodsId结果是[A001,A002]$..price—— 递归搜索所有层级里叫price的字段结果是[9.9,19.9]JSONPath最常用的就这几个操作符表达式语法含义$根对象.key取当前对象的key属性..key递归搜索所有层级的key属性[0]数组按下标取元素下标从0开始[*]匹配数组所有元素[?(.price 10)]过滤器按条件筛选数组元素过滤器这个有场景比如你要从返回的商品列表里取出价格大于某个值的商品用正则写会想哭用JSONPath过滤器一句话就搞定了。这里先留个印象后面实操会用到。3.2 JSON提取器界面关键配置JSON提取器的配置界面比正则提取器还简单核心就几项Name of created variable变量名。JSON Path expressionsJSONPath表达式路径。Match No.匹配序号与正则提取器的行为一致。0随机1取第一个-1取全部。如果JSONPath表达式定位到的是一个数组而你只想要第一个元素这里就要填1。Default Values默认值还是推荐填上。Compute concatenation var选中后当匹配到多个结果时支持再生成一个拼接后的变量名变量名_ALL把所有结果用逗号拼起来。某些扩展场景有用新手可以先不管。JSON提取器的“JSON Path expressions”一行写一个表达式“Name of created variable”一行对应一个名字两者按行一一对应。所以你也可以在一个提取器里同时配置多个路径一次提取多个字段。有一点要注意JSON提取器要求响应内容必须是标准JSON格式。如果响应前面有一段因为调试输出的log、或者编码问题导致JSON解析失败提取器就会静默失败返回默认值。遇到这类情况先回“查看结果树”里确认响应内容是否是合法JSON可以用在线的JSON校验工具或者直接看高亮颜色判断。3.3 实操示例从嵌套JSON中提取多字段沿用刚才的登录响应案例。响应是标准JSON提取token、userId、nickname三个字段用JSON提取器只需要配三行JSON Path expressions: $.data.token $.data.userId $.data.nickname Name of created variable: jwtToken userId nickname注意路径是写从根开始.data.token表示根下的data对象里的token字段。如果响应体里直接是最外层是token那就是$.token。如果想提取数组里的数据比如一个查询商品列表的接口返回{ code: 0, data: { goodsList: [ {goodsId: G001, name: 手机, price: 2999}, {goodsId: G002, name: 耳机, price: 399} ] } }提取第一个商品的goodsId路径就是$.data.goodsList[0].goodsId如果你想用过滤器提取价格大于1000的所有商品$.data.goodsList[?(.price 1000)].goodsId这个表达式返回的是符合条件的goodsId数组。这里有两个使用分支如果Match No.填1取第一个如果填-1取全部用变量名_ALL的方式引用。3.4 JSON提取器与正则提取器怎么选我总结了一个非常实用的判断口诀能JSON就JSONJSON不行再正则。为什么这么建议结构化提取有天然优势。JSONPath是按路径定位的不会因为某个字段出现多次就取错正则靠特征匹配很容易误伤。比如响应里有字段userId: 1024同时日志信息里也出现了userId: 1024用正则userId:(\d)提取时会返回多个匹配Match No.填1也可能取到你不想要的那个而JSON提取器用$.data.userId定位永远只取结构路径上的那一个值。JSON提取器的性能通常也更好。正则需要对全文做模式匹配复杂度高一些JSON是树形解析路径直接查值。在并发量大的压测场景下这个差别会放大。但JSON提取器不可能是万能的。接口返回的不是JSON时比如返回一个HTML页面里面有动态生成的token或者返回一个重定向的URL片段或者返回一段纯文本中间夹着编号这样的场景JSON提取器完全没法处理只能用正则。另外提一个比较坑的情况有些接口会返回JSONP格式内容比如callback({code:0})这种外层包了一个函数名严格意义上已经不是标准JSON了JSON提取器解析不了。此时要么先把外壳剥掉再解析要么直接用正则把({.*?})捕获出来。我实际遇到的旧系统接口里这个情况并不少见。4. 常见问题与排查技巧实录4.1 提取不到值时的排查路线图提取不到值这是出现频率最高的问题。我在不同项目里帮人排查过无数次总结出一套固定的排查路线照着走基本都能解决先确认响应里确实有目标数据。在“查看结果树”里打开上游请求的响应内容用页面的搜索功能CtrlF搜一下你要提取的数据是否存在。这一步看着傻但真有人把字段名拼错了响应里根本没有这个字段还花半天找表达式的问题。确认提取器挂在了正确的取样器下面。提取器必须作为HTTP请求的子节点存在或者放在HTTP请求后面同级的后置处理器位置。如果放在了别的请求下面那它处理的就不是这个请求的响应。确认Field to check选择正确。最常见的目标在响应体里选了“Body”就行。如果你提取的字段在响应头Response Header里那选Body必然失败。查看结果树里确认正则表达式是不是真的匹配到了。这里有个很多人不知道的调试方法正则表达式提取器在查看结果树里会有一个“正则表达式测试器”模式你可以把响应内容复制进去直接测试正则表达式看看能否匹配、捕获组的值是什么。这比反复执行整个测试计划快得多。JSON提取器虽然没有单独的测试面板但可以把响应内容复制到一个JSON格式化工具里手动验证JSONPath表达式。确认匹配序号是否合理。响应里可能匹配到多个结果Match No.设1但第一个结果不是你想要的那就需要调整正则表达式让锚点更精确或者把Match No.改成其他值。4.2 常见问题速查表现象可能原因解决思路变量引用后是默认值提取器没有匹配到任何内容按上面的路线图排查提取到多个值但只想要其中一个Match No.设置不正确或正则范围太宽调整Match No.缩小正则锚点范围token中间有点号和横线提取出来内容不对正则中的点号、横线被当成特殊字符处理需要转义特殊的元字符如\.JSON提取器取不到值正则却能取到响应不是合法JSON格式检查响应Content-Type检查是否JSONP或前后有额外内容提取出的字符串带双引号正则捕获组把引号也包含进去了调整捕获组边界把引号放在括号外面多线程并发时变量互相串变量使用范围有问题检查变量是否是线程共享确认提取器的作用域是否正确提取器放在线程组下时不生效作用域不对提取器没有绑定到具体请求把提取器移动到对应请求节点下这里特别说一下匹配序号为0和-1的区别。0是随机从匹配结果里挑一个适用于从一批相同格式的数据里随便拿一个的场景。-1是拿全部结果配套的用法是后续用ForEach控制器遍历。比如登录后返回多个菜单权限ID你可以用-1把这些ID都提出来再结合ForEach控制器逐个访问。这两个值用对了能让脚本灵活不少但新手阶段日常填1就够用。4.3 关于调试效率的几点经验调试提取器的过程其实是在和正则表达式、JSON数据格式做斗争。我自己实战下来有一些值得记住的经验第一提取器配置前后一定要先用Debug Sampler看变量值。这一步能帮你把“提取问题”和“请求问题”彻底隔离。变量值对了后面请求报错那就是请求本身的问题变量值不对再回到提取器排查。第二Default Values一定要填且要填一个有明显标识的值。我见过有人默认值留空结果提取失败时变量是空字符串后面接口报了一个500排查了半天才发现是空值传过去了。填一个EXTRACT_FAILED这种一眼就能认出来的默认值失败时错误信息里带着它马上就定位到了。第三写正则表达式的时候在浏览器里调试比在Jmeter里反复跑高效得多。现在很多在线正则工具都能实时显示匹配结果和捕获组把响应内容粘进去表达式边写边看结果确认无误后再填回Jmeter。这比反复运行测试计划的体验好太多。JSONPath也可以用在线JSON工具或本地写个小脚本调试逻辑是一样的。第四注意Jmeter的版本差异。不同版本在“正则表达式提取器”的界面字段名称上可能有些微差异比如汉化版本和英文版本字段对不上但核心配置逻辑是一致的。如果界面显示的字段和你看到的教程对不上优先把这个字段对应的英文名找出来再回来看中文界面大概率能对上。4.4 真实场景案例一个让我印象深刻的生产事故说一个我自己遇到的真实问题。有一次给客户做压测脚本里登录接口返回一个加密字符串之后所有接口都要带着它。我用正则表达式提取器写的表达式是token:(.*)当时在测试环境跑得好好的接口都能正常返回。结果压测一开始大量请求报401未授权。排查了很久发现测试环境的token是短字符串没有其他字段跟它冲突生产环境的token是一个JWT格式的长字符串中间有大量点号和Base64字符更关键的是响应体里token后面还有其他字段。因为这个表达式用了贪婪匹配(.*)把 token 值后面所有内容全吞进去了导致变量值根本不是一个合法token。解决方式是改成非贪婪表达式token:(.*?)加了问号之后匹配到第一个闭合引号就会停下来取到的就是干净的token值。这个例子我一直在内部培训里讲想强调的就是正则表达式的贪婪与非贪婪在这种场景下不是优化点是生死线。如果你之前写的提取器时灵时不灵先看看是不是这里的坑。后来这个项目我在关键接口上换成了JSON提取器路径一行$.data.token直接把这类问题从根上避免掉了。这也是为什么我一直强调能用JSON提取器的场景不要用正则不是正则不行而是正则可变因素太多了JSON提取器从结构上就锁死了路径不容易出幺蛾子。5. 扩展思考提取器的组合用法与进阶方向5.1 多个提取器叠加使用一个请求的响应里可能既有业务数据又有审计信息字段很多、类型很杂。这种情况下我常常会在同一个HTTP请求下挂多个提取器一个用JSON提取器按结构取主要字段一个用正则提取器去抓那些JSON提取器处理不了的碎片信息。比如有的老系统接口返回内容很大核心token是JSON字段但页面里还嵌了一个base64图片资源要拿这个资源地址就得用正则。多个提取器的执行顺序按它们在请求节点下的排列顺序依次执行后执行的提取器可以使用先执行提取器生成的变量。这个特性可以用来做二次提取比如先用正则把某个大段内容捕获出来存入变量再用另一个正则表达式提取器去这个变量里继续匹配更细的数据。5.2 正则提取器While控制器的循环处理正则提取器在配合While控制器时非常灵活。场景是这样的接口异步处理任务第一次轮询时任务还没完成响应里可能没有目标数据过几秒再查一次可能就有了。用正则提取器提取一个状态标记结合While控制器实现“直到取到数据才继续往下走”的逻辑。While控制器的循环条件里可以直接引用提取出来的变量比如判断${taskStatus}是否等于success不等就继续循环。这里有个坑需要注意While条件里引用变量时如果变量不存在或者为空部分Jmeter版本会报错。所以建议设置默认值保证变量始终有值让循环条件可以正常判断。5.3 不只用于接口关联参数化与断言提取器的应用范围不只是接口关联。我在实际工作中还会用它做两件事一是提取数据做参数化。压测时要模拟多组不同用户数据但不想用CSV文件写死就从注册接口的返回里提取一批用户ID塞给后续业务接口轮流使用。配合Match No.设为-1和ForEach控制器可以把这个流程做得非常自动化。二是提取数据做断言。Jmeter的断言功能支持用变量参与判断比如一个响应里返回了一个错误码我可以用正则或JSON提取器把这个错误码提出来然后在断言里判断它是否等于预期值。这样可以把一些复杂的校验逻辑拆成“提取断言”两步写起来比直接在断言里写正则要清晰很多。这两种用法本质上还是提取器的数据流转能力只是目标从“传给下一个接口”变成了“给测试流程提供决策依据”。思路是一样的但能解决的问题范围却大了不少。5.4 学习优先级建议如果你刚接触这块内容我的建议是先把JSON提取器用熟因为它简单、直观、不容易出错。把$.、..、数组下标、过滤器这几种路径写法都练一遍配合真实接口把不同层级的数据都提取一遍这个工具基本就掌握得差不多了。正则表达式提取器建议先掌握点的匹配、星号、加号、问号的组合用法会写(.*?)就够了能解决80%的日常需求。不要一开始就钻到复杂正则里出不来。真想深入的话买本正则相关的书或者系统过一遍语法规则日常写提取器、写断言的时候多练水平自然就上来了。最后再提一个笨但有效的方法把提取器配好之后故意把正则写错几次看看Jmeter报什么错、变量变成什么默认值、接口返回什么异常把这些现象都记下来。踩过这些坑之后再处理实际问题你一眼就能看出是哪种故障排查速度会快很多。我这些年用Jmeter的经验里很大一部分就是从这些报错的蛛丝马迹里积累起来的。