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:
- The “is it done yet” loop Occasionally you will find that you must write some code...
- Querying a MySQL database with LoadRunner Let’s imagine that you want to execute arbitary SELECT, INSERT,...
- Changing LoadRunner/VuGen log options at runtime LoadRunner has a whole bunch of logging options. These can...
- Harvesting file names with VuGen VuGen isn’t just a tool for load testing and application...
- What’s New in LoadRunner 9.50? LoadRunner 9.5 was released today and, as mentioned by the...
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.
April 14th, 2009 at 3:56 am
Awesome Work !! Thanks Stuart
May 7th, 2009 at 1:23 pm
Scott Moore has an alternative solution here: http://www.loadtester.com/vugen-dynamic-data-websubmitdata
June 1st, 2009 at 7:19 pm
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.]
October 21st, 2009 at 9:04 pm
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.
October 28th, 2009 at 12:10 pm
Thank you for your improvement to my code.
Cheers,
Stuart.
November 26th, 2009 at 12:12 am
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…” ?
November 26th, 2009 at 10:30 am
Nice pickup Stan! I have corrected the article.
Cheers,
Stu.
April 6th, 2010 at 4:28 am
What is the quantity value change.
right now all item ID has quantity 1.
what if that value is dynamically change
June 9th, 2010 at 1:08 am
Hi ,
I need help to make URL dynamic which is get to next page.
Details scenario,
1. Have 3 pages.
1st page to 2nd page request occur.
In 2nd page after click on ok button, 1 parameter generated by application, which could not be in any HTML page.
After that that request get by GET method in third page.
So I have to capture that dynamic value generated by application, how can I do that?