In my previous article ("Getting Started with CGIDEV2 – Part 1"), I discussed how to write a program using CGIDEV2...
By submitting your personal information, you agree that TechTarget and its partners may contact you regarding relevant content, products and special offers.
to produce a simple report for the Web. Now it's time to look at how you can get information from a Web page in a browser into your RPG program.
The CGI APIs used to retrieve and process data from the Web are cumbersome, to say the least, so this is one of the areas where CGIDEV2 proves invaluable.
Since it is rare that you will fit a complete report on one Web page, let's look at another version of the Product Report used in the previous article, which allows the user to "page" through the report.
Figure 1 shows an example of a page of the report. The list is by Product Category, and the user can click on the "Next Page" link to view the products in the next category.
Figure 1: Output from a simple "print" style CGI program with Next Page option
So how does a browser send information to a program? There are two methods that can be used – GET and POST. We will look at the difference between the two in a moment but the end result of both is that the browser sends a parameter string along with the program call. You have often seen these in URLs when accessing the internet. A URL with a parameter string has the following format:
The parameter string starts with a '?' followed by the name of a parameter field, followed by the value of the parameter. Subsequent parameters are delimited by a '&'.
The paging logic is that when the user clicks the next page link, the program is called along with a parameter identifying the category to be listed.
Figure 2 shows the HTML for the report page. In a change from the example in the previous article, this html source is using delimiters of ' ' to indicate a section and ' ' to indicate a variable; this makes the html document a little easier to manage with a design tool like WebSphere Developer's Studio Client (WDSC).
<!-- $top$ --> Content-type: text/html <html> <head> <title>List Products by Category</title> <!-- comment --> </head> <body> <center> <h1>RPG Meets the Web!</h1> <!-- $tablestart$ --> <b> Product list as at <!-- %today% --> - Days Since Last Order</b><br><br> <b>Category: <!-- %CatCod% --> <!-- %CatDes% --><br><br></b> <table border=1 CellSpacing=2 CellPadding=2 Width="80%"> <b><th>Product<br>Code</th> <th>Description</th> <th></th> <th>Landed<br>Cost</th> <th>Selling<br>Price</th> <th>Date Last<br>Ordered</th> <th>Days Since<br>Last Ord</th></b> <!-- $tableline$ --> <tr align=right> <td align=center><!-- %prodcd% --></td> <td align=left><!-- %prodds% --></td> <td><img border=0 src="/cgiseminar/Images/P<!-- %prodcd% -->.JPG"></td> <td><!-- %LndCst% --></td> <td><!-- %SellPr% --></td> <td align=center><!-- %DtLOrd% --></td> <td><!-- %dayssince% --></td> </tr> <!-- $tableend$ --> </table></center> <!-- $nextlink$ --> <a href="/cgi-bin/cvallrep2a?CGICatCod=<!-- %CatCod% -->"> Next Page</a><br><br> <!-- $end$ --> <a href="/cgiseminar/CGIExamples.html">Return to Index</a> </body> </html>
Figure 2: HTML for report page with Next Page link.
The important section is nextlink at the very end of the source. It identifies the Next Page text with a hyperlink to recall the RPG program and pass a parameter which identifies the next category code to be displayed. For example, when displaying the list of products for category '01' and where '05' is the next category, the Next Page link would have a hyperlink of
The CGI program
Figure 3 shows the source of the CGIDEV2 program CVALLREP2A. What you are really interested in here is how the program receives the parameters passed from the browser because they are not passed using a good old parameter list. The program makes use of five of the CGIDEV2 subprocedures: gethtmlIFS, wrtsection, updHTMLvar, ZhbGetInput and ZhbGetVar.
H Option(*SrcStmt: *NoDebugIO) DftActGrp(*No) BndDir('RPGBROWSE') //---------------------------------------------------------- // // CGI Application // // Paul Tuohy, ComCon // //---------------------------------------------------------- // // CVALLREP2:- List Products by Category Using CGIDEV2 // //---------------------------------------------------------- FCategor1 IF E K DIsk ExtFile('CGISEMINAR/CATEGOR1') FProduct2 IF E K Disk ExtFile('CGISEMINAR/PRODUCT2') /copy prototypeb /copy usec D LastSold S D D DaysSince S 5I 0 D SavedQryStr S 32767 Varying //=================================================================== // Main line //=================================================================== /FREE gethtmlIFS('/cgiseminar/cvallrep2a.html':'<!-- $':'$ -->': '<!-- %':'% -->'); wrtsection('top'); // Check for Input If ZhbGetInput(SavedQryStr: QUSEC) > 0; CatCod = ZhbGetVar('CGICatCod'); SetLL CatCod Product2; Else; Setll *Loval Product2; EndIf; Read Product2; Chain CatCod Categor1; updHTMLvar('today':%char(%Date())); updHTMLvar('catcod':catcod); updHTMLvar('catdes':catdes); wrtsection('tablestart'); DOW Not %EOF(Product2); // Form Table line and output HTML updHTMLvar('prodcd':prodcd); updHTMLvar('prodds':prodds); updHTMLvar('lndcst':%Char(LndCst)); updHTMLvar('sellpr':%Char(SellPr)); If DTLORD <> 0; LastSold = %Date(DtlOrd:*YMD); DaysSince = %Diff(%Date():LastSold:*Days); updHTMLvar('dtlord':%Char(lastsold)); updHTMLvar('dayssince':%Char(dayssince)); Else; updHTMLvar('dtlord':'No sales to date'); updHTMLvar('dayssince':'No sales to date'); EndIf; wrtsection('tableline'); ReadE CatCod Product2; EndDo; wrtsection('tableend'); // Determine if "Next" link needs to be displayed SetGT CatCod Product2; Read Product2; If Not %EOF(Product2); updHTMLvar('catcod':catcod); wrtsection('nextlink'); EndIf; wrtsection('end *fini'); return; /END-FREE
Figure 3: A simple CGIDEV2 program with Next Page logic.
Important items to note:
- The binding directory is specified on the H spec.
- The EXTFILE keyword is used to qualify the file names that will be used at run time.
- Copy directives are used to include the PROTOTYPEB and USEC members containing the CGIDEV2 prototypes and standard API error data structure.
- The varying length field SavedQryStr is defined. It will contain the input parameter string (but you will not need to access it).
- The program starts by calling the gethtmlIFS subprocedure to load the CVALLREP2A.HTML document. The additional parameters identify the start and end delimiters being used for sections and variables in the html document.
- The ZhbGetInput subprocedure is used to determine if there are any parameters being "passed" on the program call. The subprocedure returns the number of unique parameters passed.
- If parameters are passed from the browser, the ZhbGetVar subprocedure is used to retrieve the value passed for CGICatCod in the parameter list. (Refer to the Next page hyperlink in the html document.)
- Using either the category code passed as a parameter or the start of the file, the program reads the next record from the Product File and uses that category code from that record to produce the list to be displayed. The updHTMLvar and wrtSection subprocedures are used to generate the list.
- At the end of the loop the program determines if there are more records on the product file and, if so, writes the nextlink section with next category code in place.
- As always, the programs ends with a write of the special section *fini. Remember, this is the instruction that actually causes the Web page to be sent to the browser.
Running the program
With the server running, open your browser and enter this URL:
where iseriesservername is the name of your iSeries. All being well, the results of your efforts will be displayed in the browser. Clicking on the Next Page link should bring you to the next category, until you reach end of file.
Using a form
The simple way of passing parameters to a program is to pass them on the hyperlink. But what if you want to have the user enter a value? Figure 4 shows an example of a form being used to request a starting category. The user enters a category code and clicks on the Submit Query button to call the CVALLREP2A program with the entered category.
Figure 4: Using a form to request a value.
The form is stored as a static HTML document. (You do not need to use a CGI program to display it.) Figure 5 shows the HTML for the form.
<html> <head> <Title>Product Report by Category</Title> </head> <body> <Center> <h2>Product Report by Category Request</h2><br> <form name="getCategory" action="/cgi-bin/cvallrep2a" method="POST"> Enter required Category Code here: <input type="text" name="cgicatcod" size=2 maxlength=2 value=""><br><br><br> <input type="submit"> </form> </Center> </body> </html>
Figure 5: HTML for a form.
The definition of the form is delimited by a <FORM> and a </FORM> tag. The <FORM> tag contains the name of the form (getCategory), the action to be taken when a submit button is pressed (identify the program to be called) and the method to be used (GET or POST).
A GET method means that the parameter string will be passed exactly as it is on the Next Page hyperlink, and the parameter string will appear on the URL when the program is called. A POST method means that the parameter string does not appear on the URL when the program is called.
If you were using the standard CGI APIs you would have to use different APIs to retrieve the parameter string after first determining whether GET or POST was used when the program was called. One of the features of the ZhbGetInput subprocedure is that it does all that for you and you don't really care whether GET or POST was used. Having said that, POST means you get an easier to read URL.
Within the form you can have standard text along with the definition of input fields. Input fields are identified by an <INPUT> tag. The <INPUT> tag contains the type of entry (text and submit in this example -- more about those in a moment), the name of the parameter field (CGICatCod to correspond to what the program is expecting), the Maximum Length of the field and the value to be displayed.
A type of text means the input field is simply text. HTML does not cater for numbers, so even if you are entering a number, you define the type as text. A type of submit means it is a submit button and clicking it causes the form to be submitted (i.e. the program identified in the action for the form is called). You can change the text displayed in the button by giving it a value. In the next article we will look at the various types available (there are lots of them) and how to start customizing the use of submit buttons.
And what changes need to be made to the CVALLREP2A program? That's easy -- NONE!
Changing the Next Page link
One of the downsides of hyperlinks is that they are implicitly GET methods; so why not change the Next Page link to use a button with a POST method instead of a hyperlink? In the CVALLREP2A HTML document simply change the definition of the nextlink section to that shown in Figure 6. The type of hidden is the same as a hidden field in a display file. Also note the value for the submit button.
<!-- $nextlink$ --> <form name="getCategory" action="/cgi-bin/cvallrep2a" method="POST"> <input type="hidden" name="cgicatcod" value="<!-- %CatCod% -->"><br><br><br> <input type="submit" value="Next Page"> </form>
Figure 6: Using a form to define a button as opposed to a hyperlink.
One step further
Now you know how to send data from a program to a browser and how to get data from a browser into a program. In the next article you will see how easy it is to use those capabilities to write a maintenance program.
About the author: Paul Tuohy is CEO of ComCon, an iSeries consulting company. He is the author of Re-Engineering RPG Legacy Applications and is one of the quoted industry experts in the IBM Redbook "Who Knew You Could Do That With RPG IV?"