16.1. Client-Server Interaction over the Internet

 < Day Day Up > 

At its core, the Internet consists of resources and users who want to access Web-based resources. Three mechanisms are required to enable this access: a naming scheme (URI) to locate the resources, a protocol (HTTP) defining how the request/response process works, and a language (HTML) for publishing information and providing a means to navigate to the resources. This environment is quite different than that facing the Windows Forms programmer and presents the Web application developer with three major challenges:

  • HTML. The developer must be familiar with the Hypertext Markup Language in order to create Web Forms. Certainly, HTML generators are a useful accessory, but at some point, the developer has to understand the raw HTML.

  • HTTP. The primary task of a Web application is to respond to HTTP requests. These requests may be sent by either an HTTP GET or POST method and may contain headers in addition to the main message body. The language used by the Web developer must provide access to the full HTTP message. For example, to process a received form, a Web application must be able to extract data from fields on the form. Header information can be equally important. It can be used to control caching, identify the source of the request, and pass cookies between the client and server.

  • State. There is no intrinsic feature in HTML or HTTP that maintains the state of variables during the request/response operation. Figure 16-1 illustrates this. The client receives an empty survey form, fills it out, and posts it back to the server. The server detects an error and returns the same Web page which is blank again. Preserving data (state) over the round trip(s) between client and server is, perhaps, the biggest challenge facing the Web programmer.

    Figure 16-1. Request/response process


One of the best ways to understand and appreciate a technology is to compare it with other technologies that perform a similar task. With that in mind, this section takes a simple Web application and implements it in JavaScript, ASP, and ASP.NET. The objectives are twofold: to understand the problems associated with implementing a Web Forms application, and to illustrate the evolutionary approach that ASP.NET uses to meet these challenges. You do not need experience with ASP or JavaScript to understand the examples, but it helps. Their function is to demonstrate how the HTTP GET and POST methods send data to a server and maintain state information. The examples could just as easily use Perl or PHP scripting.

Web Application Example: Implementing a BMI Calculator

This application calculates the Body Mass Index (BMI) for a given height and weight. The user enters this information (see Figure 16-2) into a form and then clicks Submit Form to send the request to a server. The server calculates the BMI and returns a Web page that includes the BMI and original data entered.

Figure 16-2. BMI Web calculator


Realistically, the calculation could be done using JavaScript on the client's page without requiring a trip to the server. But let's suppose that we also want to record each time the calculator is used. For that, a trip to the server is necessary.

A JavaScript BMI Calculator

JavaScript remains the primary language used for client-side coding on a Web application. It is platform and browser independent (although there are a few exceptions). As we will see later, ASP.NET automatically generates JavaScript for its client-side processing.

The first example uses JavaScript on both the client and server side. Listing 16-1 contains the HTML and JavaScript that comprise the client-side page. For brevity, some of the less important code is excluded.

The HTML in the <body> defines the fields and buttons on a form bmi_input. This form defines GET as the HTML method for delivering the contents of the form to the server. When the button is clicked, control passes to the JavaScript function post that verifies the content of the form and sends it to the server. We'll get to the other JavaScript code after we look at the server side.

Listing 16-1. JavaScript/HTML to Display a BMI Calculator bmi.htm
 <HTML> <HEAD><TITLE>BMI Calculator</TITLE> <SCRIPT LANGUAGE="Javascript" TYPE="text/javascript"> <! // hrefstr is set to querystring values if any var hrefstr= location.search.substring(1,       location.search.length); function showbmi(){    if (hrefstr )   // display values in form fields    {       var parms   = hrefstr.split('&');       var f = self.document.forms[0];       f.bmi.value = eval(parms[0]);       f.hti.value = eval(parms[2]);       f.wt.value  = eval(parms[3]);       f.htf.value = eval(parms[1]);    } } // -->Code for Verify goes here. // Post Form to Web Host function post() {    if (verify()) //Call function to verify values in form fields    {       var f = self.document.forms[0];       f.bmi.value=0;       f.submit();  // Use HTTP GET to send form to server    } } //--> </script> </HEAD> <BODY bgcolor=#ffffff> <FORM NAME="bmi_input" method=GET action=bmicalculator.htm>    <table border=0 cellpadding=2 cellspacing=0 width=180       bgcolor=#cccccc>    <tr><td colspan=3 align=center><font size=2 color=#33333>       <b>BMI Calculator</b> </td></tr>    <tr><td><font size=2 ><b>BMI:</b></td>       <td colspan=2 ><input type=text size=5 name=bmi>    </td></tr>    <tr><td colspan=3><hr size=1></td></tr>    <tr><td><font size=2 >Height:</td>       <td><input type=text size=3 name=htf maxlength=1></td>       <td><input type=text size=3 name=hti maxlength=2></td>    </tr>    <tr><td>&nbsp;</td><td valign=top><font size=2>feet</td>        <td valign=top><font size=2>inches</td>    </tr>    <tr><td><font size=2 >Weight:</td>       <td colspan=2><input type=text size=3 name=wt             maxlength=3></td>    </tr>    <tr><td colspan=3 align=center>    <INPUT TYPE="button" VALUE="Submit Form" ONCLICK=             "self.post()";>    </td></tr>    <tr><td colspan=3>&nbsp;</td></tr>    </table> </FORM> <SCRIPT LANGUAGE="Javascript" TYPE="text/javascript"> <!--    showbmi();   // Fills form with values if they exist //--> </script> </body> </html> 

