February 28, 2021
Hot Topics:

Programming Microsoft .NET : Web Forms

  • By Jeff Prosise
  • Send Email »
  • More Articles »

HTML Forms

Simple.html is a far cry from a full-blown Web application. It's a static HTML file that solicits no user input. Real Web applications like the ones located at www.amazon.com and www.ebay.com accept input from their users, and they vary the HTML that they return to Web browsers based on the contents of that input.

At the heart of almost every genuine Web application is an HTML form. An HTML form is the portion of an HTML document that appears between <form> and </form> tags. The HTML in Figure 5-1 describes a simple form representing a Web-based adding machine. The form contains two text input fields for the user to type numbers into and an equals button that submits the form back to the server. Figure 5-2 shows how the form appears in Internet Explorer. As you can see, the browser renders an <input type="text"> tag as a text input field and an <input type="submit"> tag as a push button. Similar tags can be used to create check boxes, radio buttons, list boxes, and other basic control types.


<html>  <body>    <form>      <input type="text" name="op1" />      +      <input type="text" name="op2" />      <input type="submit" value="  =  " />    </form>  </body></html>

Figure 5-1. A simple HTML form.

Figure 5-2. Calc.html displayed in Internet Explorer.

A submit button (<input type="submit">) plays a special role in an HTML form. When clicked, it submits the form to a Web server. To be more precise, the browser submits the form along with any input in the form's controls. How the form is submitted depends on whether the <form> tag includes a Method attribute and the value of that attribute, if present. If the <form> tag lacks a Method attribute or includes a method="get" attribute, the browser sends an HTTP GET command to the server with the user's input appended to the URL in the form of a query string:

GET /calc.html?op1=2&op2=2 HTTP/1.1  .  .  .Connection: Keep-Alive[blank line]

If, on the other hand, the <form> tag includes a method="post" attribute, the form is submitted to the server using an HTTP POST command. Rather than transmit user input in the URL, with a POST command the browser passes it in the body of the HTTP request:

POST /calc.html HTTP/1.1  .  .  .Content-Type: application/x-www-form-urlencodedContent-Length: 11op1=2&op2=2

Regardless of whether a GET or a POST command is used, when input from an HTML form is submitted back to the server, we say that a "postback" has occurred. Remember that term because you'll encounter it repeatedly in this and the next several chapters.

For a first-hand look at HTML forms in action, copy Calc.html to your PC's \Inetpub\wwwroot directory and call it up in Internet Explorer by typing the following URL:


Now type 2 into each of the form's text boxes and click the = button. As evidence that a postback occurred, observe what appears in the browser's address bar (shown in Figure 5-3). If you change the <form> tag to

<form method="post">

and repeat the experiment, you won't see any change in the URL. But the postback occurs just the same, and the Web server can access the user's input by examining the body of the request.

Figure 5-3. Calc.html following a postback.

Server-Side Processing

So far, so good. As Calc.html demonstrates, building the client half of a Web application is easy. After all, it's just HTML. The hard part is building the code that runs on the Web server. Something has to be running there to extract the user input from the URL (or from the body of the HTTP request if the postback was performed with POST instead of GET) and generate a new Web page that displays the sum of the inputs next to the = button. In other words, if the user enters 2 and 2 and clicks the = button, we'd like the Web server to respond by returning the following HTML:

<html>  <body>    <form>      <input type="text" name="op1" value="2" />      +      <input type="text" name="op2" value="2" />      <input type="submit" value="  =  " />      4    </form>  </body></html>

Note the Value attributes added to the <input type="text"> tags. Including the inputs in the page returned from the Web server following a postback perpetuates the illusion that the user is seeing one Web page when in fact he or she is seeing two in succession.

There are many ways to write applications that process input from HTML forms. One solution is an application that uses the Common Gateway Interface (CGI). CGI defines a low-level programmatic interface between Web servers and applications that run on Web servers. Applications that use it are typically written in a programming language called Perl, but they can be written in other languages as well. CGI applications read the input accompanying postbacks through server environment variables and standard input (stdin), and they write HTTP responses to standard output (stdout). CGI has a reputation for being slow because many implementations of it launch a new process to handle each incoming request. Despite this, CGI enjoys widespread use on UNIX-based Web servers. It's rarely used on the Windows platform.

