ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

JMeter提取器实战:正则与JSON提取token关联接口参数

JMeter提取器实战:正则与JSON提取token关联接口参数 先抛一个实际场景你刚把登录接口跑通返回了一串token可下一个查询接口死活要拿这个token当入参。手动复制粘一次两次还行压测跑50个线程的时候怎么办总不能一个个手填。这时候Jmeter里的正则表达式提取器和JSON提取器就是干这个用的。它们能从上一个请求的响应里把需要的值“抠”出来存成变量让下一个请求自动引用。这篇文章就把这两个提取器的基础用法掰开揉碎讲清楚配合实际例子说明每一步怎么配、为什么这么配以及我踩过的那些坑。如果你刚接触Jmeter接口测试或性能测试正被“响应里有值但不知道怎么传给下一个请求”卡住这篇就是给你的。已经会用一点但经常提取失败、匹配不到值的老手也可以直接跳到第6部分那部分全是实战问题实录。1. 提取器到底是干什么用的很多新手刚接触Jmeter时会困惑为什么请求A跑完了请求B不能直接用A返回的数据这就要先说清楚接口测试里一个核心概念关联。1.1 从一个登录拿token的例子说起假设现在有一个典型的业务流程客户端调用/api/login提交用户名和密码服务端校验通过后返回一段JSON里面包含access_token过期时间等客户端后续每次请求都要在请求头里带上Authorization: Bearer token否则服务端返回401用Jmeter做接口自动化或压测时你不能写死token因为每次登录返回的token几乎都不同。更麻烦的是同一个脚本可能同时跑100个虚拟用户每个用户登录后拿到的token都不一样必须各用各的。提取器解决的就是这个“动态参数传递”问题。它会按照你设定的规则在指定请求的响应内容里查找目标值找到后把它存入Jmeter内置变量之后在同一线程内的任何请求中直接用${变量名}引用即可。1.2 正则提取器和JSON提取器的本质差异Jmeter提供了多种提取器最常用的就是这两个正则表达式提取器用正则语法从任意文本中匹配内容。它不只适用于JSONHTML页面、XML、纯文本响应头、甚至请求URL都能处理是通用型选手。JSON提取器专门针对JSON格式响应设计使用JSONPath语法定位节点。结构清晰、容错性好不会因为响应里多了一个空格或字段顺序变化就匹配失败。从名字就能看出来前者的核心是“正则表达式”后者的核心是“JSONPath表达式”。选择哪个取决于你的响应格式和提取场景。多数情况下只要后端返回的是JSON我会优先用JSON提取器因为可读性和稳定性都更好。但遇到HTML页面、JS脚本、或者非结构化文本里的动态值正则提取器就是唯一选择了。2. 正则表达式提取器从零读懂每个配置项正则表达式提取器的界面初看有七八个字段不少人第一次打开直接懵了。别急一个一个拆开讲弄懂每个字段是干什么的你就能灵活应对各种提取需求。2.1 界面各字段的含义与设置思路一个典型的正则表达式提取器配置如下配置项示例值作用说明Apply toMain sample only作用于哪个取样器结果一般保持默认Response Field to TestBody从响应的哪个部分提取常用BodyField to checkBody针对Body再细分一般用不到Name of created variabletoken提取结果存入的变量名Regular Expressiontoken:([^])匹配规则Template$1$取第几个捕获组Match No.1取第几个匹配结果Default ValueNOT_FOUND提取失败时的缺省值下面重点说三个容易出错的字段。Name of created variable就是变量名后续用${变量名}引用。注意同一个线程组里不要重复命名否则后提取的会覆盖先提取的。Template模板默认是$1$意思是取正则表达式里第一个括号( )捕获的内容。如果你写了两个捕获组比如想同时提取token和userId可以用$1$和$2$分别引用。这个字段很多人忽略导致明明正则写对了取出来的却是整个匹配文本就是模板没设对。Match No.匹配数字0代表随机取一个1代表取第一个匹配结果-1代表取所有匹配结果存入变量时用变量名_1、变量名_2这种格式区分配合ForEach控制器可以遍历。日常用最多的就是1。2.2 正则写法的关键细节与避坑正则表达式本身是个大话题但Jmeter里做提取常用的核心语法其实不多。给你一份“够用版”速查\d匹配数字\w匹配字母数字下划线.匹配任意字符*表示前面的字符出现0次或多次表示出现1次或多次.*是贪婪匹配会尽可能匹配更多.*?是非贪婪匹配匹配到第一个符合条件的位置就停()表示捕获组提取时结合Template使用[^]表示匹配一个或多个非引号字符这是提取JSON值时的常用写法举个实际例子。假设响应Body是{code:0,message:success,data:{token:abc123xyz,expireIn:7200}}要提取token正则表达式可以写token:([^])这里[^]的意思是不包含引号的一串字符它能准确匹配到abc123xyz不会因为后面还有其他内容而乱匹配。很多新手喜欢写token:(.*)这就是典型的坑。.*是贪婪匹配在整段JSON中会一直匹配到最后一个引号导致提取结果变成abc123xyz,expireIn:7200}这种“过头”的值。虽然非贪婪写法.*?能解决一部分问题但更推荐用[^]这类明确边界的方式它表达的是“你不是引号就一直取”语义更严谨也基本不受响应结构变化影响。再说一个实际经验如果响应是HTML比如要提取一个隐藏域的valueinput typehidden namecsrf_token value8f9a7b6c正则表达式可以写namecsrf_token value([^])这样能准确匹配到8f9a7b6c。如果页面结构经常变可以把匹配范围放宽一些比如只匹配value([^])但这样风险是页面里可能还有其他input的value导致提取错值。我的习惯是尽量带上更多上下文定位比如前面的namecsrf_token部分定位准确后再提取目标值。2.3 Debug Sampler提取结果的“照妖镜”配置完正则提取器怎么确认提取的值对不对最简单的办法是用Debug Sampler。在Jmeter里添加路径线程组 → 添加 → Sampler → Debug Sampler。运行后打开“查看结果树”在Debug Sampler的响应数据里能看到所有Jmeter变量的当前值包括你刚提取的token。我每次调试提取器都会在脚本里挂一个Debug Sampler确认变量有值了再继续下一步。如果Debug Sampler里显示变量为空或者Default Value说明提取失败需要回头检查正则或响应内容。这个习惯能帮你把“到底提没提取到”的问题快速定位而不是等到后面请求报401了才回头查。3. JSON提取器JSONPath才是核心JSON提取器界面比正则提取器简洁许多但正因为简洁很多人反而不清楚该怎么写JSONPath。这里把语法和配置一并讲透。3.1 JSONPath基础语法速成JSONPath可以理解为JSON版的XPath用于定位JSON中的节点。常用语法如下表达式作用$根节点$.data.token取data对象下的token字段$.data.list[0]取list数组的第一个元素$..token递归搜索所有层级的token字段$..[?(.nametest)]过滤取name值为test的元素$.data.list[*].id取list数组中所有元素的id字段在Jmeter的JSON提取器里$开头的表达式最常用。比如上面那个登录响应的例子{code:0,message:success,data:{token:abc123xyz,expireIn:7200}}提取token的JSONPath就是$.data.token如果要提取expireIn就是$.data.expireIn你看比正则表达式直白多了不需要关心是引号还是逗号路径一写就能定位。3.2 JSON提取器的配置字段Jmeter的JSON提取器JsonPath Extractor主要配置项Names of created variables变量名多个用英文逗号分隔JSON Path expressions对应的JSONPath表达式多个用英文逗号分隔Default Values提取失败时的缺省值多个用英文逗号分隔Match No.0随机-1取全部具体数字取第N个一个很实用的技巧这个提取器支持一次配置多个变量和多个表达式用英文逗号分隔即可。比如同时提取token和expireIn可以这样填Names of created variablestoken,expireInJSON Path expressions$.data.token,$.data.expireInDefault ValuesNOT_FOUND,0这样一次取样就能拿到两个变量不用挂两个JSON提取器脚本看起来干净很多。3.3 处理JSON数组返回值实际接口中经常遇到数组比如{data:{list:[{id:1,name:A},{id:2,name:B}]}}如果我想提取第一个元素的idJSONPath是$.data.list[0].id如果我想提取所有元素的idJSONPath是$.data.list[*].id这里有一个细节要注意用[*]匹配多个结果时如果Match No.填的是1Jmeter会默认取第一个结果如果填-1变量会以变量名_1、变量名_2的格式存储。比如变量名是ids提取后会有ids_11、ids_22。这一点和正则提取器的行为一致。另外JSON提取器在Match No.设成1时如果JSONPath匹配到的是一个数组节点比如直接写$.data.list取到的值可能是[A,B]这种数组形式而不是单个值。需要取具体某个元素时还是应该把下标写进JSONPath里而不是依赖Match No.去挑。4. 两种提取器怎么选实战判断逻辑讲了这么多很多人会问那到底什么时候用正则什么时候用JSON我的判断标准很简单三个问题过滤一遍响应是不是JSON格式是 → JSON提取器优先响应是不是HTML/XML/纯文本是 → 正则提取器响应结构复杂既要解析JSON又要处理特殊字符大概率还是JSON提取器更省心4.1 按响应格式和字段特点选择响应是JSON时优先JSON提取器。原因很直接JSONPath是按数据结构定位的不依赖字段顺序不关心值是字符串还是数字只要路径对就能取到。相比之下正则表达式要写各种边界条件稍不注意就匹配过头或匹配不到。但也不是绝对。有一种情况我会用正则响应里的JSON字段名和值都经常变化但值的格式很固定。比如某个字段的值总是一串32位十六进制字符串我可以直接写([a-f0-9]{32})这比JSONPath更“容错”因为即使外层字段名变了只要值格式没变照样能提取到。这种场景在接手混乱的老项目时尤其有用。响应是HTML时只能正则。HTML没有统一的JSON结构JSONPath无从谈起。比如要提取网页里的csrf_token、某个隐藏域的值、JS变量里的配置都得靠正则。不过HTML结构复杂写正则时要格外小心最好带上足够的上下文定位。4.2 同一个场景两种写法的对比用一个真实接口来对比一下。假设响应是{status:ok,result:{orderId:SO20240618001,amount:199.00}}提取orderId。JSON提取器写法JSONPath$.result.orderId配置变量名orderIdDefault ValueNOT_FOUND正则表达式提取器写法正则orderId:([^])模板$1$匹配数字1两种都能拿到SO20240618001。但从可维护性来说JSON提取器明显更易读一眼就能看出要的是哪个字段。如果响应字段顺序调整成{result:{amount:199.00,orderId:SO20240618001}}正则依然能匹配因为它只看orderId:...这个片段JSON提取器也不受影响因为路径$.result.orderId是结构化的。但如果嵌套层级加深比如增加了一层{status:ok,result:{data:{orderId:SO20240618001,amount:199.00}}}正则表达式就得改成orderId:([^])这行正则其实不用改因为orderId:...这个模式没变。但如果页面里还有其他地方出现orderId正则就可能匹配到错误的位置JSON提取器写清楚完整路径就不会有这个问题。4.3 提取多个字段时的配合策略实际业务里很多场景需要从同一次响应中提取多个字段。比如登录接口除了返回token还返回userId、nickname后续多个请求分别要用。这种情况我一般优先用JSON提取器一次配多个变量简单高效。只有在某个字段藏在HTML里、不得不正则时才会加一个正则提取器专门处理那一个值。混用没问题只要变量名不冲突即可。还有一个批量提取的思路如果你需要把列表里所有id都取出来可以用JSON提取器配合[*]和Match No.-1把ids_1、ids_2... 都存好再用ForEach控制器遍历逐个请求。这个方法在做“列表页→详情页”的业务流时非常实用比如先查询订单列表再对每个订单号分别查询详情。虽然ForEach本身是另一个话题但提前知道提取器能提供这种批量能力设计脚本时思路会宽很多。5. 完整实战登录拿token再请求下单接口前面讲了原理和配置这一部分用一套完整流程把提取器的使用串起来。你可以跟着步骤走一遍跑通了就算真正入门了。5.1 场景设定与测试计划结构假设待测系统提供两个接口POST /api/login请求参数为JSON返回内容如下{code:0,msg:success,data:{token:abc123xyz,userId:10086}}POST /api/order/create需要在请求头X-Token中带上登录返回的token同时在请求体JSON中带上userId服务端才会正常创建订单。这个场景是典型的“前一个接口的响应作为后一个接口的输入”。测试计划结构如下测试计划线程组取样器登录接口HTTP Request后置处理器JSON提取器或正则表达式提取器调试器Debug Sampler验证用取样器创建订单接口HTTP Request5.2 登录接口与JSON提取器配置先添加登录接口的HTTP Request协议http服务器名称或IP你的测试环境地址方法POST路径/api/loginBody Data{username:test,password:123456}运行一次在“查看结果树”里确认登录成功响应里确实有token字段。然后右键登录请求 → 添加 → 后置处理器 → JSON提取器配置如下Names of created variablestoken,userIdJSON Path expressions$.data.token,$.data.userIdDefault ValuesLOGIN_FAILED,0Match No.1保存后再添加一个Debug Sampler到线程组运行脚本查看Debug Sampler的输出。正常情况下应该看到tokenabc123xyzuserId10086到目前为止提取成功。这一步如果是正则提取器写法就是Name of created variabletokenRegular Expressiontoken:([^])Template$1$Match No.1Default ValueLOGIN_FAILED两个方式选一个即可这个场景我更推荐JSON提取器理由前面说过了可读性好一次能提取多个字段。5.3 在下一个请求中引用变量现在配置创建订单的HTTP Request路径/api/order/create方法POST请求头添加X-Token值为${token}Body Data{userId:${userId},productId:1001,count:2}注意${userId}如果提取出来是纯数字直接放在JSON里没问题如果是字符串记得手动加引号比如userId:${userId}。这是很多新手会踩的坑省得后面排查半天。运行整个脚本如果创建订单接口返回200或0具体看业务约定说明token和userId都正确传递了。如果返回401优先检查请求头里token是不是空的这时候回头看Debug Sampler就能快速定位。5.4 执行顺序与作用域的坑Jmeter后置处理器的作用域很关键JSON提取器挂在哪个请求下就从哪个请求的响应中提取提取后变量在该线程内后续所有请求中都可以引用。一个常见的错误是把提取器挂在“线程组”下而不是挂在具体请求下面。这样Jmeter会报错或者提取不到值因为提取器需要关联到一个具体的取样器结果。正确的做法是选中目标HTTP请求右键 → 添加 → 后置处理器 → 选提取器。另一个容易忽略的点是Jmeter变量默认只在当前线程内有效。如果你有多个线程组或者用了 setUp 线程组做登录、普通线程组做业务跨线程组使用变量时不能直接${token}得用${__setProperty(globalToken, ${token})}把它转成Jmeter属性再用${__property(globalToken)}读取。这个属于进阶内容但提前知道能避免不少弯路。6. 常见问题与排查技巧实录这一部分整理我实际使用中遇到的高频问题按“现象→原因→解决”的思路写方便你对照排查。6.1 提取结果为NULL或Default Value现象Debug Sampler里变量值是空或者显示的是你填的Default Value。排查步骤按顺序来先看响应内容是否真包含目标值。用“查看结果树”打开上一个请求的响应搜索关键字确认不是接口本身没返回。检查是不是字段选错了。正则提取器默认在Body里提取如果目标值在Response Headers里需要把“Response Field to Test”改成“Response Headers”。检查正则表达式是否匹配过头或匹配不到。把正则表达式复制到任意正则测试工具里粘贴一段实际响应内容验证能匹配到目标值再回填Jmeter。检查模板是否写对。如果捕获组有多个模板$2$可能对应不到目标值。最容易坑人的地方是转义。正则表达式里如果在Jmeter中使用某些字符需要转义尤其是括号、引号和反斜杠。比如你直接在Jmeter里写token:(.)这没问题。但如果响应里的引号本身是转义的比如完整值是这样的JSON字符串{\token\:\abc123\}那在Jmeter里写正则时双引号可能需要写成\也就是\token\:\([^\])\这个细节在调试时非常恼人我的建议是不确定时优先用JSON提取器绕过正则转义问题。6.2 JSON提取器返回的是数组形式现象JSON提取器取出的值是[abc123]而不是abc123。原因JSONPath表达式匹配到的节点是数组或者表达式写得太宽比如直接写$..token返回的可能是个数组。解决把JSONPath写得更精确加上具体的下标比如$.data.token如果只想取第一个值也可以在表达式里就限定比如$.data.list[0].name而不是等到Match No.里设置。不要依赖Match No.去“剥掉”数组那样容易出错。6.3 正则表达式匹配到多个值但只需要其中一个现象页面上有多个input标签想取第二个的值但正则默认取第一个。解决在Match No.里填2就是取第二个匹配结果。填0则随机取一个。如果需要把匹配到的所有值都用作参数填-1然后用变量名_1、变量名_2这种格式引用。给个具体例子响应里有三个a href/detail/1001、a href/detail/1002、a href/detail/1003你想提取所有ID正则写href/detail/(\d)Match No.填 -1变量名设为ids则调试结果会看到ids1001默认第一个ids_11001ids_21002ids_31003注意变量名本身不带下标的那个会保存第一个匹配结果。批量场景用ids_1这种方式遍历即可。6.4 提取的变量在下一个线程组无法引用现象登录线程组提取的token业务线程组里引用不到。原因Jmeter变量默认是线程级作用域两个线程组之间不共享。解决用Jmeter属性Property做中转在登录线程组的JSON提取器后加一个BeanShell取样器或JSR223取样器写入props.put(globalToken, ${token});业务线程组里用${__P(globalToken,)}读取JSR223脚本建议用Groovy配置更灵活也更符合新版Jmeter的习惯。具体写法props.put(globalToken, vars.get(token));读取时在需要引用的地方写${__P(globalToken,)}这个方法在复杂脚本里几乎必用建议收藏。6.5 中文内容乱码导致正则匹配失败现象响应里包含中文正则提取到的内容是乱码或者匹配不上。原因Jmeter默认按ISO-8859-1解码响应内容遇到UTF-8的中文就乱码。解决在HTTP Request的“内容编码”里填UTF-8或者用“后置处理器 → 前置处理器”先处理编码。最省事的做法是在HTTP Request高级选项里把“响应数据的编码”设置成UTF-8。如果接口返回的是GBK编码就填GBK。这个设置直接影响提取器看到的文本编码不对后续正则写得再好也没用。6.6 提取器执行了但Debug Sampler里看不到现象运行整个线程组后Debug Sampler显示变量为空但单独跑登录请求时提取正常。原因可能是请求失败导致没有响应或者提取器的作用域没覆盖到。检查“查看结果树”里登录请求是否绿色通过同时确认提取器是挂在登录请求下而不是挂在其他请求或线程组下。还有一个隐蔽原因是登录请求发生了重定向Jmeter默认不会自动处理重定向后的响应但提取器作用于“主请求”如果你勾选了“跟随重定向”实际响应可能来自重定向后的页面。此时把“Apply to”改为“Main sample and sub-samples”也许就能解决。结语与个人经验用熟了之后你会发现提取器真正难的不是语法而是搞清楚“当前这个接口到底返回了什么、我要的字段长什么样”。所以我的习惯是写提取器之前先用查看结果树或Postman看一遍实际响应确认字段位置和格式再动笔写表达式。省掉这一步后面反复调试的时间会是写表达式的好几倍。再分享一个实用小技巧正式压测前我会在脚本里保留一个隐藏的Debug Sampler把公共变量都打印出来跑一轮冒烟测试确认所有变量都有值再进入正式执行。这样能避免压测跑到一半才发现变量取空、请求全部失败的尴尬局面。另外给所有提取器都填上明确的Default Value一旦接口异常你能快速从报错信息里发现是哪个环节断了排查效率翻倍。如果只是做接口自动化或轻量级性能测试这两个提取器足够应付九成以上的动态参数关联需求。往后遇到复杂场景比如需要同时从多个接口提取并拼接参数、在循环里批量处理提取结果思路也都是在基础用法上叠加控制逻辑。把这篇文章里的配置和排查方法吃透再去看更高级的用法你会觉得顺很多。
返回列表