Using the GET method causes the form's data to be sent to the server as part of the URL. This string of data, referred to as a query string, contains name-value pairs that correspond to the name of the fields on the form, followed by their value. Figure 16-3 shows how form data is passed to the Web page bmicalculator.htm that calculates the BMI.

Figure 16-3. Passing data in a query string


Data is returned to the client in the same manner by appending it to the URL of the page containing the original form. Note that bmi is now set to the calculated value. Here is the server-side code that creates this response:

 <html><head></head> <body > <script language="javascript"> <!    // Use location.search to access the query string    var hrefstr = location.search.substring(1,          location.search.length);    var parms   = hrefstr.split('&');       feet    =  parms[1];       inch    =  parms[2];       pounds  =  parms[3];       totinches = eval(feet)*12 + eval(inch);       // ..... Calculate BMI       var h2 = totinches * totinches;       bmi = Math.round(eval(pounds) * 703 * 10/ h2)/10;       // --> Place code here to maintain count of visits.       //... Return value and original parameters as a query string       ndx = hrefstr.indexOf('htf');       self.location = 'bmi.htm?bmi=             '+bmi+"&"+hrefstr.substring(ndx);    //--> </script> </body></html> 

This code grabs the values from the query string using location.search, parses them, calculates the BMI, and then creates a URL for the page to be returned. The final step is for the client browser to display the calculated BMI value.

At the bottom of the code in Listing 16-1 is a call to the JavaScript function showbmi(). This function operates much like the preceding server code. It extracts the data from the query string and displays it in the appropriate fields on the form. Note that the function first confirms that the query string is not empty, as will be the case the first time the form is loaded.

The use of a query string is popular for applications that transfer small amounts of data. However, it becomes a problem with large forms because the query string is limited by the 2K maximum string length imposed by some browsers (IE). In addition, it raises obvious security concerns by exposing data in a URL line that can be altered by the user. Query string encryption can mitigate this problem and should be considered where it makes sense. A more robust solution is to use the HTTP POST method in place of GET.

An ASP (Active Server Pages) BMI Calculator

JavaScript is intended primarily for client-side scripting and cannot process data sent by a POST method. This example replaces the JavaScript server code with an ASP file that handles the POST request. We change one statement in the client code to indicate that POST is being used and to provide the name of the ASP server page:

 <FORM NAME="bmi_input" method=POST action=bmicalculator.asp> 

The code for the ASP server file is quite simple. It accesses the data passed from the client by referencing a request object. The data is passed to a VBScript function that calculates the BMI. A URL is then constructed that contains the query string with the BMI value and other parameters. The response.redirect method sends the form to the client's browser.

 <script language="VBScript" runat="server"> function getBMI(inch,feet,pounds)    totinches = feet*12 + inch    h2 = totinches * totinches    getBMI= (pounds * 703 * 10/ h2)/10 end function </script> <%    ' ... POST data is available in request object    inch    = request("hti")    feet    = request("htf")    pounds  = request("wt")    bmi= left(cstr(getBMI(inch,feet,pounds)),4)    ' ... return value and original parameters as a query string    hrefstr = "&htf=" +cstr(feet) + "&hti=" + cstr(inch) +              "&wt="&cstr(pounds)    response.redirect ("bmi3.htm?bmi="+bmi+hrefstr) %> 

This solution illustrates the fundamental ASP approach of using VBScript to interact with HTTP response and request objects. In addition, HTML and JavaScript can be intermixed with the ASP code to create Web Forms. Although this offers a degree of flexibility, it often results in a babel of code and inconsistent coding techniques.

Using ASP.NET to Implement a BMI Calculator

