How to handle HTTP POSTs with a changing number of name-value pairs

Occasionally you will find that you need to create a VuGen script for a web application which changes the number of name-value pairs which are sent with a POST request. This tech tip shows you how to handle this situation by dynamically constructing a POST body.

In the example below, you can see that the web_submit_data function for the updateItemsFromSearch request (which adds items to a shopping cart) has six item IDs and six item quantities. The web_subit_data function presents the name-value pairs of the POST request in an easy to read format, but doesn’t give too many clues as to how you might handle the case of submitting a varying numbers of item IDs, without writing an if statement and having separate web_submit_data functions for every possible number of items.

Fortunately, there is an easy way to do this. Read on…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
web_submit_data("updateItemsFromSearch.do", 
	"Action=http://www.example.com.au/catalog/updateItemsFromSearch.do", 
	"Method=POST", 
	"TargetFrame=", 
	"RecContentType=text/html", 
	"Referer=http://www.example.com.au/catalog/search.do?key=0/46EF7F373045033002000000AC193D36", 
	"Snapshot=t10.inf", 
	"Mode=HTML", 
	ITEMDATA, 
	"Name=sortOption", "Value=PRICE_ASCENDING", ENDITEM, 
	"Name=pageselect", "Value=10", ENDITEM, 
	"Name=page", "Value=", ENDITEM, 
	"Name=itemPageSize", "Value=10", ENDITEM, 
	"Name=next", "Value=addToBasket", ENDITEM, 
	"Name=itemkey", "Value=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51", ENDITEM, 
	"Name=order", "Value=", ENDITEM, 
	"Name=itemquantity", "Value=1", ENDITEM, 
	"Name=isExtendedResult", "Value=null", ENDITEM, 
	"Name=display_scenario", "Value=products", ENDITEM, 
	"Name=contractkey", "Value=", ENDITEM, 
	"Name=contractitemkey", "Value=", ENDITEM, 
	"Name=item[0].itemID", "Value=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51", ENDITEM, 
	"Name=item[0].quantity", "Value=1", ENDITEM, 
	"Name=item[1].itemID", "Value=46EF7F373045033002000000AC193D3640EBE620771F00A500000000AC193D52", ENDITEM, 
	"Name=item[1].quantity", "Value=1", ENDITEM, 
	"Name=item[2].itemID", "Value=46EF7F373045033002000000AC193D363F4191CB824400F3E1000000AC193D38", ENDITEM, 
	"Name=item[2].quantity", "Value=1", ENDITEM, 
	"Name=item[3].itemID", "Value=46EF7F373045033002000000AC193D36464DB497A33B005602000000AC193D51", ENDITEM, 
	"Name=item[3].quantity", "Value=1", ENDITEM, 
	"Name=item[4].itemID", "Value=46EF7F373045033002000000AC193D36484A7C7E995A8034E1008000AC193D51", ENDITEM, 
	"Name=item[4].quantity", "Value=1", ENDITEM, 
	"Name=item[5].itemID", "Value=46EF7F373045033002000000AC193D36484E990F2564C27EE1008000AC193D35", ENDITEM, 
	"Name=item[5].quantity", "Value=1", ENDITEM, 
	LAST);

