This chapter discusses the Common Gateway Interface (CGI) and the role it plays in linking client-side actions (such as database access requests) to server-side reactions (such as database access and queries, as well as the subsequent dynamic generation of HTML for data presentation). You've already looked at using HTML user interfaces (enhanced with ActiveX and VBScript) to support database access on the client side. Now it's time to start looking at one of the most popular methods for handling information submitted from these forms to a Web server--this is the world of CGI. This chapter demystifies CGI and shows you that it really is no more than another component of a distributed applications development environment.
This chapter begins with an introduction to just what CGI is and some of the tasks it can help you accomplish. A discussion on CGI input and output processing follows, including some specifics on environment variables and their uses, as well as the methods used by the client to make a request to the server when an HTML form is submitted.
Next you will discover how the Windows CGI interface provides an easy means for developing CGI applications in a Windows development environment. Additionally, you will see how the enterprising work of some sharp Visual Basic developers has resulted in a publicly available module of VB code that greatly simplifies development of Web applications using Windows CGI and Visual Basic.
Finally, the remainder of the chapter takes a look at using the DB-Library VB API that comes with Microsoft SQL Server to create sophisticated Web database applications.
CGI is most simply described as a means by which a Web server can communicate with applications external to the server software and have those applications perform some processing on its behalf. In essence, CGI is a server-side process that serves as a go-between for the Web server and other applications programs, information resources, and databases. These information resources and databases can reside on the same physical machine as the Web server or on a machine at some other geographical location.
CGI provides a standard interface between the Web server and external applications. These applications commonly are referred to as gateway programs, CGI programs, or simply CGI scripts. This interface abstracts the details of communications between the Web server and the CGI program to the point where the only detail that application developers really need to know about the interface is how to input data into and retrieve data from their application. You therefore need no fundamental knowledge of the process of communicating directly with HTTP servers to use programs such as database query programs. You just need to understand how to handle the data that the server passes to the program and return the data generated by the program.
CGI programs extend the basic functionality of the Web server and give it the capability to service a variety of Web client requests that the server alone ordinarily would not be able to accommodate. CGI provides the mechanism for giving Web clients (your users) access to any application or resource to which you want to provide access.
http://hoohoo.ncsa.uiuc.edu
Gateway program, CGI program, and CGI script are essentially three names for the same thing; the remainder of this chapter uses the term CGI program. CGI programs contain the code that accepts data from the Web server (most often passed on from its Web clients) and does something with that data. Tasks can range from various forms of processing, accessing information resources, creating output, or all of these. CGI programs can be developed in virtually any language supported on the Web server host machine, including compiled and interpreted languages such as C, C++, Java, JavaScript, Visual Basic, FORTRAN, Perl, Awk, Expect, and Tcl, as well as some of the newer Perl extensions for client/server database access--for example, Sybperl and Oraperl.
A CGI program enables you to develop applications that greatly extend the functionality and capabilities of your Web server. In doing so, you can integrate other system resources, such as databases, document repositories, statistical applications, specialty processing, and so on with your Web server. These resources do not need to be local to the server host machine; they can be geographically distributed databases and applications that the CGI program can access. In other words, a CGI program running on a machine in Washington, D.C., can be designed to access databases on machines in Los Angeles, California, or any other location around the globe.
By providing a means to access resources external to your Web server, CGI programs extend the availability of these resources to your Web clients. What does this mean? Suppose that your organization has a database of information that it wants the public or users internal to the organization to be able to access. You now can develop CGI programs that accept user input from an HTML forms interface and use that input to perform complex searches and queries against the database. Other applications can be processing HTML order forms for your online store. This order processing also can be integrated with applications that perform inventory and warehouse management, accounting, and so on.
One of the most powerful aspects of CGI programming is that it gives you a means of generating HTML documents on-the-fly. No longer does your Website have to be a repository of static documents and data. Using CGI, you can develop applications that enable users to create dynamic, customized HTML documents and data presentations by specifying their unique content.
The typical steps in the CGI data-flow process follow, as shown in Figure 18.1:
FIGURE 18.1. The CGI data-flow process.
A Web server and a CGI program can communicate and pass data to one another in four ways:
Actually, CGI programs and a Web server can communicate through one additional method: by using a Windows .INI file. This method is specific to a specialized form of CGI known as Windows CGI. Because the Windows CGI specification is of particular interest to CGI developers in the Windows operating system environment, the latter half of this chapter (beginning with the section entitled "What is Windows CGI?") is dedicated to covering it in detail.
CGI input data is data submitted by the requesting client to the server, which the CGI program uses to perform some action, such as a database query. Depending on the way in which the client request is made (for example, the forms request METHOD=POST), this input data may be encoded before being passed by the server to the CGI program by environment variables or by standard input. When standard input is used, the length of the input string still is passed through an environment variable.
In addition to the actual data, the server also may pass information to the CGI program detailing the type of data that was passed. This is the case when the data is passed to the program by standard input. Standard input gives the CGI program a means of determining how it is supposed to handle or process the input data. This data type information also is passed through an environment variable.
A CGI program can generate a variety of types of output. This output can consist of instructions to the server to perform some task, such as calling another program or perhaps redirecting the client to obtain resources at another URL. More often, though, this output consists of HTML, ActiveX controls, and VBScript generated to present the results of some action performed for a requesting client. These results could be the results of a database query, for example.
CGI output always is returned by standard output. Even in cases when the CGI program has no data to return, it must generate a response indicating this. Two methods of return are possible: parsed-header output and non-parsed headers. With parsed-header output, CGI scripts produce output that is interpreted by the server and sent to the client. The script is not required to send an HTTP header, because this function is handled by the server when it parses the script output before passing it on to the Web client. Parsing incurs some overhead in processing by the server. Sometimes, you may want to avoid the overhead of this additional processing. To do so, CGI scripts can generate an HTTP header directly to avoid parsing by the server. These scripts must be prefixed by nph- to tell the server that output is to be passed directly to the client without parsing. Parsed header output is the only concern here, because this is the form that servers are required to support.
When the CGI program has data to return to the client, the data must be sent in a specific format:
Data Header (of a type understood by the server) . . .a blank line . . . the body of the response (i.e. optional output data to the requesting client)
As noted, the response data is optional, but if it is included, it must be prefaced by a MIME Content-type data header as seen in the following line of code:
Content-type: text/html
If the output data is not included, the data header must be a Location type (a URL), such as this:
Location: http://www.north.pole/santas_list.html
or a Status type (a message string), such as this:
Status: msg# msg_text
The server reads the CGI output and takes action depending on the header it finds. If the header is a Content-type header, the output is returned to the client. If the header is a Location type, the server ignores anything following the Location header and instead directs the client to access data or resources at the URL specified. If the header is a Status type, msg# and text enable the server to override the defaults it normally would return to the client.
Whenever you are working on a computer, running a program, or simply logging on, background variables are part of your working environment. These background variables are known as environment variables and typically are set automatically when you log on, established by a program at runtime, or even defined and set explicitly by you. Typically, environment variables persist for the life of a parent process and are passed to or inherited by child processes and applications of the parent.
Several standard CGI environment variables are defined when a server executes a CGI program. These can be used to pass data between the server and the CGI program. These variables come in two basic types. The first type of variable is the variable set during all client requests; this is a not request-specific variable. The second type of variable is a variable specific to the client request the CGI program is fulfilling; this is a client-specific or, more commonly, request-specific variable.
HTTP_REFERER=file:///C|/My Documents/SAMS/WebDev/echoenv.html
HTTP_CONNECTION=Keep-Alive
Environment variable values are used by CGI programs primarily as a means of attending to client requests for processing, database accesses, and so on. Sometimes, however, the variables are used to determine the client's type of Web browser. In other cases, the variables may be used to maintain and pass state information between the client and the CGI program between independent requests for service from the client. This need for state information occurs because HTTP is a stateless protocol. Therefore, after a client/server connection is dropped, your CGI program has no knowledge or memory of preceding connections unless you explicitly build in a mechanism for maintaining this information. You can maintain the state of user input to a form, for example, by designing CGI applications that embed these values as hidden fields in subsequent HTML documents that it generates for the client.
A list of 19 standard CGI environment variables follows (variables that are not request-specific are so indicated):
CONTENT_LENGTH
CONTENT_TYPE
GATEWAY_INTERFACE (not request-specific)
HTTP_ACCEPT
HTTP_USER_AGENT
PATH_INFO
PATH_TRANSLATED
QUERY_STRING
REMOTE_ADDR
REMOTE_HOST
REMOTE_IDENT
REMOTE_USER
REQUEST_METHOD
SCRIPT_NAME
SERVER_NAME (not request-specific)
SERVER_PORT
SERVER_PROTOCOL
SERVER_SOFTWARE (not request-specific)
The following sections briefly describe each of the CGI environment variables.
For servers that support user authentication and run protected CGI programs, the value of the AUTH_TYPE variable details the method used to validate users. The authentication method is protocol-specific. For example, this code indicates the most common value used:
AUTH_TYPE = Basic
In this case, a basic authentication scheme requires that a client provide a password and user identification in order to authenticate itself.
The CONTENT_LENGTH variable represents the length (in characters) of the data buffer sent by the client to the server during a request. For example, a test request for forms processing on the server reported this:
CONTENT_LENGTH=105
The value of the CONTENT_TYPE variable represents the MIME type of the data sent from the client to the server as the result of a METHOD=POST request from the client. This method commonly is used in HTML forms processing. If no data is sent, the value is set to NULL. For example, a test request for forms (using POST) processing on the server reported this:
CONTENT_TYPE=application/x-www-form-urlencoded
This is the default encoding for forms.
The GATEWAY_INTERFACE variable simply contains the version of the CGI specification to which the server complies. This environment variable is not request-specific. The server reports this value as the following, which indicates CGI 1.1:
GATEWAY_INTERFACE=CGI/1.1
The value of the HTTP_ACCEPT variable is a comma delimited list of all the MIME data types the requesting client will accept. MIME is covered in more detail in Appendix G, but basically seven MIME types are used by HTTP. These include application, text, multipart, message, image, audio, and video. All these types also have subtypes associated with them. For example, GIF is a subtype of image. My server reported that a Microsoft Internet Explorer Web client accepts the following image types:
HTTP_ACCEPT=image/gif, image/x-xbitmap, image/jpeg, */*
Note that the value of HTTP_ACCEPT is a list of accepted MIME types and is formatted as type/subtype, type/subtype, and so on.
The HTTP_USER_AGENT variable contains the Web browser the client used to send a request. It provides a nice way for CGI programs to format output to take advantage of extensions used by certain browsers or to accommodate shortcomings of some browsers. The server reported the following for a Microsoft Internet Explorer Web client, for example:
HTTP_USER_AGENT=Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)
In this case, the value of the environment variable indicates that the client Web browser is Microsoft Internet Explorer (MSIE) 2.0 running under Windows 95 and that it is compatible with Netscape (Mozilla) 1.22.
The value of the PATH_INFO variable represents extra path information supplied by the client. Servers can decode this information from URLs before executing the CGI script. Notice the following URL in an HTML forms interface document:
<FORM METHOD="POST" ACTION="http://jupiter.omniscient.com/cgi-bin/test-env/extrastuff">
This URL points to a CGI program called test-env and is appended with additional information (extrastuff). This resulted in the server reporting the following:
PATH_INFO=/extrastuff
CGI applications typically do not have any knowledge of the method by which this value was set. It is up to the developer to know the context in which this value may be used. This variable gives the developer another method of passing information from an HTML document to a CGI program. Suppose that an anchor with the following URL is in the document:
<A HREF="http://jupiter.omniscient.com/cgi-bin/myVBGateway.exe/param1=val1+param2=val2
This results in the following PATH_INFO variable value:
PATH_INFO=/param1=val1+param2=val2
This variable provides a method by which two additional parameters might be passed to the CGI program myVBGateway.exe, which then could obtain these values from the PATH_INFO environment variable.
The value of the PATH_TRANSLATED variable is a virtual-to-physical PATH_INFO mapping provided by the server. In other words, it represents the server's attempt to provide a full operating system absolute path to the data indicated in PATH_INFO. For the URL specified in the FORM ACTION attribute in the previous example, that is
="http://jupiter.omniscient.com/cgi-bin/test-env/extrastuff"
the server uses the server's DocumentRoot value of /usr/local/etc/httpd/htdocs to translate the full path to this:
PATH_TRANSLATED=/usr/local/etc/httpd/htdocs/extrastuff
The value of the QUERY_STRING variable is a URL-encoded string that is appended after a question-mark (?) character at the end of the URL referencing the CGI routine being executed. URL-encoding is a method of translating some data values to be transferred between the client and server to ensure that the data sent is received intact. URL-encoding and using the METHOD=GET form request method to set this variable are discussed in the sections "Getting Client Data to a CGI Program" and "Query Strings" a bit later in this chapter.
For a METHOD=GET test, the following URL-encoded data string was sent to the server by the client:
http://jupiter.omniscient.com/cgi-bin/test-env?uname=&email=&address=&bus=sw Â&title=Pres&cgidev=Perl5&dbase=Access&description=&dbyesno=YES&subscribe=YES
The server reported the value of the QUERY_STRING variable as this:
QUERY_STRING=uname=&email=&address=&bus=sw&title=Pres&cgidev=Perl5 Â&dbase=Access&description=&dbyesno=YES&subscribe=YES
The REMOTE_ADDR variable contains the value of the Internet provider (IP) address for the requesting client or agent (which actually may be a host of the requesting client). For the previous test examples, the server reported the following:
REMOTE_ADDR=168.143.1.44
The REMOTE_HOST variable contains the full DNS hostname for the requesting client or agent if the server is able to obtain this information. Otherwise, the value is NULL. For the previous test examples, the server reported the following:
REMOTE_HOST=dkittel.clark.net
The REMOTE_IDENT variable contains the name of the remote user as retrieved from the server if the HTTP server supports RFC 931 identification. The RFC 931 Authentication Server documentation states that HTTP servers supporting this feature should make an effort to identify the requesting client or agent. This is not considered a valid means of authenticating users. Enabling this feature also can result in performance degradation because extra work is imposed on the server to attempt identification. An example of this variable follows:
REMOTE_IDENT=kittel.omniscient.com
The REMOTE_USER variable contains the authenticated user's name if the server supports user authentication and the CGI application program is protected from unauthorized use. If the AUTH_TYPE variable is set to basic, for example, the REMOTE_USER variable can be set by using the user's identification as sent by the client. An example of this process follows:
REMOTE_USER=julie_wirkkala
The REQUEST_METHOD variable contains the client request method. The GET and POST methods are by far the most popular and widely used methods.
In the previous test example of a request for forms processing, METHOD=POST was specified. The server subsequently reported the following:
REQUEST_METHOD=POST
http://www.w3.org/hypertext/WWW/Protocols/HTTP/Request.html http://www.w3.org/hypertext/WWW/Protocols/HTTP/Methods.html
The SCRIPT_NAME variable contains a virtual path to the CGI program being executed by the server. For the previous text example, the server reported this:
SCRIPT_NAME=/cgi-bin/test-env
The SERVER_NAME variable contains the Web server hostname, DNS alias, or IP address. This environment variable is not request-specific. The server reports this value as the following:
SERVER_NAME=jupiter.omniscient.com
In this case, the Web server hostname, jupiter.omniscient.com, was set. In other cases, the following DNS alias might be returned:
SERVER_NAME=jupiter
Or an IP address can be returned, such as this:
SERVER_NAME=555.555.0.19
The SERVER_PORT variable contains the port to which the client request was sent. Ports are communications sockets used by TCP/IP applications. Most TCP/IP applications use a well-known port identifier. Such is the case with HTTP, which uses port number 80 as a default. It is possible, however, for the port number to be explicitly called out in the URL. A server may use a port number such as 8080 as the designated port for the HTTP server. In this case, URLs need to explicitly reference the port, as this example shows:
http://jupiter.omniscient.com:8080/cgi-bin/test-env
The server reported the following:
SERVER_PORT=8080
Using different port numbers enables multiple servers to be run from the same machine. This can be beneficial for establishing servers for CGI testing as well as separate intranets.
The SERVER_PROTOCOL variable contains information about the protocol used by the requesting client. This variable includes both the name and revision number. For the earlier test example, the server reported this:
SERVER_PROTOCOL=HTTP/"1.0
The SERVER_SOFTWARE variable contains the name and version number of the Web server that is tending to Web client requests and running the CGI. This environment variable is not client-request-specific. The server reports this value as the following:
SERVER_SOFTWARE=NCSA/1.5
In this case, the server software is the NCSA Web server, Version 1.5.
A server can pass client data to a CGI program by using several methods. Two methods are by far the most functional and widely used: GET and POST. This section contains references to query strings and URL-encoding. This material is covered in the sections "Query Strings" and "URL-Encoding" presented a bit later. Feel free to flip forward for a look at this material if you get curious during the discussion of GET and POST.
The GET method is used to pass user input to an HTML form to a CGI program for processing. Although GET is the default value for the METHOD attribute, it is considered to be the less-preferred method for forms-input handling because of limitations on the amount of data that can be passed using GET (a limitation not incurred by using the POST method). Because writing CGI programs that obtain their input from the QUERY-STRING environment variable is very straightforward, GET still is useful for simpler forms with only a few input objects. A fair number of older HTML and CGI programs still exist that use GET.
After a user clicks the Submit button on a form, the client browser URL-encodes and assembles user-input data into a query string that is appended to the Action URL specified in the <FORM> tag in the HTML document. A particular request to the server may look like this:
GET /cgi-bin/dosomething.exe?name=myname&title=developer HTTP/1.0
The server then passes the URL-encoded user-input query string to the CGI program through the QUERY_STRING environment variable.
After a user clicks the Submit button on a form, the client browser URL-encodes user input in the same manner it does for GET; however, the data is not appended to the specified Action URL. Instead, the data is sent in a data block to the server as part of the POST operation. (A data block is simply a stream of data, of arbitrary length, passed to the CGI program.) In this case, the Action URL is the URL to which the data block is POSTed. A particular request to the server might look like this:
POST /cgi-bin/dosomething.exe HTTP/1.0 Accept: text/plain Accept: text/html Accept: */* Content-type: application/x-www-form-urlencoded Content-length: 36 ford=escort&toyota=mr2&special=rolls
The server now passes the encoded user data to the CGI program by standard input. Additionally, the CONTENT_LENGTH and CONTENT_TYPE environment variables are set for use by the CGI program.
Earlier, you learned that the value of the QUERY_STRING environment variable is obtained from a URL-encoded string that is appended after a ? character at the end of the URL that references the CGI routine being executed. But how is the information added to the end of the URL in the first place? This can occur in two ways.
In the first method, the query information is added manually to an anchor's HREF URL in an HTML document. The URL in the following anchor, for example, references a program that prints a list of all employees due for a promotion this year:
<A HREF http://www.north.pole/print-promotion-list.exe>List Promotions</A>
However, the URL may be manually appended with query string information to cause the CGI program to print a list only if a certain specified name or names are on the list:
<A HREF http://www.north.pole/print-promotion-list.exe?Mark+Drew> ÂDisplay Promotions List</A>
In the second method, HTML forms using the GET method cause encoded user form input to be appended to the URL.
URL-encoding is a method by which requesting client data is changed or remapped to encoded characters before the server passes that data to a CGI program. URL-encoding causes the following things to occur:
Suppose that you have a form using the POST method. This form has three user-input fields for the user's name, e-mail address, and some free-form text. The user fills the fields as follows:
User name field (uname) is set to Drew Kittel E-mail field (email) is set to kittel@lnk.com Freeform text (description) is set to ~!@#$%^&*()-=+|\/~
When the form is submitted, the requesting client's data is URL-encoded according to the previously described specifications, and the server then passes this data to the CGI program. In this case, the URL-encoded data sent to the CGI program via standard input follows:
uname=Drew+Kittel&email=kittel@lnk.com Â&description=%7E%21@%23%24%25%5E%26*%28%29-%3D%2B%7C%5C%2F%7E
Note that the form name/value pairs are separated by & characters and that the actual field names and their respective values are separated by = characters. Finally, note how some of the characters in the free-form text are mapped to %hex-equivalent. For example, the = character in the free-form text was encoded as %3D. This differentiates it from the = character the server uses as a separator for field names and values.
Data provided by a requesting client using forms with GET and POST methods always is URL-encoded before being passed to the CGI program.
As stated in earlier sections, CGI specifies that data passed to back-end programs, such as a database program, be passed by environment variables or standard input. This specification is very specific to UNIX-type environments and does not suit the Windows development environment particularly well.
The Windows CGI specification was developed to provide a framework for how Web servers should implement CGI interfaces that support the development of CGI applications in a Windows environment. Unlike its UNIX counterparts (or any other known CGI interfaces), the Windows CGI interface does the work of decoding and parsing URL-encoded data passed to the server from the browser client. This decoded data is provided to back-end CGI programs via a formatted profile file (and, in some instances, a raw content file). The profile file is simply a version of the very familiar Windows .INI file.
The Windows CGI interface is the only known CGI interface that performs the work of decoding and parsing form data for you. Doing things in this manner greatly improves the capability to develop Web applications using common Windows development tools, such as Visual Basic and Visual Basic for Applications. This process also enables developers to focus on developing the application instead of worrying about the details of how data is passed from the client to the server and to CGI programs.
http://www.ora.com/
Evaluation versions of Netscape's servers are available for downloading at
http://www.netscape.com/
Additionally, you can get the full Windows CGI 1.3a Interface specification at the O'Reilly WebSite central site at
http://website.ora.com/wsdocs/32demo/windows-cgi.html
In the following sections, you'll examine a few fundamental concepts about the workings of Windows CGI. In particular, you'll learn about these topics:
The Windows CGI Data-Flow Process Figure 18.2 illustrates the data flow and processing performed during the request-response cycle. Windows CGI responds to and services requests from client browsers by following these steps:
FIGURE 18.2: The Windows CGI data-flow process.
Windows CGI I/O Spooling The Windows CGI specification stipulates that servers implementing the Windows CGI interface should provide for a spooled exchange of data between the Web server and CGI programs. In order to ensure efficient transfers of data between spool files and the network, the specification also recommends that the server use memory-mapping techniques while minimizing the number of network I/O requests used.
I/O spooling is performed by Windows CGI-compliant servers for a number of reasons:
Data Decoding Performed by the Server A browser can send form data to a server in one of two ways:
Windows CGI-compliant servers are required to support both of these form data types.
How the Server Launches Windows CGI Programs The Windows CGI specification states that compliant servers must launch a CGI program by using the CreateProcess() service with the following command line:
win-cgi-exe cgi-data-file
Here, win-cgi-exe is the complete path to the CGI program executable, and cgi-data-file is the complete path to the CGI data file.
The Windows CGI Profile (.INI) File As previously stated, Windows CGI-compliant Web servers pass data to CGI programs via a private profile (.INI) file. The data in this file is maintained in key-value format so that CGI programs can use standard Windows API calls for extracting key-value pairs from the file. The profile file consists of eight sections per the following template:
[CGI] [Accept] [System] [Extra Headers] [Form Literal] [Form External] [Form File] [Form Huge]
Accept: type/subtype {parameters}
Accept: type/subtype=Yes
key=pathname length
key=offset length
key=[pathname] length type xfer [filename]
In the previous section, you discovered in great detail how the Windows CGI interface works. In particular, you saw how the interface takes care of many details, such as I/O spooling and the tedious task of decoding URL-encoded form data. Your CGI program still must do a bit of work, though, before it can complete the specific tasks it was designed to perform. Consider how you would perform these tasks:
Because these are functions that should be performed by any well-conceived CGI program, wouldn't it be nice if some proven development tools existed that took care of many of these details for you and enabled you to focus on the application?
Thankfully for VB developers, such a set of proven tools does exist in the form of the Windows CGI framework for Visual Basic. This framework is a 32-bit Visual Basic module (CGI32.BAS) that can be added to your VB projects to provide the following functionality:
http://software.ora.com/techsupport/software/extras.html
Using the CGI32.BAS framework in your applications requires that you structure your applications in a specific way and keep a few additional considerations in mind:
A Sample CGI_Main() Program As previously mentioned, CGI_Main() is a program you write to perform the primary functions of your CGI program--it is where the meat of your application resides. This program is called from the Main() program in the CGI32.BAS framework. Listing 18.1 shows a simple CGI_Main() program that uses the WebDate() and Send() functions supplied by the framework and returns the date and time at the Web server.
Listing 18.1. A Simple CGI_Main() Program.
Sub CGI_Main() Send("Content-type: text\html") Send( " ") Send("<HTML><HEAD><TITLE>Web Server Date/Time</TITLE></HEAD>") Send("<BODY>") Send("The Web Server Date/Time is: " & WebDate()) Send("</BODY></HTML>") End Sub
A Sample Inter_Main() Program The Inter_Main() program is called from the Main() program in the CGI32.BAS framework if the CGI program is executed from Windows (if a user double-clicks the .exe filename). For most CGI applications, it is sufficient to have the program display a message box stating that this program is a CGI program. The following example illustrates how this is done:
Sub Inter_Main() MsgBox "This is a Windows CGI Program" End Sub
Table 18.1 provides a listing and description of global CGI variables provided by the Windows CGI Visual Basic framework. The use of many of these variables is demonstrated in Listing 18.2 and Figures 18.5 through 18.7. Data types in this table are specified as the following:
S String
T Tupl
V Variant
Table 18.1. Global CGI Variables Provided by the Windows CGI Visual
Basic Framework.
Variable | Description | Data Type |
The following variables hold server information |
||
CGI_GMTOffset | Number of seconds from Greenwich Mean Time (GMT). | V |
CGI_ServerAdmin | Server administrator's e-mail address. | S |
CGI_ServerSoftware | Name and version of Web server software (for example, WebSite/1.1f). | S |
CGI_Version | CGI version this server complies with (for example, CGI/1.2). |
S |
The following variables hold browser and user information |
||
CGI_AcceptTypes |
MIME data types accepted by the browser. |
T |
CGI_From |
E-mail address of the user. This rarely |
S |
CGI_NumAcceptTypes |
Number of MIME data types accepted by the browser. |
I |
CGI_Referer |
URL that referred to the CGI program. |
S |
CGI_RemoteAddr |
IP address for the remote host running the browser. |
S |
CGI_RemoteHost |
Hostname for the remote host running |
S |
CGI_RequestProtocol |
Name and revision of protocol used |
S |
The following variables hold information about executable, logical, and physical paths |
||
CGI_ContentFile |
Full path to a file that contains any |
S |
CGI_ContentLength |
Length (in bytes) of attached ContentFile data. |
L |
CGI_ContentType |
MIME content types of request with |
S |
CGI_ExecutablePath |
Path to the CGI program being run. |
S |
CGI_FormTuples |
name=value pairs sent by the form (if any). |
T |
CGI_HugeTuples |
Large name=value pairs. |
HT |
CGI_LogicalPath |
Logical or extra path information. |
S |
CGI_NumFormTuples |
Number of name=value pairs sent by the form (if any). |
I |
CGI_NumHugeTuples |
Number of large name=value pairs. |
I |
CGI_PhysicalPath |
Physical path--the translated version of the logical path in CGI_LogicalPath. |
S |
CGI_QueryString |
Encoded portion of the URL appended after the ? character. This contains GET data or the query string (if it exists). |
S |
CGI_RequestMethod |
Request method (for example, GET or POST). |
S |
CGI_ServerName |
Server hostname for the request (this |
S |
CGI_ServerPort |
Port number associated with the request. |
I |
The following variables hold information about security |
||
CGI_AuthPass |
Authorized user's password (only if |
S |
CGI_AuthRealm |
Authorized user's realm. |
S |
CGI_AuthType |
Authorization method. |
S |
CGI_AuthUser |
Authorized user's name. |
S |
The following variables hold other miscellaneous information |
||
CGI_DebugMode |
CGI tracing flag from the server. |
I |
CGI_ExtraHeaders |
Extra header information supplied by |
T |
CGI_NumExtraHeaders |
Number of extra headers supplied. |
I |
CGI_OutputFile |
Full path to the file in which the Web |
S |
Table 18.2 provides a listing and description of utility functions provided by the Windows CGI Visual Basic framework. The use of many of these functions is demonstrated in Listing 18.2 and Figures 18.5 through 18.7. Return value types in this table are specified as the following:
N/A | Not applicable |
T/F | True or False |
S | String |
Function | Description | Return Type |
ErrorHandler() | Global exception handler. | N/A |
FieldPresent() | Tests for the presence of a form field | T/F |
(especially useful to determine whether | ||
checkboxes on a form were checked). | ||
GetSmallField() | Retrieves the contents (for example, | S |
a value from a name=value pair) of a | ||
named form field. | ||
PlusToSpace() | Removes plus sign (+) delimiting | N/A |
characters from a string. The + signs | ||
are converted to spaces. | ||
Send() | Writes a string into the output spool | N/A |
file. This function is used heavily for | ||
formatting HTML output to the client. | ||
SendNoOp() | Sends a complete response that causes the | N/A |
browser to do nothing and to stay on its | ||
current page. | ||
Unescape() | Removes URL-escaping from a string; | S |
returns a modified string. | ||
WebDate() | Returns a Web-compliant date and time | S |
string (in GMT). |
You may remember that one of the benefits of using CGI applications is the capability to generate HTML on-the-fly. This capability to generate HTML documents and data presentations dynamically is not just a nice feature--it is an essential feature when providing access to database, customer service, and electronic commerce applications. These types of applications are interactive by nature and require the capability to formulate responses and outputs at runtime. Prepackaged HTML and static documents may be served up, but more often, responses need to be customized according to the user's input. Besides, it just isn't practical or desirable to have canned output for every permutation of database query a user's form inputs might generate.
Generating HTML on-the-fly might sound complicated, but by using Windows CGI and the CGI32.BAS framework utility functions, it's actually remarkably easy if you remember the following two points. First, a specific format is required if the CGI program is returning data to the requesting client. The format requires a data header, followed by a blank line, followed by the data to be returned. The required format follows:
Content_type: text/html start of HTML document ...body of document... end of HTML document
Note that the blank line after the data header is absolutely required. This action is something that is required time and again, and it easily can be implemented as the subprocedure (see the SendHTMLStart procedure in Listing 18.2).
Second, CGI programs that use the framework should never abort or perform an exit within the CGI program. This can cause the program to hang or exhibit undesirable results.
Generating dynamic HTML that uses the CGI32.BAS framework module and its Send() utility function is demonstrated in the next section.
Up to this point, you've been introduced to how Windows CGI interface passes form data to a Windows CGI program and is used to return data to a client. Additionally, you saw an overview of the CGI32.BAS framework and the functionality it provides. Now it's time to look at how the CGI32.BAS Visual Basic framework actually can be used by a CGI program to access the form data and then dynamically generate a response back to the client browser.
Listing 18.2 presents the Windows CGI Visual Basic program process_form_demo.exe. This program uses several CGI32.Bas utility functions and global variables to obtain information about HTML form input and CGI environment variables from the CGI request package created by the server. It then uses the CGI32.BAS Send() function to echo this information back to the client browser in the form of an HTML document. This program can be used with any HTML form and provides a nice way to test HTML forms in your applications.
In this sample Windows CGI Visual Basic program, a user is presented with the
HTML form shown in Figures 18.3 and 18.4.
FIGURE
18.3.: An online reader survey and subscription
appli-cation form used to invoke the CGI program shown in Listing 18.1.
FIGURE
18.4.: A continuation of the online reader
survey and subscription appli-cation form shown in Listing 18.1.
This form, an online reader survey and subscription application for a trade magazine, asks the user to input demographic information and then submit it to qualify for a subscription. Listing 18.2 shows the HTML code for this form.
Listing 18.2. Code Listing for dBWeb_form.htm, an Online Reader Survey and Subscription Application.
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <meta name="GENERATOR" content="Microsoft FrontPage 2.0"> <title>Web.dB Journal - Reader Survey and Subscription Application</title> </head> <body bgcolor="#FFFFFF"> <!-- Example: Magazine Reader Survey and Subscription Form --> <!-- Author: Drew Kittel --> <!-- HTML Specifications: HTML Level 2.0 Compliant (w/TABLE) --> <!-- Revision History: created 10 Mar 1996 --> <h2 align="center"> <em>Web.dB</em> - Reader Survey and Subscription Application</h2> <hr> <form action="http://dkittel.clark.net:8080/cgi-win/process_form_demo.exe" method="POST"> <!-- Create TEXT and TEXTAREA widgets for name, e-mail, mail entry --> <!-- Use TABLE to provide a bit of formatting control --> <p><strong>Please provide the following personal information</strong> <em> (Please fill all fields)</em> </p> <table border="0"> <tr> <td>Full Name: </td> <td><input type="text" size="50" name="uname"></td> <td><br> </td> </tr> <tr> <td>E-Mail: </td> <td><input type="text" size="50" name="email"></td> </tr> <tr> <td>Mailing<br> Address: </td> <td><textarea name="address" rows="5" cols="50"></textarea></td> </tr> </table> <p><strong>What is your company's main product/service</strong> (choose the most applicable) <br> <!-- Use radio buttons to force mutually-exclusive choices --> DB-Software <input type="radio" checked name="bus" value="sw"> Consulting <input type="radio" name="bus" value="consult"> Engineering <input type="radio" name="bus" value="engr"> Research <input type="radio" name="bus" value="research"> ADP Services <input type="radio" name="bus" value="adp"> </p> <p><strong>What is your title?</strong> (choose the most applicable) <br> <!-- Use radio buttons to force mutually-exclusive choices --> President-CEO <input type="radio" checked name="title" value="Pres"> Manager <input type="radio" name="title" value="Manager"> Designer <input type="radio" name="title" value="Designer"> Programmer <input type="radio" name="title" value="Programmer"> Analyst <input type="radio" name="title" value="Analyst"> </p> <hr> <!-- These scrolled lists allow multiple choices --> <!-- TABLE used to provide side-by-side formatting --> <p><strong>Please tell us about your development environment</strong> <em>(Please select all that apply)</em> </p> <table border="0" noborder> <tr> <th>Languages</th> <th>Data Bases</th> <th width="20%">Describe Your Applications</th> </tr> <tr> <td><select name="cgidev" multiple size="8"> <option selected value="Perl5">Perl 5.0 </option> <option value="Magic">MAGIC </option> <option value="Sybperl">SybPerl </option> <option value="Oraperl">OraPerl</option> <option value="C">C </option> <option value="C++">C++</option> <option value="Unix Shells">Unix Shells</option> <option value="Python">Python</option> <option value="REXX">REXX</option> <option value="FORTRAN">FORTRAN</option> </select> </td> <td><select name="dbase" multiple size="8"> <option selected value="Access">MS ACCESS</option> <option value="Sybase">Sybase</option> <option value="Oracle">Oracle</option> <option value="FoxPro">FoxPro</option> <option value="WDB">WDB</option> <option value="Web-Genera">Web-Genera</option> <option value="VAX DBMS">VAX DBMS</option> <option value="DB2">DB2</option> <option value="Informix">Informix</option> <option value="RdB">RdB</option> </select> </td> <td><textarea name="description" rows="8" cols="50"></textarea> </td> </tr> </table> <p><strong>Do you currently develop/plan to develop Web-accessible databases?</strong><br> YES<input type="radio" checked name="dbyesno" value="YES"> NO<input type="radio" name="dbyesno" value="NO"> </p> <p><strong>Do you want to receive/continue to receive a free subscription to </strong><em><strong>Web.dB</strong></em><strong>?</strong><br> <strong>YES!</strong><input type="radio" checked name="subscribe" value="YES"> NO<input type="radio" name="subscribe" value="NO"> </p> <p><input type="submit" value="Send My Subscription Now!"> <input type="reset" value="Clear Form Entries"> </p> </form> <hr> <p> </p> </body> </html>
When the survey form is submitted, this Action URL is invoked:
action="http://dkittel.clark.net:8080/cgi-win/process_form_demo.exe"method="POST"
Form data is passed to the server, and the CGI program process_form_demo.exe is launched.
Figures 18.5 through 18.7 illustrate the dynamic HTML generated by the program
and returned to the client.
FIGURES
18.5. Dynamic HTML generated by
the program and re- turned to the user. (Remainder of this form is shown in Figures
18.6 and 18.7.)
FIGURE
18.6.Figure 18.5 continued.
FIGURE
18.7. Figures 18.5 and 18.6 continued.
Listing 18.3 shows the VB code for CGI_Main(), Inter_Main(), and a few subroutines used to create the executable.
Listing 18.3. Code Listings for Modules Used to Create the process_form_demo.exe CGI Program.
Attribute VB_Name = "Process_Form_Demo" Option Explicit Sub CGI_Main() Dim i As Integer SendHTMLStart Send ("<HTML><HEAD><TITLE>Form Processing Demo</TITLE></HEAD>") Send ("<body bgcolor=#FFFFFF>") Send ("<CENTER>") Send ("<H2>Form Inputs and CGI EnvironmentVariables</H2>") Send ("</CENTER>") Send ("<HR>") SendFormTuples Send ("<HR>") SendCGIVariables Send ("<HR>") SendExtraHeaders Send ("<HR>") SendMIMETypes ` Now return to complete HTTP. Send ("</BODY></HTML>") End Sub Sub Inter_Main() MsgBox "This is a Windows CGI program" End Sub Sub SendCGIVariables() Send ("<H3>CGI Variables</H3>") Send ("<UL>") Send ("<LI><I>CGI Version: </I>" & CGI_Version) Send ("<LI><I>Request Protocol: </I>" & CGI_RequestProtocol) Send ("<LI><I>Request Method: </I>" & CGI_RequestMethod) If CGI_RequestKeepAlive Then Send ("<LI><I>Keep-Alive: </I> Yes") Else Send ("<LI><I>Keep-Alive: </I> No") End If Send ("<LI><I>Executable Path: </I>" & CGI_ExecutablePath) Send ("<LI><I>Logical Path: </I>" & CGI_LogicalPath) Send ("<LI><I>Physical Path: </I>" & CGI_PhysicalPath) Send ("<LI><I>Query String: </I>" & CGI_QueryString) Send ("<LI><I>Content Type: </I>" & CGI_ContentType) Send ("<LI><I>Content Length: </I>" & CGI_ContentLength) Send ("<LI><I>Server Software: </I>" & CGI_ServerSoftware) Send ("<LI><I>Server Name: </I>" & CGI_ServerName) Send ("<LI><I>Server Port: </I>" & CGI_ServerPort) Send ("<LI><I>Server Admin: </I>" & CGI_ServerAdmin) Send ("<LI><I>Remote Host: </I>" & CGI_RemoteHost) Send ("<LI><I>Remote Address: </I>" & CGI_RemoteAddr) Send ("<LI><I>From: </I>" & CGI_From) Send ("<LI><I>Referer: </I>" & CGI_Referer) Send ("<LI><I>User Agent: </I>" & CGI_UserAgent) Send ("<LI><I>Authentication Method: </I>" & CGI_AuthType) Send ("<LI><I>Authenticated Username: </I>" & CGI_AuthUser) Send ("<LI><I>Authentication Realm: </I>" & CGI_AuthRealm) Send ("</UL>") End Sub Sub SendExtraHeaders() Dim i As Integer If CGI_NumExtraHeaders > 0 Then Send ("<H3>Extra Headers:</H3>") Send ("<UL>") For i = 0 To CGI_NumExtraHeaders - 1 Send ("<LI><I>" & CGI_ExtraHeaders(i).key & ": </I>" & ÂCGI_ExtraHeaders(i).value) Next i Send ("</UL>") End If End Sub Sub SendFormTuples() Dim i As Integer Send ("<H3>Form fields data</H3>") If CGI_NumFormTuples > 0 Then Send ("<UL>") For i = 0 To CGI_NumFormTuples - 1 Send ("<LI><I>" & CGI_FormTuples(i).key & ": </I>" & ÂCGI_FormTuples(i).value) Next i Send ("</UL>") Else Send ("(none)") End If End Sub Sub SendHTMLStart() Send ("Content-type: text/html") Send ("") End Sub Sub SendMIMETypes() Dim i As Integer Send ("<H3>MIME Types:</H3>") If CGI_NumAcceptTypes > 0 Then Send ("<UL>") For i = 0 To CGI_NumAcceptTypes - 1 If CGI_AcceptTypes(i).value = "Yes" Then Send ("<LI>" & CGI_AcceptTypes(i).key) Else Send ("<LI>" & CGI_AcceptTypes(i).key & " (" & ÂCGI_AcceptTypes(i).value & ")") End If Next i Send ("</UL>") Else Send ("(none)") End If End Sub
Most of this program is very straightforward and can be easily understood by briefly studying the variables and functions summarized in Tables 18.1 and 18.2. You should note a few things about this example:
As you can see, dynamic HTML generation in a CGI program is very straightforward. Of course, this is a simple example that simply uses HTML tags for creating lists. But the example does demonstrate how powerful this method can be when your CGI integrates other processing, database accesses, and subroutines designed to streamline the generation of the HTML tags and data returned. It also is possible to generate very complex responses to the requesting client. Data retrieved from a database, for example, could be formatted in very elaborate HTML tables using a variety of header fields, different font sizes and styles, as well as different font colors and table cell background colors for emphasis and to create visually appealing presentations of data. Additionally, dynamic generation of responses to the client could be used to generate graphical charts using specialized ActiveX controls. VBScript also could be included to enable the user to manipulate attributes of the displayed chart, such as color, title, graph style, and so on. These techniques are explored in greater detail in Chapter 19, "Database Results Presentation Using HTML and CGI."
Now it's time to explore how a Web server can use the power of CGI programs to access databases and submit queries on behalf of a Web client. This is an exciting time to be involved in Web database development. The world is just now realizing the potential of Web-based database applications. Individuals, organizations, and agencies are rushing to exploit the technology and implement these systems. This section provides you with some of the tools required to get involved right away.
In this section, you'll briefly examine some of the common methods used to access Web databases. You'll look at the reasons for my opinion that customized CGI programs offer one of the most flexible means of providing that access. You'll also see that all useful CGI database applications must accomplish a series of basic steps. Finally, you'll be presented with an example that illustrates how Windows CGI and the Visual Basic API for Microsoft's SQL Server can be used to develop Web database applications.
Currently, a few fundamental methods exist by which Web servers can access database systems on behalf of a Web client. Most of these methods use extensions to or are some variant of CGI programs. Note that each method has its inherent strengths and weaknesses, and some have implementations specific to the database system and Web server being used. The following sections briefly explore these common methods:
HTML Embedded SQL Extensions Many database systems provide a mechanism for embedding SQL statements directly within HTML files passed to the Web client by the Web server. When a user submits a query, the HTML file is passed to a CGI program, which parses the SQL statements and passes these on to the database system. Results of the SQL query subsequently are retrieved by the CGI program, reformatted to HTML, and returned to the requesting client. This mechanism is typical of many shareware databases and gateways. Additionally, the developer often is limited in his capability to modify the CGI program performing the actual database access and formatting of results. Chapter 24, "The Development of a PC-Based WWW Database System," demonstrates the use of DBGateway--a gateway application (developed using Windows CGI and Visual Basic) that uses HTML-embedded SQL. Codeless Interfaces Essentially, codeless types of access methods consist of software toolsets (often Perl scripts) that work with developer-defined template files. These template files specify various views into the database and the way in which extracted data should be manipulated and formatted when returned to the requesting client. Programs within the software toolset use these template files to automatically generate HTML forms. These forms, when submitted by the user, are then processed by CGI programs, which query the database and format the results in accordance with the predefined templates.
Most of the toolsets available today are somewhat manual in nature. In the near future, however, visual programming tools that implement drag-and-drop technology for building forms interfaces, specifying database queries, and detailing the format of results sent to the user should become more prevalent. Custom CGI Programs Customized CGI programs are gateway programs specifically written to accept and process Web client requests for a service provided by the Web server. Typically, these requests are submitted by users in the form of input they have entered in an HTML form. CGI programs designed for database access parse this user input, formulate queries based on the input, connect to the database and submit the query, and accumulate results. They also can manipulate these results or perform some other specialized processing and dynamically create HTML documents, which subsequently are sent back to the Web client. Web Server API Programs The use of Web Server APIs is becoming increasingly more popular. Most major Web servers now have an API available to applications developers. Microsoft has ISAPI for Internet Information Server (IIS), Netscape has NSAPI for its line of servers, and O'Reilly has WSAPI for its WebSite servers.
Server API programs provide many of the same benefits of CGI because they are very flexible and enable developers to greatly extend the functionality of the server. Most server APIs do not allow developers to choose the language in which they would like to develop their applications, however. In other words, the server API is language-specific. Additionally, although many server API programs realize a significant performance advantage over their CGI counterparts, this advantage often is realized because the application shares process space with the server itself. This can be dangerous; if a bug causes an application to die, for example, the server may crash as a result.
Currently, customized CGI programs are by far the most common method for accessing Web databases. CGI programs give developers a tremendous amount of power and flexibility in extending Web server functionality. Without question, other methods, such as those described earlier, have their place and provide advantages when dealing with well-defined and structured systems. As most experienced software developers have discovered, though, there invariably comes a time when development tools don't provide the functionality, flexibility, or power to do what is necessary to get the job done. Custom CGI programs provide this functionality, flexibility, and power by enabling developers to design the application to best suit the problem at hand.
A custom CGI program allows for development of applications that access databases in a variety of ways, including these:
Additionally, custom CGI programs provide distinct advantages over other methods, including these:
Figure 18.8 illustrates the basic steps required in all useful CGI database access programs. The basic steps apply to most programs, but the sequence may vary somewhat from application to application. All useful programs perform the following tasks, however:
FIGURE 18.8. The fundamental steps that make up basic CGI database programs.
In this section, you will explore a method of developing Web database Windows CGI applications using Visual Basic. Specifically, you will examine all the fundamentals required to begin developing Web database applications that use Microsoft's SQL Server 6.5 database. In doing so, you will be introduced to the Microsoft DB-Library Visual Basic API for SQL Server.
http://www.microsoft.com/This evaluation version is a full copy of the SQL Server 6.5 product and includes the DB-Library API for developing VB applications. Note that the main setup program included with the downloadable version does not perform a full installation of all components of the product. You must manually execute the setup programs under the following directories to install ODBC, Programmer's Toolkit (PTK) and other VBSQL sample and help components, respectively. These directory paths are all relative from the temporary installation directory created by the main setup program:
/I386/ODBC/Setup.exe /PTK/I386/Setup.exe /PTK/Vbsql/Setup.exeWhen installing the PTK components, make certain that if you are running IIS on your system you first shut down the server before running the setup program. Otherwise, the PTK installation will hang occasionally.
Microsoft DB-Library is a library of functions that enables very sophisticated client/server applications to be developed for SQL Server. The VB API is a set of Visual Basic functions and routines that lets your application (in this case, your CGI program) interact directly with Microsoft SQL Server. Functionality in the VB API represents a subset of the functionality available in the C language API.
As you will see in subsequent sections of this chapter, this API enables you to develop CGI programs that act as clients to SQL Server databases. The DB-Library API provides these database client programs with the capability to make multiple connections to SQL Server databases, send very complex Transact-SQL queries to the databases, and retrieve results of those queries. The interface also provides the mechanisms necessary to use more sophisticated SQL Server capabilities, such as stored procedures, triggers, and cursors.
By using the VB API, SQL Server now becomes an integral part of your Visual Basic CGI programs and enables you to perform virtually any database operation supported by SQL Server on behalf of Web clients submitting requests from their browsers. CGI database applications can include standard database operations such as these:
CGI database applications also can perform more complex operations, such as these:
Several files are necessary for developing applications using the VB API; these
are summarized in Table 18.3. Note that many of these files are not included with
DB-Library for Visual Basic; therefore, you need to make certain that they already
exist on your system.
Table 18.3. Files Required for Developing Visual Basic DB-Library
Applications.
Filename | Description | Included with DB-Library? |
VBSQL.OCX | OLE custom control for DB-Library for Visual Basic. Contains all library functions required to develop client applications. Located in the \PTK\I386 directory under the temporary installation directory used by the main setup program when SQL Server was installed. | Yes |
VBSQL.BAS | An include file that contains all DB-Library Visual Basic function androutine declarations. Located in the \MSSQL\DBLIB\INCLUDE directory of the SQL Server installation on your system. | Yes |
MFC40.DLL | A current version of the MicrosoftFoundation Class (MFC). | Yes |
NTWDBLIB.DLL | The DB-Library for Win32 (Windows 95 and Windows NT). | No, but included with SQL Server |
MFCANS32.DLL | The MFC ANSI to Unicode Translation layer. | No |
MSVCRT40.DLL | The Microsoft Visual C Runtime Library. | No, but included with SQL Server |
To create a VB 5 project that uses the DB-Library Visual Basic API, you should follow these five basic steps:
Adding the VBSQL.BAS DB-Library Header File to the Project To add the VBSQL.BAS DB-Library OLE header file to the project, perform these steps:
Adding the VBSQL.OCX DB-Library OLE Custom Control to the Toolbox To add the VBSQL.OCX DB-Library OLE custom control to the project toolbox, follow these steps:
FIGURE 18.9.The components dialog box.
FIGURE 18.10. The Add ActiveX Control dialog box.
FIGURE 18.11. Adding the VBSQL.OCX custom control to the project toolbox.
Adding the VBSQL.OCX DB-Library OLE Custom Control to a Form in the Project To add the VBSQL.OCX DB-Library OLE custom control to a form in the project, perform the following steps. Do not add a control to more than one form.
FIGURE
18.12.The VBSQL.OCX control has been added to the project
toolbox.
FIGURE
18.13. Selecting the VBSQL.OCX
control.
FIGURE 18.14. Adding the VBSQL.OCX control to a form in the project.
Adding Error-Handling and Message-Handling Event Procedures The VBSQL.OCX custom control has two event procedures that can be used within your applications:
Use of these handlers is not required, but it is recommended that any complex database program include at least some nominal form of these error and message handlers.
To edit these error-handling and message-handling event procedures in your project, perform these steps:
Note that Visual Basic creates a VBSQL.OCA file (usually in the same directory as VBSQL.OCX). This file is used to store cached-type library information and other data specific to the custom control and to re-create it as needed.
Nearly all DB-Library applications perform a common set of operations or steps:
The following sections briefly describe each of these steps:
Providing Error Handlers As previously mentioned, the VBSQL.OCX control provides the error-handling interface for your application. When added to the primary form in an application, it installs two error-handler event procedures, assigns them the default names VBSQL1_Error and VBSQL1_Message, and supplies the appropriate parameter list as part of each procedure name. Note that only one error handler control can be placed in an application.
After error handlers are added to a form, implementing them is as easy as adding the required code to perform whatever specialized error handling is appropriate for your application. Initializing DB-Library Before calling any DB-Library functions or performing any operations with SQL Server, your application must initialize the DB-Library for use. A special function, SqlInit, is provided for this task. SqlInit takes no parameters and returns the DB-Library version number as a string. Note that the program's error handlers should be available before SqlInit is called. The following code snippet demonstrates the use of this function:
IF SqlInit() = "" THEN PRINT "DB-Lib was not properly initialized" ... other error-handling code ... END IF
Connecting and Logging On to SQL Server After the application has been initialized, it interacts with SQL Server by opening one or more SQL Server connections (using appropriate user logon information). Client applications use connections to send SQL queries and other statements to SQL Server. The connection also serves as a means for receiving the results of statements sent to SQL Server.
Opening a connection to SQL Server requires using the SqlLogin, SqlSetLUser, SqlSetLPwd, SqlSetLApp, and SqlOpen functions provided by DB-Library. SqlLogin allocates a SQL Server logon record and returns an identifier for that logon record initially filled with attributes assigned default values. The logon record is made up of a set of attributes that are initially assigned default values. SqlSetLUser and SqlSetLPwd set the username and password logon record attributes that will be used to log onto SQL Server. SqlSetLApp sets the application name that appears in the SQL Server sysprocesses system table. Finally, SqlOpen logs onto SQL Server using the SqlLogin logon record, establishes a connection, and returns a connection identifier that remains associated with the connection for as long as the connection remains open. SqlOpen can be used to open multiple connections to SQL Server, thus providing the potential to develop very complex applications. The following snippet of code demonstrates the use of these functions:
Login = SqlLogin() Result = SqlSetLUser(Login, "Guest") Result = SqlSetLPwd(Login, "c0rnd0gs") Result = SqlSetLApp(Login, "webguest_pubs") SqlConn = SqlOpen(Login, "OMNISCIENT")
As an alternative, DB-Library also provides a convenience function called SqlOpenConnection, which combines the work of several lower-level functions into a single function. This function provides a more convenient method for logging on to SQL Server and opening a connection in one step, thus reducing the amount of code needed to log on as a user and open a connection. Sending SQL Statements to SQL Server After an application successfully establishes a connection to SQL Server, it can begin the task of doing some real work. DB-Library provides two functions--SqlCmd and SqlExec-- for sending Transact-SQL statements to SQL Server for execution. SqlCmd is used to buffer commands to be sent to SQL Server. It can be called several times in succession to append commands to contents already in the buffer (just be certain that blank spaces are provided where appropriate). Additionally, several SQL statements may be buffered to be sent all at once.
After the command buffer is filled with SQL statements to send, SqlExec actually sends the buffer contents to SQL Server. The following snippet of code demonstrates how these functions can be used to obtain two columns of data from the pubs demonstration database delivered with SQL Server. Note the use of the SqlConn data structure returned from a previous call to SqlOpen:
Result = SqlCmd(SqlConn, "SELECT au_lname, city") Result = SqlCmd(SqlConn, " FROM pubs..authors") Result = SqlCmd(SqlConn, " WHERE state = `MD'") Result = SqlExec(SqlConn)
As an alternative, DB-Library also provides the functions SqlSend and SqlOk, which can be used in place of SqlExec. Processing SQL Server Results After SQL statements are sent to SQL Server for execution, it is necessary to retrieve the results of those statements (and optionally perform some application-specific processing on the data). DB-Library provides two functions--SqlResults and SqlNextRow--for retrieving the results of Transact-SQL statements.
SqlResults sets up the results of a SQL statement for processing. It is called once for each SQL statement that was previously sent in the command buffer and should return the value SUCCEED. Additionally, it is called a final time to return the NOMORERESULTS indicator (which signifies that there are no more results for the current SQL statement being processed).
After SqlResults returns SUCCEED (when results of the current SQL statement being processed become available), SqlNextRow is called in succession (until it returns NOMOREROWS) to retrieve rows of data returned. Note that, unless row buffering has been enabled, the processing of each row returned must be performed after SqlNextRow returns SUCCEED and before the next call to SqlNextRow. The following snippet of code demonstrates the use of these functions:
Result = SqlResults(Sqlconn) IF Result = SUCCEED THEN DO UNTIL SqlNextRow(Sqlconn) = NOMOREROWS Name = SqlData(Sqlconn, 1) City = SqlData(Sqlconn, 2) ... perform additional application-specific processing here ... LOOP END IF
Closing SQL Server Connections and Exiting the Application After the SQL Server client application performs its work, it must close any open connections to SQL Server, free memory resources, and exit in a clean manner. DB-Library provides the SqlExit and SqlWinExit functions to perform these tasks. SqlExit closes all SQL Server connections opened by an application and also frees system memory associated with each connection. (Note that single, specific connections can be closed by using the SqlClose function.) SqlWinExit is used to free all memory allocated to a Windows application by DB-Library. The following snippet of code illustrates how these functions are used in response to a user clicking a button on a form in an application:
SUB AppQuit_Click() SqlExit SqlWinExit END SUB
This section ties together much of the material of previous sections on Windows CGI and the SQL Server DB-Library API for Visual Basic. It presents a basic template for CGI applications that integrates these tools, provides a few rules, and finally presents a simple Windows CGI application that demonstrates how all these are tied together.
CGI_Main and the DB-Library Earlier in this chapter, you learned that the CGI32.BAS framework for Visual Basic requires a CGI_Main() procedure, and that it is within this procedure that most of the work of your CGI application is done. Therefore, it stands to reason that any database work using the DB- Library API also must be performed from within this procedure. The following code shows a generalized template of a CGI_Main() procedure that uses the DB-Library to perform some database processing on behalf of a Web client. Notice the close correspondence with the general flowchart for CGI database programs in Figure 18.8.
Sub CGI_Main() Load the form containing the VBSQL.OCX control Initialize DB-Library using SqlInit Use CGI32.BAS functions to obtain form data submitted with the request Open a Connection and login using SqlSetLUser, SqlSetLPwd, SqlSetLApp, ÂSqlOpen Formulate SQL statements using form data submitted with the request Add SQL statements to the command buffer using SqlCmd Send the command buffer using SqlExec Retrieve and process returned results using SqlResults, SqlNextRow and ÂSqlData Perform any (optional) application-specific processing of results data Â(using your own code, third-party software libraries, stats packages, etc.) Format results for presentation to the client using Send (output can include ÂHTML, ActiveX content as well as VBScript) Clean up connections and memory resources using SqlExit and SqlWinExit Unload the form containing the VBSQL.OCX control Return to CGI32.BAS Sub Main() End Sub
In addition to previously presented guidelines for developing applications using CGI32.BAS, the following general rules apply to Windows CGI applications using DB-Library:
This section presents a simple Web database query example that ties together many
of the concepts presented in this chapter. This example illustrates how user input
to an HTML form can be used by a Windows CGI Visual Basic 5.0 program to formulate
a query, send the query to a Microsoft SQL Server database, fetch results of the
query, and very simply present the results to the user (Web client). The example
uses the CGI32.BAS framework, the SQL Server DB-Library API for Visual Basic,
and the sample pubs database that comes with SQL Server. The pubs
Simple Query Form The HTML form shown in Figure 18.15 is used to accept user input
for simple queries against the SQL Server pubs database. Listing 18.4 shows
the HTML code for this form.
FIGURE
18.15. A form for the simple SQL Server
pubs database query.
Listing 18.4. HTML Code for the Pubs Database Query Form Shown in Figure 18.15.
<html> <head> <meta name="GENERATOR" content="Microsoft FrontPage 2.0"> <title>SQL Server DB-Library Example</title> <!-- pubs database query form --> <!-- Author: Drew Kittel --> </head> <body bgcolor="#FFFFFF"> <h2 align="center"><font color="#0000FF">SQL Server DB-Library CGI Example<br> </font><font color="#0000FF" size="4">Pubs Database Query </font></h2> <hr> <form action="http://dkittel.clark.net:8080/cgi-win/pubs_dblib_query.exe" Âmethod="POST"> <p><b>What type of book(s) are you interested in?</b> <em>(Check all that apply)</em> </p> <p><input type="checkbox" name="btype0" value="wwwdbdev"> <strong>WWW Database Development </strong><br> <input type="checkbox" name="btype1" value="mod_cook"> <strong>Modern Cooking</strong> <br> <input type="checkbox" name="btype2" value="trad_cook"> <strong>Traditional Cooking</strong> <br> <input type="checkbox" name="btype3" value="popular_comp"> <strong>Computing</strong> <br> <input type="checkbox" name="btype4" value="business"> <strong>General Business</strong> <br> <input type="checkbox" name="btype5" value="psychology"> <strong>Pop Psychology</strong> <br> </p> <p><b>How much can you spend per book?:</b> $<input type="text" size="6" name="uprice"> </p> <p><input type="submit" value="Start the Search"> <input type="reset" value="Clear Entries"> </p> </form> </body> </html>
This form includes checkboxes that enable the user to specify the type of book(s) in which he is interested and a text input box in which he can enter the maximum price (per book) he is willing to spend. Note the values of the name attributes associated with these user-input objects; you will see these in the Windows CGI program that processes this form and handles the database query.
After the user clicks the Start the Search button, the form inputs are submitted to the server. The METHOD and ACTION attributes assigned to this form specify the CGI program to be used by the server and the method by which user input is passed. In this case, the CGI program that will process the user input is called pubs_dblib_query.exe, and the POST method is used. This means that the CGI program will need to read decoded form data (in key=value format) from the [Form Literal] section of the profile (.INI) file. The pubs Query CGI Database Program The program used to handle the simple pubs database query is a VB 5 program that uses the DB-Library VB API and the CGI32.BAS framework, both of which were covered in detail earlier in this chapter. Because the CGI32.BAS framework does all the work of decoding form data submitted using the POST method, the only thing the CGI developer needs to know at this point is the names of the form-input objects (the checkboxes and text-input field). The values associated with these input widgets now can be obtained directly from the profile file using the GetSmallField() and FieldPresent() functions provided by the CGI32.BAS framework.
Listing 18.5 shows the code for the entire CGI program. As you look through the code and explanations that follow it, note how each of the steps specified in the flowchart in Figure 18.8 is implemented.
Listing 18.5. VB Code for the pubs_dblib_query.exe Windows CGI Program Used to Process Input to the Form Shown in Figure 18.15.
Attribute VB_Name = "PUBS_DBLIB_QUERY" Option Explicit Dim Login Dim Result Dim SqlConn Dim Title Dim Price Dim Clauses As String `Form field name variables Dim btype0 As String Dim btype1 As String Dim btype2 As String Dim btype3 As String Dim btype4 As String Dim btype5 As String Dim uprice Sub CGI_Main() `Load (but don't show) the form containing the `VBSQL.OCX control Load Form1 SendHTMLStart Send ("<HTML>") Send ("<HEAD><TITLE>SQL Server Query Results</TITLE></HEAD>") Send ("<body bgcolor=#FFFFFF>") Send ("<CENTER>") Send ("<H2><font color=#0000FF>SQL SERVER Query Results</font></H2>") Send ("</CENTER>") Send ("<HR>") `Process form inputs and format the clauses portion `of SQL query Clauses = "type in (" If FieldPresent("btype0") Then Clauses = Clauses & Chr(34) & GetSmallField("btype0") & Chr(34) & ", " End If If FieldPresent("btype1") Then Clauses = Clauses & Chr(34) & GetSmallField("btype1") & Chr(34) & ", " End If If FieldPresent("btype2") Then Clauses = Clauses & Chr(34) & GetSmallField("btype2") & Chr(34) & ", " End If If FieldPresent("btype3") Then Clauses = Clauses & Chr(34) & GetSmallField("btype3") & Chr(34) & ", " End If If FieldPresent("btype4") Then Clauses = Clauses & Chr(34) & GetSmallField("btype4") & Chr(34) & ", " End If If FieldPresent("btype5") Then Clauses = Clauses & Chr(34) & GetSmallField("btype5") & Chr(34) & ", " End If Clauses = Clauses & Chr(34) & Chr(34) & ") AND price < " & ÂGetSmallField("uprice") `Initialize DB-Library If SqlInit() = "" Then Send ("<EM><B>The program encountered an error</B></EM><BR>") Send ("<EM><B>DB-Library was not properly initialized</B></EM>") Else `Get a Login record and set login attributes. Login = SqlLogin() Result = SqlSetLUser(Login, "Guest") Result = SqlSetLPwd(Login, "c0rnd0gs") Result = SqlSetLApp(Login, "webguest_pubs") `Get a connection for communicating with SQL Server. SqlConn = SqlOpen(Login, "OMNISCIENT") `Put the command into the command buffer. Result = SqlCmd(SqlConn, "SELECT title, price") Result = SqlCmd(SqlConn, " FROM pubs..titles") Result = SqlCmd(SqlConn, " WHERE " & Clauses) `Send the command to SQL Server and start execution. Result = SqlExec(SqlConn) `Process the command. `Retrieve result rows, format lightly and send to client. Result = SqlResults(SqlConn) If Result = SUCCEED Then Send ("<PRE>") Do Until SqlNextRow(SqlConn) = NOMOREROWS Title = SqlData(SqlConn, 1) Price = SqlData(SqlConn, 2) ` ... other application-specific processing here ... Send ("<EM>" & Title & " " & Price & "</EM>") Loop Send ("</PRE>") End If `Close connection and exit program. SqlExit SqlWinExit End If `Unload the form containing VBSQL.OCX control Unload Form1 `Now return to complete HTTP Send ("</BODY></HTML>") End Sub Sub Inter_Main() MsgBox "This is a Windows CGI program" End Sub Sub SendHTMLStart() Send ("Content-type: text/html") Send ("") End Sub
The output generated by the program in response to the query shown in Figure 18.15
is shown in Figure 18.16.
FIGURE
18.16. Results of the simple query shown
in Figure 18.15.
The program begins with a few variable declarations followed by a call to the SendHTMLStart procedure. This procedure simply sends an HTML Content-type header to the server for inclusion in the response package to be sent to the client. Following this, the standard <HTML>, <BODY>, <TITLE>, and <HEAD> tags used to begin an HTML document are sent using the CGI32.BAS Send() function. Note that the page also is made a bit more aesthetically pleasing by setting the background color and the color of the font used for heading text.
When the form in Figure 18.15 is submitted (using the POST method, in this case), the Web server decodes the form data and creates the request package, as described earlier in this chapter. At this point, the data is readily available to the program by using the CGI32.BAS functions FieldPresent() (to first check whether data for each specific checkbox in the field was sent) and then GetSmallField() (which retrieves the value associated with the key--the name of the form field). For example, this code first checks to see whether data for the WWW Database Development checkbox in Figure 18.15 exists (that is, whether the user enabled the checkbox):
If FieldPresent("btype0") Then Clauses = Clauses & Chr(34) & GetSmallField("btype0") & Chr(34) & ", " End If
If this data does exist, the Clauses string is appended with the current content of the string, a quotation mark (") (using the Chr(34) function, the value of the field as retrieved by the GetSmallField function), another quotation mark ("), and, finally, a comma (,) character. This sequence is repeated for each checkbox in the form to determine whether it was selected. The value of the price input field also is determined in this manner and is used to append the Clauses string with a < price condition. For the user input shown in Figure 18.15, the result of these operations is a Clauses string of
type in ("wwwdbdev", "popular_comp", "business", "") AND price < 59.95
Now that the user's form input has been processed, the program sets about the work of initializing the DB-Library, getting a connection, and logging on. Note that a Guest account has been established on the SQL Server. This account has limited privileges (essentially, read-only) to the pubs database that is to be queried. After a connection is successfully established, three calls to the SqlCmd function formulate a SQL statement to send to the pubs database. Note that, although the bulk of the query could be hard-coded, you still can support ad-hoc user queries by appending the Clauses string that was formulated by using form data from the user. For example, the SQL statement created and sent to the server as a result of input in Figure 18.15 follows:
SELECT title, price FROM pubs..titles WHERE type in ("wwwdbdev", "popular_comp", "business", "") AND price < 59.95
After the query is sent to the server and executed, the SqlResults, SqlNextRow, and SqlData DB-Library functions are used within a loop to retrieve each row returned from the server. Within this loop, a minimal amount of formatting is performed by using the HTML <PRE> tag. This enables you to simply send each record of data returned from the query back to the requesting client in the form of preformatted text. Note that, with a little more work and imagination, the output generated dynamically at this stage of the program could include tables complete with headers, various background colors, and fonts. You could even develop your own set of VB functions that use the Send() function and <TABLE> tags to support creation and population of various types of HTML tables to be returned to the client.
After all data from the server is processed, formatted, and sent to the server for inclusion in the response package, the SqlExit and SqlWinExit calls are made to clean up before finally unloading the form that contains the VBSQL.OCX control. The program then sends the closing </BODY> and </HTML> tags before returning to the Sub Main program in the CGI32.BAS framework.
http://www.mcp.com/37064500504674/que/et/se_cgi/Cgi21fi.htm
This chapter presented an in-depth overview of CGI and what it enables Web application developers to do. It emphasized that the primary strength of CGI is that you can use it to expand the functionality of the Web server and then make that functionality accessible to Web clients. You saw that CGI programs invoked via HTML forms give users access to databases, resources, and applications that previously were not accessible.
You then took a brief look at the CGI data-flow process and how data moves from a Web client to the server and then to the CGI program. You learned how results from a CGI program follow this path back to the requesting client. You then delved into CGI input and output, examining the various methods to move client data to the server and on to the CGI program. CGI environment variables were introduced, the differences between GET and POST methods were outlined, and URL encoding was explained in depth.
The remainder of this chapter focused on using CGI in a Windows development environment. You explored using the Windows CGI and took a long look at how CGI32.BAS simplifies the task of developing Windows CGI Visual Basic. Next, you discovered how you can use Windows CGI with the Microsoft SQL Server DB-Library VB API to create Web database applications to perform a variety of complex operations on behalf of Web clients. Finally, the chapter pulled these concepts together by presenting an example showing how to put these concepts into practice.