Before building an ASP.NET solution for the calculator, let's first take a general look at the features of ASP.NET and how they affect Web Forms programming. One way to understand the Web Forms model is to look at the requirements faced by the ASP.NET designers and how they met them.

  • Integration with the .NET environment. Web Forms are built on the Common Language Runtime (CLR) and have access to all types in the Framework Class Library, as well as special namespaces, such as System.Web and System.Web.UI that support Web applications. An important aspect of .NET integration is that it is required only on the server side; users are not required to install the .NET Framework on their computers. The emphasis is on rendering code that runs unaided on a browser. In fact, the emitted code is tailored to recognize the browser it is running on, so it can take full advantage of its features.

  • Linking client-side HTML controls with server-side controls. All controls in ASP.NET are classes. Controls that appear on a browser window are renderings based on HTML tags. To reconcile this differing technology, ASP.NET includes server-side controls that map directly to HTML tags. For example, the <input type = text> tags we saw in the earlier examples are represented by the HtmlInputText class found in the System.Web.UI.HtmlControls namespace. When information on a Web page is sent to a server, all HTML tags designated to run on the server are converted into their corresponding .NET class and compiled as members of the Web page. We'll see how HTML tags are mapped to server controls in the next example.

  • Compiled rather than interpreted Web pages. Traditionally, Web pages have been rendered by JavaScript, VBScript, Perl, and other scripting interpreters. In contrast, an ASP.NET Web page consists of a user interface defined by HTML, and interface logic written in a .NET language such as C# or VB.NET. The first request for an ASP page (.aspx file) results in the page being compiled into a .NET class. Further requests are then handled by the assembly created by compilation. For the user, it means faster Web access; for the developer, it means applications are developed using the same .NET tools available for desktop and component applications.

