在LR中当使用HTML录制方式时,录制的脚本中主要由函数web_link()、web_submit_form()、web_url()、web_submit_data()组成,当使用HTTP录制方式时,录制的脚本中主要由函数web_url()、web_submit_data()组成,主要区别在于:
当使用HTML录制时得到的函数更接近用户的实际操作,脚本代码相对较少且易于理解一些,但是函数之间有前后依赖关系,耦合度较高,其中web_link()用于模拟用户点击页面上的链接;web_url()用于请求某个链接,Mode的值为HTML,如果某个链接页面上有大量的非html元素,例如:js、css、vbs、activeX、applets、各种格式的图片等,则按照录制时的设置来处理,默认为也录制到脚本步骤中,即web_url()函数的EXTRARES选项中,另外,也可以设置成不录制或录制到并发组web_concurrent_start(NULL)和web_concurrent_end(NULL)之间的web_url()函数中。
当使用HTTP录制时得到的函数将包含所有请求内容,每一项需要请求的内容都会得到一个web_url()函数,Mode的值为HTTP,因此脚本中会有大量的web_url()函数,且一般不带EXTRARES选项,得到的脚本没有很强的前后依赖关系,相对HTMP脚本的耦合度低一些,请求的内容也更全一些,一般只在很标准B/S模式下才会选用HTML,多数时候都可以选用HTTP的方式来录制。另外在该录制模式下,也可以设置选择是否使用并发组函数web_concurrent_start()和web_concurrent_end(),如选择则在很多非HTML的元素的请求前后都会加上并发组函数,还可以选择是否只使用自定义函数web_custom_request(),如选择则得到的脚本中所有的web_url()、web_submit_data()函数都将由函数web_custom_request()替代之。
本文的主角web_custom_request()终于上场了,web_custom_request()函数是一个可以用于自定义http请求的“万能”函数,具有web_link()、web_url()、web_submit_data()函数的功能,一般当自定义请求时可以配合函数web_add_auto_header()、web_add_header()等系列函数来自定义其请求头,web_custom_request()函数的Mode字段为HTML时相当于使用HTML录制方式的函数,Mode字段为HTTP时相当于使用URL录制方式的函数,Method字段可以为GET和POST分别用于模拟http的get和post请求。
该函数web_custom_request()的具体参数说明可以参见本片文章结尾的附录。
当使用web_custom_request()函数来发送http的get和post请求时,如果不使用web_add_header相关函数来自定义添加头部,请求中是否带有默认的头部字段?通过使用抓包工具来查看发出的请求中的头部字段:
脚本一:
web_custom_request("web_custom_request",
"URL=http://www.baidu.com",
"Method=GET",
"TargetFrame=",
"Resource=0",
"Referer=",
"Mode=HTTP",
"Body=123",
LAST);
脚本一实际发出的请求:
GET / HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: Keep-Alive
Host: www.baidu.com
Content-Length: 3
123
脚本二:
web_custom_request("web_custom_request",
"URL=http://www.baidu.com",
"Method=POST",
"TargetFrame=",
"Resource=0",
"Referer=",
"Mode=HTTP",
"Body=abcd",
LAST);
脚本二实际发出的请求:
POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: Keep-Alive
Host: www.baidu.com
Content-Length: 4
abcd
由此可知,在使用web_custom_request()函数来模拟HTTP请求时,发出的请求中默认带有部分的http请求头部字段,具体字段如上,如需要修改默认的头部字段或增加其他头部字段就在web_custom_request()函数的前面使用web_add_header()函数来添加,如果要减少某个头部字段或全部自动添加的头部字段就在web_custom_request()函数的前面使用web_remove_auto_header()和web_revert_auto_header(),头部字段所有请求中公共的一些头部可以放在web_add_auto_header()函数中,配合web_add_header系列函数来完成自定义的业务脚本。
web_custom_request函数之Body详解:
1. Body:
一般情况下Body中内容是作为字面值进行发送的,Body中也可以发送二进制流(用十六进制表示),方法如下,但仅限二进制流中不含空字符\x00,如果一段Body的二进制流中有空字符,则web_custom_request实际发送的内容为第一个空字符之前的数据,其后的数据(包括空)都会被截断。
例如:
1.1 Body内容不含空字符\x00
1 web_custom_request("web_custom_request",2 "URL=http://www.baidu.com",3 "Method=POST",4 "TargetFrame=",5 "Resource=0",6 "Referer=",7 "Mode=HTTP",8 "Body=\x3d\x9d\x1d\xef\xa4\x04\x41",9 LAST);
发送数据包的Body内容为:
1.2 Body内容含空字符\x00
1 web_custom_request("web_custom_request",2 "URL=http://www.baidu.com",3 "Method=POST",4 "TargetFrame=",5 "Resource=0",6 "Referer=",7 "Mode=HTTP",8 "Body=\x3d\x00\x9d\x1d\xef\xa4\x00\x04\x41",9 LAST);
发送数据包的Body内容为:
因此使用Body是无法发送含有空的二进制数据流的,此外,Body中的内容可以使用参数替代,web_custom_request函数的整个Body字符串参数("Body=abcd")也是可以使用一个变量(数组或字符指针)来替代的,注意点:1、变量中的内容不需要含有双引号,例如:char BodyVar[20] = "Body=abcd"; 2、使用变量时后面加一个逗号,例如:BodyVar, 。
2. BodyBinary:
当要发送的二进制数据流中含有空时可以使用BodyBinary来发送,注意下面的代码,发送的请求中第一个空之后的数据仍然被截断, 发送数据包的Body内容同1.2中的截图:
1 web_custom_request("web_custom_request",2 "URL=http://www.baidu.com",3 "Method=POST",4 "TargetFrame=",5 "Resource=0",6 "Referer=",7 "Mode=HTTP",8 "BodyBinary=\x3d\x00\x9d\x1d\xef\xa4\x00\x04\x41\x00\x00\x42",9 LAST);
正确的写法如下,使用两个转义字符:
1 web_custom_request("web_custom_request",2 "URL=http://www.baidu.com",3 "Method=POST",4 "TargetFrame=",5 "Resource=0",6 "Referer=",7 "Mode=HTTP",8 "BodyBinary=\\x3d\\x00\\x9d\\x1d\\xef\\xa4\\x00\\x04\\x41\\x00\\x00\\x42",9 LAST);
也可以如下这样写,只在出现空的地方用两个反斜杠:
1 web_custom_request("web_custom_request",2 "URL=http://www.baidu.com",3 "Method=POST",4 "TargetFrame=",5 "Resource=0",6 "Referer=",7 "Mode=HTTP",8 "BodyBinary=\x3d\\x00\x9d\x1d\xef\xa4\\x00\x04\x41\\x00\\x00\x42",9 LAST);
发送数据包的Body内容为:
即二进制数据流中含有空也是可以发送的,上面一栏显示的是八进制表示。BodyBinary中的内容同样可以使用参数替代,整个BodyBinary字符串参数同样可以使用变量替换。如下:
1 char *sendbufhex = "\\x3d\\x00\\x9d\\x1d\\xef\\xa4\\x00\\x04\\x41\\x00\\x00\\x42"; 2 lr_save_string(sendbufhex,"sendbufhexParam"); 3 4 web_custom_request("web_custom_request", 5 "URL=http://www.baidu.com", 6 "Method=POST", 7 "TargetFrame=", 8 "Resource=0", 9 "Referer=",10 "Mode=HTTP",11 "BodyBinary={sendbufhexParam}",12 LAST);
1 char *sendbufhexBody = "BodyBinary=\\x3d\\x00\\x9d\\x1d\\xef\\xa4\\x00\\x04\\x41\\x00\\x00\\x42"; 2 3 4 web_custom_request("web_custom_request", 5 "URL=http://www.baidu.com", 6 "Method=POST", 7 "TargetFrame=", 8 "Resource=0", 9 "Referer=",10 "Mode=HTTP",11 sendbufhexBody,12 LAST);
但是当参数或变量中的内容并不是上面这样直接赋值一个常量字符串,而是通过其他拆分拼接而来时,就只需要使用一个反斜杆,比如:我要发送的的一串二进制流的十六进制字符串为:3d009d1defa4000441000042,参数写法代码如下:
1 char sendbuf[100] ="3d009d1defa4000441000042"; 2 char sendbufhex[100]; 3 char temp[100]; 4 int sendbuflen,i; 5 6 sendbuflen = strlen(sendbuf); 7 memset(sendbufhex,'\0',sizeof(sendbufhex)); 8 9 for(i=0;i<sendbuflen;i+=2)10 {11 memset(temp,'\0',sizeof(temp));12 temp[0] = '\\';13 temp[1] = 'x';14 temp[2] = sendbuf[i];15 temp[3] = sendbuf[i+1];16 lr_output_message("Notify:temp:%s",temp);17 strcat(sendbufhex,temp);18 }19 20 lr_output_message("Notify:sendbufhex:%s",sendbufhex);21 22 lr_save_string(sendbufhex,"sendbufhexParam");23 24 lr_output_message("Notify:sendbufhexParam:%s",lr_eval_string("{sendbufhexParam}"));25 26 web_custom_request("web_custom_request",27 "URL=http://www.baidu.com",28 "Method=POST",29 "TargetFrame=",30 "Resource=0",31 "Referer=",32 "Mode=HTTP",33 "BodyBinary={sendbufhexParam}",34 LAST);
特别注意:拼接的参数中只需要一个饭斜杠而不是两个饭斜杠,”3d009d1defa4000441000042" ==》 "\x3d\x00\x9d\x1d\xef\xa4\x00\x04\x41\x00\x00\x42"
变量替换写法代码如下:
1 char sendbuf[100] ="3d009d1defa4000441000042"; 2 char sendbufhex[100]; 3 char temp[100]; 4 int sendbuflen,i; 5 sendbuflen = strlen(sendbuf); 6 lr_output_message("Notify:sendbuflen:%d",sendbuflen); 7 8 memset(sendbufhex,'\0',sizeof(sendbufhex)); 9 strcat(sendbufhex,"BodyBinary=");10 11 for(i=0;i<sendbuflen;i+=2)12 {13 memset(temp,'\0',sizeof(temp));14 temp[0] = '\\';15 temp[1] = 'x';16 temp[2] = sendbuf[i];17 temp[3] = sendbuf[i+1] ;18 lr_output_message("Notify:temp:%s",temp);19 strcat(sendbufhex,temp);20 }21 22 lr_output_message("Notify:sendbufhex:%s",sendbufhex);23 24 web_custom_request("web_custom_request",25 "URL=http://www.baidu.com",26 "Method=POST",27 "TargetFrame=",28 "Resource=0",29 "Referer=",30 "Mode=HTTP",31 sendbufhex,32 LAST);
3. RAW_BODY
待续...
可用于发送json格式的数据:
1 web_custom_request("request", //随便写个名字 2 "Method=POST", //请求的方法 3 "URL=http://www.baidu.com", //请求地址 4 "RecContentType=application/json", //指定相应头的Content-Type,这里是JSON 5 "EncType=application/json", //指定请求头的Content-Type,这里也是JSON 6 "Mode=HTML", 7 RAW_BODY_START, //请求BODY开始的标识符 8 "{\"userIds\":[1300000001,1300002029,1300003163],\"type\":0}", //BODY的内容 9 55, //上面一行BODY内容的长度10 RAW_BODY_END, //请求BODY结束的标识符11 LAST);
实际发送的数据内容为:
web_custom_request函数语法详解
语法:
Int web_custom_request (const char *RequestName, <List of Attributes>,[EXTRARES, <List of Resource Attributes>,] LAST );
返回值:
LR_PASS(0)代表成功
LR_FAIL(1)代表失败。
参数:
1、RequestName:步骤的名称,VuGen中树形视图中显示的名称。
2、List of Attribute:支持的属性有以下几种:
1)URL:
页面地址。
2)Method :
页面的提交方式,POST或GET。
3)EncType:
编码类型。此参数给出一个内容类型(Content-Type),指定其做为回放脚本时“Content-Type”请求头的值,例如“text/html”。Web_custom_request函数不处理未编码的请求体。请求体参数将会使用已经指定的编码方式。因此,如果指定了不匹配HTTP请求体的“EncType”,会引发服务端的错误。通常我们建议不要手动修改录制时的“EncType”值。
任何对于“EncType”的指定都会覆盖web_add_[auto_]header函数指定的Content-Type。当指定了“EncType=”(空值)时,不会产生“Content-Type” 请求头。当省略了“EncType”时,任何一个web_add_[auto_]header函数都会起作用。如果既没有指定EncType也没有web_add_[auto_]header函且“Method=POST”,
“application/x-www-form-urlencoded”会做为默认值来使用。其他情况下,不会产生Content-Type请求头。
4)BodyFilePath:
作为请求体传送的文件的路径。它不能与下面的属性一起使用:Body,或者其他Body属性或Raw Body属性包括BodyBinary,BodyUnicode,RAW_BODY_START或Binary=1。
5)UserAgent:
用户代理,它是一个HTTP头的名字,用来标识应用程序,通常是浏览器,它呈现的是用户和服务器的交互。
例如:头信息“User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)”识别的是Window NT下的IE浏览器6.0。其他的User-Agent的值用来描述其他的浏览器,或者非浏览器程序。通常,一个应用程序中所有的请求都使用相同的用户代理,录制者作为一个运行时参数来指定(Run-Time Setting—Browser Emulation—User Agent)。不管怎么说,即使是在一个简单的浏览器进程中,仍有可能会用到直接与服务器交互的非浏览器组件(例如ActiveX控件),通常他们有着不同于浏览器的用户代理属性。指定“UserAgent”表示这是一个非浏览器的请求。指定的字符串被HTTP头“User-Agent:” 使用,在某些情况下,它同时会影响回放脚本时的行为。例如,不使用浏览器缓存,假设指定的URL属于资源等等。(LoadRunner本身不检查指定的字符串与浏览器本身的值是否相同)
6)Binary:
“Binary=1”表示页面请求体中的每一个以file://x/##形式出现的值(在这里“##”代表2个十六进制数字),都会被替换为单字节的十六进制的值。如果“Binary=0”(默认值),所有的字符序列只是按照字面的值传递。需要注意双斜杠的用法。在C编译器中双斜杠被解释为单斜杠。如果不需要零字节,单斜杠可以在Binary不等于1的情况下使用(例如,使用\x20代替file://x20/)。如果需要零字节,那么只能使用file://x00/且设置 “Binary=1”,\x00在逻辑上会被截断。
7)ContentEncoding
指定请求体的使用指定的方式(gzip或者deflate)进行编码(例如,压缩),相应的“Content-Encoding:” HTTP头会和此请求一起发送。这个参数适用于web_custom_request和web_submit_data。
8)FtpAscii:
“1”使用ASCII模式处理FTP操作;"0" 使用二进制模式。
9)TargetFrame:
当前链接或资源所在Frame的名称。除了Frame的名字,还可以指定下面的参数:
_BLANK:打开一个空窗口。
_PARENT:把最新更改过的的Frame替换为它的上级。
_SELF:替换最新更改过的的Frame。
_TOP:替换整个页面。
10)RecContentType:
录制脚本时响应头的内容类型。例如text/html、 application/x-javascript等。当没有设置Resource属性时,用它来确定目标URL是否是可记录的资源。此属性包含主要的和次要的资源。最频繁使用的类型是 text、application、image。次要的类型根据资源不同变化很多。例如:"RecContentType=text/html":表示html文本。"RecContentType=application/msword":表示当前使用的是Msword。
11)Referer:
当前页面关联的页面。如果已经显式指定了url的地址,此项可以省略。
12)Resource:
指示URL是否属于资源。1 是;0 不是。设置了这个参数后,RecContentType参数被忽略。“Resource=1”,意味着当前操作与所在脚本的成功与否关系不大。在下载资源时如果发生错误,是当作警告而不是错误来处理的;URL是否被下载受“Run-Time Setting—Browser Emulation--Download non-HTML resources” 这个选项的影响。此操作的响应信息是不做为HTML来解析的。“Resource=0”,表明此URL是重要的,不受发送请求(RTS)的影响,在需要时也会解析它。
13)ResourceByteLimit:
web页面下载资源的极限大小。当达到设置的极限后,无法下载其他资源。仅仅对需要下载的资源有效。下载过程:如果总计下载大小小于极限值,则正常开始下载。如果当下载时达到了设置的极限值,资源大小可知(在HTTP响应头中指定了Content-Length),这中情况下,如果只需要一个缓冲区,那么下载可以正常完成。如果需要的不止一个缓冲区,或者资源大小不可知,下载就会中断同时关闭当前连接。这个特性可以用来模拟用户不等待一个页面下载完成时导航到另一个页面的情况。在HTTP模式中无法使用,在Concurrent Groups(Vuser脚本中的一个区,此区中的所有函数并发执行)区中也无法使用。仅仅适用于Sockets的回放,WinInet也是不适用的。
14)Snapshot:
快照的文件名。
15)Mode:
两种录制级别HTML、HTTP。HTML级别--在当前Web界面上录制直观的HTML动作。以一步步的web_url、web_link、web_image、web_submit_form来录制这些动作。VuGen仅仅录制返回HTML页面的请求,不处理脚本和应用程序。HTTP级别--VuGen把所有的请求录制为web_url指令,不生成web_link、web_image、web_submit_form这些函数。这种方法更为灵活,但是生成的脚本不够直观。
16)ExtraResBaseDir:
根URL,放在EXTRARES组里。它是用来解析相对URL的(译者加:类似于Windows的相对路径和绝对路径)。URL可以是绝对路径(例如http://weather.abc.com/weather/forecast.jsp?locCode=LFPO),也可以是相对路径(例如“forecast.jsp?locCode=LFPO”)。真正的URL的下载是通过绝对路径进行的,所以相对URL路径必须使用根路径URL去解析。例如,使用http://weather.abc.com/weather/做为根路径来解析“forecast.jsp?locCode=LFPO”,最后的URL是:http://weather.abc.com/weather/forecast.jsp?locCode=LFPO。如果没有指定“ExtraResBaseDir”,默认的根URL是主页面的URL。
17)Body:
请求体。不同的应用中,请求体分别通过Body、BodyBinary或者BodyUnicode参数来传递。请求体可以只使用其中一个参数,也可以使用一连串的分开的参数组成多请求体。例如:
web_custom_request(
……
"BodyUnicode=REPRICE"
"BodyBinary=\\x08\\x00\\xCC\\x02\\x00\\x00"
"Body=.\r\n"
"-dxjjtbw/(.tp?eg:ch/6--\r\n",
LAST);
在上面的代码中,使用了3个参数来划分请求体,一个是Unicode段,一个是二进制段,最后一个是常规的字符串。最终的请求体是这3个参数按照在函数中的顺序连接起来的值。还有一个很少用到的参数,Binary。它也能描述二进制请求体,但只允许函数中只有一个请求体参数。所有的请求体都是ASCII字符,以null结束。
Body--表示规则的,可打印的字符串。无法表示空字节。所有的字符都以一个反斜杠表示。注意:在旧的脚本中,可以看见不可打印的字符在请求体中以16进制方式进行编码。(例如 “\\x5c”),在这种情况下,必须使用“Binary=1”来标识。空字节使用"file://0.0.0.0/"来表示。 相反,新脚本则会把把请求体分开放在不同的参数中("Body=...", "BodyBinary=...", Body=...")。
BodyBinary--表示二进制代码。不可打印的字符在请求体中以16进制方式file://xhh/进行编码。在这里HH 表示十六进制值。空字节使用"file://0.0.0.0/"来表示。
BodyUnicode--美国英语,特指拉丁UTF-16LE(little-endian)编码。这种编码方式会在在每个字符末尾附加一个0字节,以便使字符更可读。但是在VuGen中实际的参数把所有的0字节都去掉的。但是在发送给Web服务器之前, web_custom_request函数会重新添加0字节的。对于不可打印的字符,使用单反斜杠表示,无法表示空字节。
注意:如果请求体大于100K,会使用一个变量来代替Body参数。变量是在 lrw_custom_body.h中定义的。
18)Raw Body:
请求体是作为指针传递的,此指针指向一串数据。 二进制的请求体可以使用BodyBinary 属性来发送(或者使用Body 属性来传递,前提是必设置
"Binary=1" )。无论如何,这种方法需要使用转义字符反斜杠把不可打印的字符转换为ASCII字符。为了能有一种更简便的表现原始数据的方式,Raw Body属性应运而生,可以传递指向二进制数据的指针。使用4个连续的参数集来表示指针,而且必须按照顺序排列:
RAW_BODY_START
指向数据缓冲区的指针
(int) 长度
RAW_BODY_END
例子:
char *abc= .../* a pointer to the raw data */
web_custom_request("StepName",
"URL=http://some.url ",
"Method=POST",
RAW_BODY_START,
"abc",
3,
RAW_BODY_END,
LAST);
在应用中,即使设置了数据的长度为0,指针也必须有值,不能为空。在“Binary=1”时,不能使用上面的语法传递原始数据。数据缓冲区中的数据不能使用参数化。也就时说,缓冲区中的任何参数(例如 "{MyParam}")不能被正确的替代为相应的值,只会以字面值发送。
3、EXTRARES:表明下面的参数将会是List Of Resource Attributes了。
4、LAST :结尾的标示符。
5、List of Resource Attributes
仅仅当Recording Options--Recording --HTML-based script-- Record within the current script step选项被选中时,List of Resource Attributes才会被插入到代码中。Web页面中的非HTML机制产生了资源列表,包含了Javascript, ActiveX, Java applets and Flash所请求的资源。VuGen's 的Recording 选项中,可以设置把这些资源录制在当前的操作中(默认是此设置)还是作为单独的步骤来录制。