First, let’s take a look at what VuGen actually sends to the web server…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /catalog/updateItemsFromSearch.do HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Cache-Control: no-cache\r\n
Referer: http://www.example.com.au/catalog/search.do?key=0/46EF7F373045033002000000AC193D36\r\n
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: en-us\r\n
Accept: */*\r\n
Connection: Keep-Alive\r\n
Host: www.example.com.au\r\n
Cookie: JSESSIONID=(J2EE5934700)ID1010337953DB11208117568232223992End; saplb_*=(J2EE5934700)5934753\r\n
Content-Length: 896\r\n
\r\n
sortOption=PRICE_ASCENDING&pageselect=10&page=&itemPageSize=10&next=addToBasket&itemkey=46
EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&order=&itemquantity=1&isExt
endedResult=null&display_scenario=products&contractkey=&contractitemkey=&item%5B0%5D.itemI
D=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&item%5B0%5D.quantity=1&
item%5B1%5D.itemID=46EF7F373045033002000000AC193D3640EBE620771F00A500000000AC193D52&item%5
B1%5D.quantity=1&item%5B2%5D.itemID=46EF7F373045033002000000AC193D363F4191CB824400F3E10000
00AC193D38&item%5B2%5D.quantity=1&item%5B3%5D.itemID=46EF7F373045033002000000AC193D36464DB
497A33B005602000000AC193D51&item%5B3%5D.quantity=1&item%5B4%5D.itemID=46EF7F37304503300200
0000AC193D36484A7C7E995A8034E1008000AC193D51&item%5B4%5D.quantity=1&item%5B5%5D.itemID=46
EF7F373045033002000000AC193D36484E990F2564C27EE1008000AC193D35&item%5B5%5D.quantity=1

As you can see, the name-value pairs that were so easy to read in the web_submit_data function are really just sent as a big blob of text, with each name-value pair separated by an ampersand (&) and some characters URL-encoded (e.g. “[” becomes “%5B”).

We can make our VuGen script look more like the actual HTTP request by regenerating the script with web_custom_request selected. Select Tools > Regenerate Script, then select the recording option of URL-based script. Under URL Advanced, select Use web_custom_request only.

You script will now look something like this…

1
2
3
4
5
6
7
8
9
10
11
web_custom_request("updateItemsFromSearch.do", 
	"URL=http://www.example.com.au/catalog/updateItemsFromSearch.do", 
	"Method=POST", 
	"Resource=0", 
	"RecContentType=text/html", 
	"Referer=http://www.example.com.au/catalog/search.do?key=0/46EF7F373045033002000000AC193D36", 
	"Snapshot=t207.inf", 
	"Mode=HTTP", 
	"Body=sortOption=PRICE_ASCENDING&pageselect=10&page=&itemPageSize=10&next=addToBasket&itemkey=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&order=&itemquantity=1&isExtendedResult=null&display_scenario=products&contractkey=&contractitemkey=&item%5B0%5D.itemID=46EF7F373045033002000000AC193D364785F195AFD7401600000000AC193D51&item%5B0%5D.quantity=1&item%5B1%5D.itemID=46EF7F373045033002000000AC193D3640EBE620771F00A500000000AC193D52&item%5B1%5D.quantity=1&item%5B2%5D.itemID="
	"46EF7F373045033002000000AC193D363F4191CB824400F3E1000000AC193D38&item%5B2%5D.quantity=1&item%5B3%5D.itemID=46EF7F373045033002000000AC193D36464DB497A33B005602000000AC193D51&item%5B3%5D.quantity=1&item%5B4%5D.itemID=46EF7F373045033002000000AC193D36484A7C7E995A8034E1008000AC193D51&item%5B4%5D.quantity=1&item%5B5%5D.itemID=46EF7F373045033002000000AC193D36484E990F2564C27EE1008000AC193D35&item%5B5%5D.quantity=1", 
	LAST);

The next step is to dynamically construct the string that will be sent as the body argument of the web_custom_request function. Do this by

  • Correlate your values using ORD=All (line 4) from the response to your search request (line 15).
  • Write a for loop to build a parameter containing your POST body (line 31). Save time by using lr_paramarr functions to extract the values from the parameter array you created with web_reg_save_param(”Ord=All”, …).
  • Use web_custom_request to send your POST body.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
lr_start_transaction("search for product");
 
// Save all item IDs returned by product search
web_reg_save_param("ItemID_array",
	"LB=<a href=\"javascript:seeSingleItem('",
	"RB='",
	"Ord=All",
	"Search=Body",
	"RelFrameId=1",
	"IgnoreRedirections=Yes",
	LAST);
 
web_reg_find("Text=Search Results for {SearchTerm}", LAST);
 
web_url("Search", 
	"URL=http://www.example.com.au/catalog/search.do?key={SearchTerm}", 
	"TargetFrame=center2", 
	"Resource=0", 
	"RecContentType=text/html", 
	"Referer=http://www.example.com.au/", 
	"Snapshot=t8.inf", 
	"Mode=HTML", 
	LAST);
 
lr_end_transaction("search for product",LR_AUTO);
	lr_think_time(5);
lr_start_transaction("add to basket");
 
// Construct variable length POST body.
lr_save_string(lr_eval_string("sortOption=PRICE_ASCENDING&pageselect=10&page=&itemPageSize=10&next=addToBasket&itemkey={ItemIDarray_1}&order=&itemquantity=1&isExtendedResult=null&display_scenario=products&contractkey=&contractitemkey="), "Body");
for (i=0; i<lr_paramarr_len("ItemIDarray"); i++) {
	lr_save_string(lr_paramarr_idx("ItemIDarray", i+1), "ItemID");
	lr_save_int(i, "ItemIndex");
	lr_save_string(lr_eval_string("{Body}&item%5B{ItemIndex}%5D.itemID={ItemID}&item%5B{ItemIndex}%5D.quantity=1"), "Body");
}
 
web_reg_find("Text=Mini Shopping Basket", LAST);
 
web_custom_request("updateItemsFromSearch.do", 
	"URL=http://www.example.com.au/catalog/updateItemsFromSearch.do", 
	"Method=POST", 
	"Resource=0", 
	"RecContentType=text/html", 
	"Referer=http://www.example.com.au/catalog/search.do?key={SearchTerm}", 
	"Snapshot=t207.inf", 
	"Mode=HTTP", 
	"Body={Body}",  // use dynamically constructed POST body.
	LAST);
 
lr_end_transaction("add to basket",LR_AUTO);

This is a useful technique to add to your repertoire.

 


Related posts:

  1. The “is it done yet” loop Occasionally you will find that you must write some code...
  2. Querying a MySQL database with LoadRunner Let’s imagine that you want to execute arbitary SELECT, INSERT,...
  3. Changing LoadRunner/VuGen log options at runtime LoadRunner has a whole bunch of logging options. These can...
  4. VuGen String Comparison Behaviour Anyone who works with VuGen should know that they should...
  5. LoadRunner in Windows 7 Windows 7 has finally been released, and I’ve had the...


Bookmark using any bookmark manager!

 

You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

7 Responses to “How to handle HTTP POSTs with a changing number of name-value pairs”

  1. Awesome Work !! Thanks Stuart

  2. Scott Moore has an alternative solution here: http://www.loadtester.com/vugen-dynamic-data-websubmitdata

  3. Hi,
    Thanks this is a great topic. I have been trying to find a way to get this done without having to change the arguments in the web_submit_data functions whenever the project team asks to change the number of name-value pairs that they want me to run the test with.

    Btw, I would like to ask if the lr_paramarr_idx and lr_paramarr_len is a function found in higher LR versions. I have tried looking for it in LR8.1 function library but these functions do not exist. Is there a work-around for this without having to use C string functions? Thanks!

    [Stuart's Reply: Yes, the lr_paramarr functions are fairly recent. They are definitely not in LoadRunner 8.1.4.]

  4. Scott’s solution is quick n dirty work around and does not scale beyond a predefined limit. I’ve been using web_custom_request for generating dynamic name-value pairs and is a very elegant solution.

    The code above would be complete if you add “convert=HTML_TO_URL” argument to the web_reg_save_param call that builds ItemID_array, so that the reserved characters like &, ?, @ etc. within the captured values (if any) will be converted to percent encoding.

  5. Thank you for your improvement to my code.

    Cheers,
    Stuart.

  6. Hi,
    Thanks for the very useful tip.

    One note: “Save time by using lr_paramarr functions to extract the values from the parameter array you created with web_reg_find.” likely should end with “… you created with web_reg_save_param(”ItemID_array…” ?

  7. Nice pickup Stan! I have corrected the article.

    Cheers,
    Stu.

Leave a Reply