Another solution-one that's more likely to find favor among Windows developers-is an ISAPI extension DLL. ISAPI stands for Internet Server Application Programming Interface. ISAPI extensions are Windows DLLs that are hosted by Internet Information Services. They're referenced by URL just like HTML files (for example, http://www.wintellect.com/calc.dll). IIS forwards HTTP requests to an ISAPI DLL by calling a special function exported from the DLL. The DLL, in turn, generates HTTP responses. ISAPI DLLs are faster than CGI applications because they (typically) run in the same process as IIS. And once loaded, they remain in memory awaiting subsequent requests. The downside to ISAPI DLLs is that they're difficult to write. An ISAPI developer must be comfortable with the architecture of Windows DLLs and also be willing to deal with HTTP messages at a very low level.

Curious to know what an ISAPI DLL looks like? Figure 5-4 shows the C++ source code for an ISAPI DLL that implements a Web calculator identical to the one shown in Figure 5-2. The heart of the DLL is the HttpExtensionProc function, which IIS calls on each and every request. The pECB parameter points to a structure containing information about the request, including a pointer to the query string (if any) accompanying the request. If the query string is empty, this implementation of HttpExtensionProc returns an HTML page depicting an empty calculator. Following a postback, however, it parses the op1 and op2 parameters from the query string and returns an HTML page that includes the sum of the inputs. In other words, it returns precisely the HTML we set as our goal a moment ago.


#include <windows.h>#include <httpext.h>#include <string.h>#include <stdlib.h>int GetParameter (LPSTR pszQueryString, LPSTR pszParameterName);BOOL WINAPI DllMain (HINSTANCE hInstance,                      DWORD fdwReason,                     LPVOID lpvReserved){    return (TRUE);}BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO* pVer){    pVer->dwExtensionVersion =         MAKELONG (HSE_VERSION_MINOR, HSE_VERSION_MAJOR);    lstrcpy (pVer->lpszExtensionDesc, "Calc ISAPI Extension");    return (TRUE);}DWORD WINAPI HttpExtensionProc (EXTENSION_CONTROL_BLOCK* pECB){    static char* szPrePostbackMessage =         "<html>\r\n"                                    \        "<body>\r\n"                                    \        "<form>\r\n"                                    \        "<input type=\"text\" name=\"op1\" />\r\n"      \        "+\r\n"                                         \        "<input type=\"text\" name=\"op2\" />\r\n"      \        "<input type=\"submit\" value=\"  =  \" />\r\n" \        "</form>\r\n"                                   \        "</body>\r\n"                                   \        "</html>";    static char* szPostPostbackMessage =         "<html>\r\n"                                            \        "<body>\r\n"                                            \        "<form>\r\n"                                            \        "<input type=\"text\" name=\"op1\" value=\"%d\" />\r\n" \        "+\r\n"                                                 \        "<input type=\"text\" name=\"op2\" value=\"%d\" />\r\n" \        "<input type=\"submit\" value=\"  =  \" />\r\n"         \        "%d\r\n"                                                \        "</form>\r\n"                                           \        "</body>\r\n"                                           \        "</html>";    //    // Build the response message body.    //    char szMessage[512];    if (lstrlen (pECB->lpszQueryString) == 0) {        // If the query string is empty, return a page         // that shows an empty calculator.        lstrcpy (szMessage, szPrePostbackMessage);    }    else {      // If the query string is not empty, extract       // the user input, process it, and return a page      // that shows inputs and outputs.      int a = GetParameter (pECB->lpszQueryString, "op1");      int b = GetParameter (pECB->lpszQueryString, "op2");      wsprintf (szMessage, szPostPostbackMessage, a, b, a + b);    }    //    // Build the response message header.    //    char szHeader[128];    DWORD dwCount = lstrlen (szMessage);    wsprintf (szHeader, "Content-type: text/html\r\n" \        "Content-Length: %lu\r\n\r\n", dwCount);    //    // Output the response message header.    //    HSE_SEND_HEADER_EX_INFO shei;    shei.pszStatus = "200 OK";    shei.pszHeader = szHeader;    shei.cchStatus = lstrlen (shei.pszStatus);    shei.cchHeader = lstrlen (shei.pszHeader);    shei.fKeepConn = FALSE;    pECB->ServerSupportFunction (pECB->ConnID,        HSE_REQ_SEND_RESPONSE_HEADER_EX, &shei, NULL, NULL);    //    // Output the response message body.    //    pECB->WriteClient (pECB->ConnID,                        szMessage,                        &dwCount,                        0);     //    // Indicate that the request was processed successfully.    //    return HSE_STATUS_SUCCESS;}int GetParameter (LPSTR pszQueryString, LPSTR pszParameterName){    char* p = strstr (pszQueryString, pszParameterName);    if (p != NULL) {        p += strlen (pszParameterName) + 1;        for (char* tmp = p; *tmp != '&' && *tmp != 0; tmp++)            ;        int len = tmp - p;        char* buffer = new char[len + 1];        strncpy (buffer, p, len);        int val = atoi (buffer);        delete buffer;        return val;    }    return 0;}
Figure 5-4. Source code for an ISAPI DLL.

Page 2 of 4

This article was originally published on February 26, 2002

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date