web_reg_save_param_ex 函数是 LoadRunner 中进行关联的基础。属于注册型(Service)函数,意味着它的执行不依赖于请求,而是在脚本运行时预先注册一个钩子(Hook),等待后续请求的服务器响应到达时,从中抓取符合条件的数据。
一、注册和捕获机制
先注册,后捕获的异步工作原理:
注册(脚本执行前):在脚本编译和执行时,函数会将其参数(如边界、参数名)注册到Vuser运行时中,声明“在下一个请求完成后,请检查其响应,并按照此规则抓取数据”。
捕获(请求完成后):脚本继续执行,发出下一个请求(如 web_url)。当该请求的响应返回时,运行时引擎会立刻检查响应内容,并根据之前注册的规则进行一致和抓取。
保存和应用:抓取到的文本被保存到指定的 LR参数(如 {CorrParam}) 中,可供后续请求使用。
因此,该函数必须放置在会生成目的响应的请求函数之前,且一般紧邻该请求。
二、函数参数详解
函数签名一般为:
c
int web_reg_save_param_ex(
const char *ParamName,
<List of Attributes>,
<List of Search Filters>,
LAST);
以下是最常用的参数详解:
基础参数:
ParamName,"CorrToken",捕获值的参数名,后续用 {CorrToken} 引用。命名应清晰。
左右边界:
LB,"name=\"csrf_token\" value=\"",左边界,必须是动态值左侧、固定不变的唯一文本。
RB," />" ,右边界,必须是动态值右侧、固定不变的唯一文本。
搜索控制:
Search,"Body" (默认), "Headers", "NoResource", "All",定义搜索范围。"Body" 最常用;"Headers"用于抓Cookie;"NoResource" 可忽略图片等资源,提升性能。
RelFrameID,"1.2",用于处理框架页,指定在哪个框架内搜索。需结合 ORD 使用。
一致和挑选:
ORD,"1" (默认), "All",一致序号。"1" 抓第一个一致项;"All" 抓全部,存入数组 {CorrToken_1}, {CorrToken_2}...
SaveOffset,"10",从一致起点后偏移指定字符数开始保存。用于边界内包含多余前缀时。
SaveLen,"20",限制保存长度。当动态值长度固定或需截断时使用。
转换处理:
Convert,"HTML_TO_URL", "HTML_TO_TEXT",数据转换。HTML_TO_URL 可将 & 转为 &,处理HTML编码。
错误处理:
NotFound,"WARNING", "ERROR",未找到时的处理。"WARNING" 发警告并继续;"ERROR" 直接报错停止。默认是 "ERROR"。
三、实战完整代码
一个处理登录流程中 sessionId 和 csrfToken 的完整案例,演示了标准使用流程。
分析:用户先访问登录页,页面返回一个CSRF令牌(藏在表单里)和一个会话Cookie(在响应头Set-Cookie中)。登录时需同时提交令牌和Cookie。
c
#include "web_api.h"
Action()
{
// 1. 初始请求:访问登录页面,同时关联两个动态值
// 1.1 注册关联,从响应头中捕获 SessionID (JSESSIONID)
web_reg_save_param_ex(
"ParamName=sSessionID",
"LB=Set-Cookie: JSESSIONID=",
"RB=;",
"Search=Headers", // 重点:在响应头中搜索
LAST);
// 1.2 注册关联,从响应体中捕获 CSRF Token
web_reg_save_param_ex(
"ParamName=sCsrfToken",
"LB=<input type=\"hidden\" name=\"csrf_token\" value=\"",
"RB=\" />",
"Search=Body", // 默认就是Body,可省略
"NotFound=WARNING", // 如果页面可能没有令牌,设为警告
LAST);
// 1.3 执行请求,触发关联函数捕获数据
web_url("login_page",
"URL=http://example.com/login",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
// 调试输出:打印捕获到的值
lr_output_message("捕获到的SessionID: %s", lr_eval_string("{sSessionID}"));
lr_output_message("捕获到的CSRF令牌: %s", lr_eval_string("{sCsrfToken}"));
// 2. 登录请求:使用捕获到的动态值
web_add_header("Cookie", lr_eval_string("JSESSIONID={sSessionID}")); // 方式一:手动添加Cookie头
web_submit_data("login_action",
"Action=http://example.com/do_login",
"Method=POST",
"EncType=application/x-www-form-urlencoded",
ITEMDATA,
"Name=username", "Value=testuser", ENDITEM,
"Name=password", "Value=123456", ENDITEM,
"Name=csrf_token", "Value={sCsrfToken}", ENDITEM, // 提交表单中的令牌
LAST);
// 3. 后续业务请求:一般只需继续携带SessionID(通过自动Cookie管理或手动添加)
web_url("get_user_profile",
"URL=http://example.com/api/user/profile",
// Cookie会自动管理,无需手动添加,前提是未禁用Cookie
LAST);
return 0;
}
分析:
双关联注册:在 web_url 前连续注册两个关联,分别针对头部的Cookie和体部的Token。
请求触发:web_url 执行后,其响应会同时被两个关联函数处理,分别抓取值。
使用参数:在登录请求中,通过 {sCsrfToken} 提交令牌,并通过 web_add_header 手动设置Cookie头(这是一种更可控的方式,也可依靠LoadRunner的自动Cookie管理)。
后续会话:登录后,会话一般由Cookie维持,后续请求可自动携带。
四、技巧调试
边界查找和转义:如果边界中包含双引号 " 或反斜杠 \,必须使用 \ 进行转义。如:"LB={\"token\":\"", "RB=\"}"。
使用ORD处理数组:当列表项有多个相同结构时(如商品ID),设 "ORD=All",用 {sItemId_1}, {sItemId_2} 等引用。
JSON响应专用:对于纯JSON API,强烈推荐使用 web_reg_save_param_json。它使用JSONPath(如 "$.data.access_token")提取数据,远比根据文本边界的 web_reg_save_param_ex 更准确、稳定。
调试:
在 Runtime Settings -> Log 中,启用 Extended log 并勾选 Data returned by server 和 Parameter substitution。
回放脚本,在 Replay Log 中搜索 Notify: Saving Parameter 日志行,这是关联成功和否的最直接证据。
对比Warning: The string ... was not found 警告,调整你的左右边界。
五、总结
作为性能测试专家,湖南卓码软件测评有限公司的工程师在实践中总结出以下法则:
位置:web_reg_save_param_ex 必须 放在其目的请求之前,且中间不能有其他会触发HTTP请求的函数。
准确定位:优先使用 web_reg_save_param_json 处理JSON。对于HTML,尽可能寻找最靠近动态值、最独特的边界文本。可先保存完整响应到文件进行分析。
错误处理:生产脚本中,应对重点关联使用 "NotFound=ERROR",保证脚本在关联失败时立即停止,而不是带着错误数据继续执行,产生误导性测试结果。
掌握此函数意味着你已能准确控制HTTP对话中的动态数据流。当面对更复杂的身份认证流程(如OAuth 2.0)时,步骤仍是:分析流程 -> 确定捕获点和使用点 -> 编写准确的关联函数。