ASP.NET offers three models for implementing a Web application:

  • Inline code. The HTML markup code and application code (C#) coexist in a single .aspx file.

  • Code-behind. The markup code and application code are placed in separate files. The markup is in an .aspx file and the logic code resides in a .cs or dll file.

  • Partial classes. This is a variation of the code-behind model that places the markup and code in separate files. The difference is that the code-behind file is implemented using partial classes. It is stored as a .cs file and is compiled along with the markup file. This model is available only with ASP.NET versions 2.0 and later.

None of the models offers a performance advantage over the others. This leaves the choice of model up to one's preference or need for code separation. Let's now examine the models by using each to implement the BMI application.

Inline Code Model

The code for the BMI Web Form application is shown in Listing 16-2. Although it resembles an HTML page, an .aspx page is actually an XML-formatted page. It is processed on the server and used to generate a mixture of HTML and JavaScript that is sent to the browser.

Notice that this example contains actual C# code between the <script> </script> tags that calculates the BMI value. This method is executed on the server and is not visible as source code to the client.

Listing 16-2. ASP.NET Inline Code Implementation of a BMI Calculator
 <%@ Page Language="C#"  %> <HTML> <HEAD><TITLE>BMI Calculator</TITLE> <script  runat="Server">    // Calculate BMI from values on form    private void getBMI(object sender, System.EventArgs e)    {       try       {          decimal f =  Convert.ToDecimal(htf.Value);          decimal inch = Convert.ToDecimal(hti.Value);          decimal w = Convert.ToDecimal(wt.Value);          decimal totinches = f * 12 + inch;          decimal h2 = totinches * totinches;          decimal massIndex = (w * 703 * 10/ h2)/10;          bmi.Value = massIndex.ToString("##.##");       }catch (Exception ex)          { bmi.Value=" "; }     } </script> </HEAD> <BODY bgcolor=#ffffff> <FORM NAME="bmi_input" runat=server>    <table border=0 cellpadding=2 cellspacing=0 width= 180          bgcolor=#cccccc>    <tr><td colspan=3 align=center><font size=2>          <b>BMI Calculator</b>          </td></tr>    <tr><td><font size=2 ><b>BMI:</b></td>          <td colspan=2 ><input type=text size=5 id=bmi                   runat=server></td></tr>    <tr><td colspan=3><hr size=1></td>    </tr>    <tr><td><font size=2 >Height:</td>          <td><input type=text size=3 id=htf maxlength=1                   runat=server></td>          <td><input type=text size=3 id=hti maxlength=2                   runat=server></td>    </tr>    <tr><td>&nbsp;</td><td valign=top><font size=2>feet</td>          <td valign=top><font size=2>inches</td>    </tr>    <tr><td><font size=2 >Weight:</td>          <td colspan=2><input type=text size=3 id=wt maxlength=3                   runat=server></td>    </tr>    <tr><td colspan=3 align=center>    <INPUT TYPE="button" VALUE="Submit Form"             OnServerClick="getBMI"             id=bmiButton runat=server>          </td>    </tr>    <tr><td colspan=3>&nbsp;</td>    </tr>    </table> </FORM> </body> </html> 

To understand the differences between standard HTML and ASP.NET code, let's compare how controls are specified in Listing 16-1 versus Listing 16-2.

  • The most obvious difference is the addition of the runat=server attribute in the <Form> and <Input> tags. This designation converts any HTML elements to HTML server controls that can be processed by the server-side code prior to sending them to the browser. The following code illustrates how an <input> tag is transformed before it emitted to the client's browser:

     ASP.NET statement:    <input type=text size=3 id=wt maxlength=3 runat=server> HTML statement emitted:    <input name="wt"  type="text" size="3"       maxlength="3" value="168" /> 

  • The <Form> tag does not include the method or action attribute. By default, the POST method is used. As the following code shows, the page is self-referencing, which means the page sends the form to itself.

     ASP.NET statement:    <FORM NAME="bmi_input" runat=server> HTML statement emitted:    <form name="_ctl0" method="post" action="bmi.aspx"       > 

  • The <button> tag contains an OnServerClick event delegate. This indicates that a method on the server will handle the event. The resulting HTML references a JavaScript function that has been added to post the form when the button is clicked.

     HTML statement emitted:    <input language="javascript" onclick =       "__doPostBack('_ctl1','')" name="_ctl1"       type="button" value="Submit Form" /> 

ASP.NET also adds three hidden fields to the HTML page it returns to the browser:

 <input type="hidden" name="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" value="" /> <input type="hidden" name="__VIEWSTATE"      value="dDwxMzc5NjU4NTAwOzs+iIczTTLHA74jT/02tIwU9FRx5uc=" /> 

The first field, __EVENTTARGET, specifies the control that invoked the request (known as a postback) to the server; the second, __EVENTARGUMENT, contains any parameters required by the event. The __VIEWSTATE field is by far the most interesting.

View State

View state is the feature in ASP.NET that automatically maintains the state of server-side controls (controls declared with runat=server) as a form makes the round trip between the client and the server. In other words, it allows a page to place data in a control such as a ListBox or GridView one time usually when the page is first loaded and ensures the data is retained as subsequent postbacks occur. Here is how it works.

When a page is posted back to the server, the data in the hidden __VIEWSTATE field is deserialized and used to set the state of controls and the overall Web page. Data received in the HTTP request as part of a POST operation is used to set the values of those related controls (note that for controls whose contents are posted TextBox, CheckBox, RadioButtons the posted data overwrites the view state data). The __VIEWSTATE field is then updated before it is passed back to the client. The returned view state value plays no role on the client other than to represent a snapshot of control values at the time the page is received by the browser.

Because the view state string can be viewed in a source listing, questions about security become a legitimate issue. However, unlike the query string, the value is not represented as clear text.

 value="dDwxMzc5NjU4NTAwOzs+iIczTTLHA74jT/02tIwU9FRx5uc=" 

By default, a machine-specific authentication code is calculated on the data and appended to the view state string. The full string is then Base64 encoded. It is possible to decode the string, but the difficulty in doing so will thwart most casual efforts. Tampering with the string can also be detected by the server and results in a security exception being thrown. As always, use Secure Sockets Layer (SSL) to ensure absolute security for Internet communications.

Core Note

Maintaining view state data within a Web page makes the page independent of the server. This means that a Web page request can be sent to any server in a Web farm rather than restricting it to a single server.


Performance is another issue that must be considered when working with the view state value. By default, it maintains data for all server-side controls on the form. The control information is not limited to only the data value associated with the control. For example, when a DataGrid is used, the view state includes not only the data in each cell, but also column and row headers, and related style attributes. The view state data can easily add several thousand bytes to a Web page and slow performance.

To improve performance, you may want to disable view state for the Web page and apply it only to selected controls. Set the EnableViewState attribute in the @Page directive to disable it at the page level:

 <%@ Page Language="C#" EnableViewState="False" %> 

Then, to enable view state for an individual control, apply the attribute as shown in the following code:

 <input type=text size=3 id=wt maxlength=3 EnableViewState=true       runat=server > 

Of course, you can also take the opposite tact and leave view state on for the page and turn it off for selective controls.

The decision to enable or disable view state is one of the key decisions in designing a Web page that displays large amounts of data in a server-side control. The easiest approach is to allow ViewState to take care of the details. However, this can result in a large HTML payload being transferred repeatedly between browser and server. An alternative is to design the code to reload the data into the controls on each postback. The data may be fetched from the original source usually a database or it may be stored on the server in session state or a cache. These last two options are described in Chapter 17, "The ASP.NET Application Environment."

Core Note

ViewState maintains not only a control's data but also its state. For example, it keeps track of the last item(s) selected in a ListBox and permits the ListBox to be redisplayed in its most recent state. The drawback of manually repopulating a control, rather than using ViewState, is that this state information is lost.


The @Page Directive

The last element to discuss in Listing 16-2 is the @Page directive. An .aspx file can contain only one @Page directive, and it is typically although not required the first statement in the file. The purpose of this directive is to assign attribute values that control the overall behavior of the Web page, as well as specify how the page is assembled and compiled on the server. Table 16-1 lists selected attributes for the @Page directive.

Table 16-1. Attributes for the @Page Directive

Attribute/Value

Description

EnableSessionState = value

Specifies the type of access the page has to the session state information. Sessions are discussed in Chapter 17.

EnableViewState = bool

Enables or disables view state for the page. Individual controls can override this value.

EnableViewStateMac = bool

Is used to make the view state more secure by adding a validator hash string to the view state string that enables the page to detect any possible attempt at corrupting original data.

SmartNavigation = bool

Setting this to true can improve the rendering of pages for users of Internet Explorer 5.0 and later. Its improvements include

  • Eliminating flickering when a page loads.

  • Retaining the input focus on the field last having it.

  • Preserving the scroll position on pages longer than one screen.

ErrorPage = url

Specifies the URL of a Web page that is called when an unhandled exception occurs.

Culture = string

A culture setting for the page based on the CultureInfo class. This attribute affects how culture-dependent functions, such as numbers and dates, are displayed. The following setting causes a DateTime object to be displayed in a European format using German months and days: Culture="de-DE". These settings can also be set in the Web.config file as described in the next chapter.

UICulture = id

Specifies the user interface culture for this page.

Example: UICulture="de"

trace = bool

Turns tracing on or off for the page. Default is false. When tracing is on, diagnostic information about a single request for an .aspx page is collected. The results of the trace are available programmatically and are appended as a series of tables at the bottom of the browser output. Tracing is discussed in Chapter 17.

Inherits = class name

Specifies the base class used to generate a class from the .aspx file. The default is System.Web.UI.Page. If code-behind is used, the class name from this code is used.

MasterPageFile = master page

Specifies the "master page" from which the current page visually inherits its layout. Introduced with 2.0.

theme = theme name

Specifies the subdirectory containing the .skin file (specifies the appearance of controls) and any other images and style sheets that define the look and style (theme) of a page. The theme file is stored in the /app_themes subdirectory. Introduced with 2.0.

Language = language

Specifies the language for inline code.

Codebehind = *.dll

Specifies the name of a compiled code-behind file. This file must be in the \bin subdirectory of the application.

Codefile = *.cs

Specifies a code-behind file containing a partial class. Introduced with 2.0.

Src = path

Specifies a code-behind file containing source code.


The Codebehind, Codefile, and Src attributes specify the assembly or source file containing the business logic code for the page. Instead of placing code between <script></script> tags as we did in Listing 16-2, the code is placed in a separate code-behind file that is referenced by these attributes. Before discussing code-behind, let's look at some additional directives that are frequently used in .aspx pages.

Other Directives
@Import Directive

This directive is used to import a namespace in an .aspx page. It serves the same purpose as the C# using statement.

 <%@ Import namespace="System.Net" %> 

Several namespaces are automatically imported in a page, making this directive unnecessary in most cases. These namespaces include

  • System, System.IO

  • System.Web.UI, System.Web.UI.HtmlControls, System.Web.UI.WebControls

  • System.Web, System.Web.SessionState, System.Web.Caching

  • System.Text, System.Text.RegularExpressions

@Assembly Directive

This directive links an assembly to the current page while the page is being compiled. This provides the page with access to all types in the assembly. It takes two forms:

 <%@ Assembly Name="webfunctions" %> <%@ Assembly src="/books/4/253/1/html/2/webfunctions.cs" %> 

The first version references an assembly that may be private or deployed in the Global Assembly Cache; the second statement causes the source to be dynamically compiled into an assembly that is linked to the Web page. Note that assemblies in the application's \bin subdirectory are automatically linked to a page and do not need to be referenced.

@Register Directive

This directive associates alias names with namespaces and classes. Its purpose is to provide a convenient syntax for adding custom controls to a Web page. The directive takes two forms:

Syntax:

 <%@ Register Tagprefix="tagprefix" Namespace="namespace"       Assembly="assembly" %> <%@ Register Tagprefix="tagprefix" Tagname="tagname"       src="/books/4/253/1/html/2/pathname" %> 

Attributes:

Tagprefix

Alias for a namespace.

Namespace

The namespace to associate with Tagprefix.

Assembly

Assembly in which namespace resides.

Tagname

Alias to associate with a class.

Src

The file containing the user control


The first form of the directive is used to add an ASP.NET server control to a page; the second form is used with a custom control contained in a source file. In the latter case, the TagPrefix and TagName are always used together as a colon-separated pair. Here is a code segment that places a user control defined in the file hdr.ascx on a Web page. The @Register directive defines the alias pair that is used to declare the control on the Web page.

 <%@ Register TagPrefix="uc1" TagName="hdr" src="/books/4/253/1/html/2/hdr.ascx" %> <form  method="post" runat="server">       <uc1:hdr  runat="server"></uc1:hdr> </form> 

We'll make use of this directive in Section 16.4, which provides examples of how to create and use custom controls. Note that @Register directive information also can be stored in the Web.config file (see Chapter 17), eliminating the need to place it in a Web page.

The Code-Behind Model

The example in Listing 16-2 contains both C# to implement program logic and HTML to render the user interface. A Web page can also be configured as an .aspx file, containing only the interface code and a separate code-behind file that contains the program logic and serves as the base class for compiling the .aspx file (see Figure 16-4). This code-behind file takes the .cs extension.

Figure 16-4. How ASP.NET responds to a Web page request


The code-behind page is linked to the .aspx file as an assembly or source file using the Codebehind or Src attributes of the @Page directive. If the Codebehind attribute is used, the assembly must be stored in the \bin directory of the application.

Let's now look at how the code in Listing 16-2 can be changed to use a code-behind file. We create a code-behind file named bmicb.cs (see Listing 16-3) to replace the code currently between the <script/> tags. The @Page directive links this file to the .aspx file:

 <%@ Page Language="C#" src="/books/4/253/1/html/2/bmicb.cs" Inherits="BMI" %> 

The code-behind page is always structured as a class whose name must be specified by an Inherits attribute. This class is shown in Listing 16-3. Let's take a close look at it, because knowledge of how the code-behind file and the .aspx file interact on the server is essential to understanding the ASP.NET Web page model.

Listing 16-3. Code-Behind File for BMI Calculator bmicb.cs
 using System; using System.Web.UI.HtmlControls; public class BMI : System.Web.UI.Page {    //     <input type=text id=htf runat=server>    protected HtmlInputText htf;    //     <input type=text id=hti runat=server>    protected HtmlInputText hti;    //     <input type=text id=wt runat=server>    protected HtmlInputText wt;    //     <input type=text id=bmi runat=server>    protected HtmlInputText bmi;    //     <input type="button" VALUE="Submit Form" id=bmiButton    //      runat=server>    protected HtmlInputButton bmiButton;    override protected void OnInit(EventArgs e)    {       // Delegate to handle button click on client       bmiButton.ServerClick += new EventHandler(getBMI);    }    protected void getBMI(object sender, System.EventArgs e)    {       decimal f =  Convert.ToDecimal(htf.Value);       decimal inch = Convert.ToDecimal(hti.Value);       decimal w = Convert.ToDecimal(wt.Value);       decimal totinches = f * 12 + inch;       decimal h2 = totinches * totinches;       decimal massIndex = (w * 703 * 10/ h2)/10;       bmi.Value = massIndex.ToString("##.##");    } } 

The first thing to observe from this listing is that it consists of one class BMI that derives from the System.Web.UI.Page class. The Page class is to Web Forms what the Form class is to Windows Forms. Like the Form, it has a sequence of events that are fired when it is initialized and loaded. It also has several properties that control its behavior many of which correspond to the @Page directive attributes already discussed. We'll look at the Page class later in this section.

One of the trickiest aspects of learning ASP.NET is grasping how server-side controls work specifically, how the content and action of controls displayed on a browser are managed on the server. Figure 16-5 illustrates the relationship. Each server control is declared as a field in the BMI class. When the values of the controls are posted to the server, they are assigned as field values. In this example, all of the controls are HTML controls that is, standard HTML controls with the runat=server attribute.

Figure 16-5. Binding between code-behind page and .aspx page


The id value in the tag must match the field name identically. The field types are defined in the System.Web.UI.HtmlControls namespace. Each HTML control has a one-to-one mapping with an HTML tag, as shown in Table 16-2.

Table 16-2. HTML Controls and Their Tags

Control

HTML Tag

HtmlAnchor

<a>

HtmlSelect

<select>

HtmlTextArea

<textarea>

HtmlButton

<input type=button> <input type=submit>

HtmlCheckBox

<input type=checkbox>

HtmlRadio

<input type=radio>

HtmlHidden

<input type=hidden>

HtmlInputText

<input type=text>

HtmlInputFile

<input type=file>

HtmlForm

<form>

HtmlImage

<img>

HtmlTable

<table>

 HtmlTableRow HtmlTableCell 

 <tr> <td> 

HtmlGenericControl

All other unmapped tags such as <div> and <p>


Handling Events on the Server

In our example, clicking the Submit button sends a request to the server to calculate and return a BMI value based on the form's content. The .aspx code for the button looks like this:

 <INPUT TYPE="button" VALUE="Submit Form"       id=bmiButton runat=server> 

Compare this with the tag defining the button in Listing 16-2:

 <INPUT TYPE="button" VALUE="Submit Form" OnServerClick="getBMI"       id=bmiButton runat=server> 

This earlier code defines a method (getBMI) to be called when the click event occurs. Because our current example has the method placed in a code-behind file, there is no reference to it in the .aspx file. Instead, the server-side code handles the event using a standard delegate-based event handling approach. An event handler method (getBMI) is defined that matches the signature of the EventHandler delegate. Then, using the button's id value, we create a delegate instance that registers the method for the ServerClick event of the bmiButton control:

 bmiButton.ServerClick += new EventHandler(getBMI); 

When the button is clicked on the browser, the contents of the form are posted to the server, which recognizes the button click event and calls the appropriate event handler. This raises one obvious question: Because a form can contain any number of buttons, how does the server determine the event that triggered the post-back? The answer is that the name of the control causing the event is passed to the server in the __EVENTTARGET hidden field that was discussed earlier. This is handled automatically by ASP.NET; the developer's responsibility is to create the delegate and server-side control event handler.

Code-Behind with Partial Classes

The problem with the preceding code-behind model is that each control in the markup page must be explicitly mapped to a protected member in the code page. Changes to the markup page require that the code page members be kept in sync. The use of a partial class in the code-behind page eliminates the need for the protected class member. The partial class is compiled with the markup page, which permits the markup and code sections to directly access each other's members. The effect is the same as using the inline model. To demonstrate this, extract the getBMI method from the inline code (refer to Listing 16-2) and place it in its own file inside a partial class. The result is a code-behind partial class as shown in Listing 16-4.

Listing 16-4. Code-Behind Partial Class File for BMI Calculator
 //file: bmi.aspx.cs using System; partial class BMICalc: System.Web.UI.Page{} {    void getBMI (Object sender, EventArgs e)    {       try       {          decimal f =  Convert.ToDecimal(htf.Value);          decimal inch = Convert.ToDecimal(hti.Value);          decimal w = Convert.ToDecimal(wt.Value);          decimal totinches = f * 12 + inch;          decimal h2 = totinches * totinches;          decimal massIndex = (w * 703 * 10/ h2)/10;          bmi.Value = massIndex.ToString("##.##");       } catch (Exception ex)          { bmi.Value=" "; }    } } 

The client markup page links to this code by specifying the file name and the partial class name in its @Page declaration:

 <%@ Page codefile="bmi.aspx.cs" inherits="BMICalc" %> 

ASP.NET 2.0 continues to support the original code-behind model, but it should be used only for preexisting code. New development should employ the inline or partial class model.

Page Class

The first time an ASP.NET page is accessed, it is parsed and compiled into an assembly. This is a relatively slow process that results in the delay one notices the first time the .aspx page is called. Subsequent requests receive much faster responses because the assembly handles them. This assembly consists of a single class that contains all of the server-side code, as well as static HTML code.

The compiled class derives either directly from the System.Web.UI.Page class or indirectly via an intermediate code-behind class. It is important to understand the members of the Page class. Its methods and properties define much of the functionality the .aspx code relies on for handling requests, and its events define junctures at which the code must perform initialization and housekeeping tasks.

Table 16-3 summarizes some of the important properties of the Page class.

Table 16-3. Selected Properties of the Page Class

Control

HTML Tag

Application

Returns the HttpApplicationState object that contains information about the executing application.

EnableViewState

Boolean value that indicates whether controls retain their values between requests. Default is true.

IsPostBack

Boolean value indicating whether the page is being loaded and accessed for the first time or in response to a postback.

PreviousPage

Provides a reference to the page originating the call. Information on the calling page is available when the page is reached by cross-page posting or is invoked by HttpUtilityServer.Transfer().

Request

Gets the HttpRequest object that provides access to data contained in the current request.

Response

Gets the HttpResponse object that is used to programmatically send HTTP responses to the client.

Server

Gets the HttpServerUtility object provided by the HTTP runtime.

Session

Gets the HttpSessionState object, which provides information about the state of the current session.


The Application and Session properties provide state information for a Web application and are discussed in the next chapter.

HttpRequest and HttpResponse Objects

The Request and Response properties expose underlying objects (HttpRequest and HttpResponse) that are used to interact with the incoming HTTP request and the outgoing HTTP response. These classes include properties and methods to read cookies from the client's machine, receive uploaded files, identify the browser, get the IP address of the client, and insert custom content into the outgoing HTTP body. Chapter 17 discusses these in detail, but as an introduction, let's look a simple code example that illustrates the fundamentals of accessing these objects within an .aspx page.

The next code segment uses the Response.Output.Write method to write information into the HTTP content returned to a browser. The information comes from Request.Browser properties that indicate the type of client. Note that by using <% %> tags in an .aspx file, we can intersperse these statements with the HTML code. Do this judiciously. An .aspx file is much easier to read when the C# code is segregated between <script> tags, as shown in Listing 16-2, or placed in a code-behind file.

 <table border=0 cellpadding=0 cellspacing=0>    <%       Response.Output.Write(@"<tr><td>Browser Version: {0}            </td></tr>", Request.Browser.Type); //  IE6    %> </table> 

Using IsPostBack

This useful property enables a Web application to distinguish between a postback and the first time a Web page is invoked. It lets the program know when to initialize values that only need to be set once. A typical example is a ListBox that contains unchanging values, such as all of the states in the United States. When assigned, these values are subsequently maintained in the __VIEWSTATE hidden field and do not need to be re-initialized.

To demonstrate, let's extend our BMI inline code example to display the date and time when the user first requests the Web page. When displayed, this date and time remain unchanged no matter how many times the BMI calculation is subsequently requested.

The date and time are displayed using a <span/> tag, which is added beneath the opening <FORM> tag (refer to Figure 16-2) in the bmi.aspx file. (This is equivalent to a Web control Label.)

 <FORM NAME="bmi_input" runat=server> <span id=sessionstart runat=server/><br> 

In the code section, we must assign the date and time to the inner contents of the <span> tag the first time that the page is called. Here is the code to do this:

 void Page_Load(object sender, EventArgs e) {    if(!this.IsPostBack) sessionstart.InnerText =          DateTime.Now.ToString(); } 

Recall that the code on the server is compiled into a class that inherits from the Page class. This class includes several events discussed in the following section that are triggered when a request is sent to the Web page. The most useful is the Page_Load event that is raised each time the page is loaded. Applications typically include an event hander for it that checks IsPostBack and performs initialization if the call is not a postback. In this example, the InnerText field of the <span> tags is set to the date and time the first time the page is loaded.

Page Events

The preceding example demonstrates how the Page_Load event handler provides a convenient place to initialize variables. The Load event is only one of four events defined by the System.Web.UI.Page class. The others are Init, PreRender, and UnLoad. These occur in a fixed sequence as shown in Figure 16-6.

Figure 16-6. System.Web.UI.Page events


To best understand the role of each, let's look at what happens when a form is posted to a server:

  1. The temporary class for the page is generated, compiled, and loaded. The ASP.NET runtime begins processing the page and fires the Page.Init event.

  2. The state of the page object and its controls are set from data in the POST variables and the __VIEWSTATE field. The Page.Load event is fired. Typically, the OnLoad event handler is overridden to initialize control values and establish database connections.

  3. Events related to all controls are fired. The last event that occurs is the one that caused the postback. After all server-side control events have been processed, the Page.PreRender event occurs.

  4. The page enters its rendering phase. The class that represents the page calls the Render method on all the individual controls included in the page.

  5. The HTTPResponse is issued, sending the rendered HTML to the client. The Page.Unload event completes the process. This event should be used to release resources by closing files and database connections.

Although Web Forms provide an event-based model that is similar to the Windows Forms model, the key difference is that the events do not actually fire until control returns to the server. There they must occur in the fixed order described in this section. Your code interacts with these events by overriding or replacing the event handlers for the events described in Figure 16-6.

Cross-Page Posting

A Web page in .NET 1.x could only post to itself. In 2.0, you can designate a target page, other than the current page, by setting the PostBackUrl property of a Button, ImageButton, or LinkButton to the address of the target page. Posting occurs when the button is clicked. To demonstrate, the button defined in this code posts the contents of the current form to nextstep.aspx when the button is clicked.

 <asp:button     text="redirect"    postbackurl="nextstep.aspx"    runat="Server"> </asp:button> 

The page to which a form is posted can view the contents of the calling form using the PreviousPage property. In this example, nextstep.aspx uses PreviousPage to get the text value of the button that initiated the post. Of course, this technique would be used more often to gather data from text boxes on the posted form.

 if(!this.IsPostBack) {    // Sets text to the value of the Text property of the button    // from the calling form having the id postBtn: "redirect"    string text =          ((Button)PreviousPage.FindControl("postBtn")).Text; } 

     < Day Day Up > 


    Core C# and  .NET
    Core C# and .NET
    ISBN: 131472275
    EAN: N/A
    Year: 2005
    Pages: 219

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net