
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
1192 CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES
The ASP.NET Page Compilation Cycle
Regardless of which page model you make use of (single-file or code-behind), your *.aspx files (and any related code-behind file) are compiled on the fly into a valid .NET assembly. This assembly is then hosted by the ASP.NET worker process (aspnet_wp.exe) within its own application domain boundary (see Chapter 17 for details on AppDomains). The manner in which your website’s assembly is compiled under ASP.NET, however, is quite different.
Compilation Cycle for Single-File Pages
If you are making use of the single-file page model, the HTML markup, server side <script> blocks, and web control definitions are dynamically compiled into a class type deriving from System.Web.UI.Page. The name of this class is based on the name of the *.aspx file and takes an _aspx suffix (e.g., a page named MyPage.aspx becomes a class type named MyPage_aspx). Figure 3116 illustrates the basic process.
Figure 31-16. The compilation model for single-file pages
This dynamically compiled assembly is deployed to a runtime-defined subdirectory under the C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files root directory. The path beneath this root will differ based on a number of factors (hash codes, etc.), but if you are determined, eventually you will find the *.dll (and supporting files) in question. Figure 31-17 shows the generated assembly for the SinglePageModel example shown earlier in this chapter.
■Note Because these autogenerated assemblies are true-blue .NET binaries, if you were to open your web appli- cations–related *.dll using ildasm.exe or reflector.exe you would indeed find CIL code, metadata, and an assembly-level manifest.

CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES |
1193 |
Figure 31-17. The ASP.NET autogenerated assembly
Compilation Cycle for Multifile Pages
The compilation process of a page making use of the code-behind model is similar to that of the single-file model. However, the type deriving from System.Web.UI.Page is composed of three (yes, three) files rather than the expected two.
Looking back at the previous CodeBehindPageModel example, recall that the Default.aspx file was connected to a partial class named _Default within the code-behind file. If you have a background in ASP.NET 1.x, you may wonder what happened to the member variable declarations for the various web controls as well as the code within InitializeComponent(), such as event-handling logic. Under ASP.NET, these details are accounted for by a third aspect of the partial class generated in memory. In reality, this is not a literal file, but an in-memory representation of the partial class. Consider Figure 31-18.
In this model, the web controls declared in the *.aspx file are used to build the additional partial class that defines each UI member variable and the configuration logic that used to be found within the InitializeComponent() method of ASP.NET 1.x (we just never directly see it). This partial class is combined at compile time with the code-behind file to result in the base class of the generated _aspx class type (in the single-file page compilation model, the generated _aspx file derived directly from System.Web.UI.Page).
In either case, once the assembly has been created upon the initial HTTP request, it will be reused for all subsequent requests, and thus will not have to be recompiled. Understanding this factoid should help explain why the first request of an *.aspx page takes the longest, and subsequent hits to the same page are extremely efficient.

1194 CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES
Figure 31-18. The compilation model for multifile pages
■Note Under ASP.NET, it is now possible to precompile all pages (or a subset of pages) of a website using a command-line tool named aspnet_compiler.exe. Check out the .NET Framework 3.5 SDK documentation for details.
The Inheritance Chain of the Page Type
As you have just seen, the final generated class that represents your *.aspx file eventually derives from System.Web.UI.Page. Like any base class, this type provides a polymorphic interface to all derived types. However, the Page type is not the only member in your inheritance hierarchy. If you were to locate the Page type (within the System.Web.dll assembly) using the Visual Studio 2008 object browser, you would find that Page “is-a” TemplateControl, which “is-a” Control, which “is-a” Object (see Figure 31-19).
As you would guess, each of these base classes brings a good deal of functionality to each and every *.aspx file. For the majority of your projects, you will make use of the members defined within the Page and Control parent classes. By and large, the functionality gained from the System. Web.UI.TemplateControl class is only of interest if you are building custom Web Form controls or interacting with the rendering process.
The first parent class of interest is Page itself. Here you will find numerous properties that enable you to interact with various web primitives such as application and session variables, the HTTP request/response, theme support, and so forth. Table 31-4 describes some (but by no means all) of the core properties.

CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES |
1195 |
Figure 31-19. The derivation of an ASP.NET page
Table 31-4. Select Properties of the Page Type
Property |
Meaning in Life |
Application |
Allows you to interact with application variables for the current website |
Cache |
Allows you to interact with the cache object for the current website |
ClientTarget |
Allows you to specify how this page should render itself based on the requesting |
|
browser |
IsPostBack |
Gets a value indicating whether the page is being loaded in response to a client |
|
postback or whether it is being loaded and accessed for the first time |
MasterPageFile |
Establishes the master page for the current page |
Request |
Provides access to the current HTTP request |
Response |
Allows you to interact with the outgoing HTTP response |
Server |
Provides access to the HttpServerUtility object, which contains various |
|
server-side helper functions |
Session |
Allows you to interact with the session data for the current caller |
Theme |
Gets or sets the name of the theme used for the current page |
Trace |
Provides access to a TraceContext object, which allows you to log custom |
|
messages during debugging sessions |
|
|
Interacting with the Incoming HTTP Request
As you saw earlier in this chapter, the basic flow of a web session begins with a client logging on to a site, filling in user information, and clicking a Submit button to post back the HTML form data to a given web page for processing. In most cases, the opening tag of the form statement specifies an action attribute and a method attribute that indicates the file on the web server that will be sent the data in the various HTML widgets, as well as the method of sending this data (GET or POST):

1196 CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES
<form name="defaultPage" id="defaultPage" action="http://localhost/Cars/ClassicAspPage.asp" method = "GET">
...
</form>
Unlike classic ASP, ASP.NET does not support an object named Request. However, all ASP.NET pages do inherit the System.Web.UI.Page.Request property, which provides access to an instance of the HttpRequest class type. Table 31-5 lists some core members that, not surprisingly, mimic the same members found within the legacy classic ASP Request object.
Table 31-5. Members of the HttpRequest Type
Member |
Meaning in Life |
ApplicationPath |
Gets the ASP.NET application’s virtual application root path on the server |
Browser |
Provides information about the capabilities of the client browser |
Cookies |
Gets a collection of cookies sent by the client browser |
FilePath |
Indicates the virtual path of the current request |
Form |
Gets a collection of HTTP form variables |
Headers |
Gets a collection of HTTP headers |
HttpMethod |
Indicates the HTTP data transfer method used by the client (GET, POST) |
IsSecureConnection |
Indicates whether the HTTP connection is secure (i.e., HTTPS) |
QueryString |
Gets the collection of HTTP query string variables |
RawUrl |
Gets the current request’s raw URL |
RequestType |
Indicates the HTTP data transfer method used by the client (GET, POST) |
ServerVariables |
Gets a collection of web server variables |
UserHostAddress |
Gets the IP host address of the remote client |
UserHostName |
Gets the DNS name of the remote client |
|
|
In addition to these properties, the HttpRequest type has a number of useful methods, including the following:
•MapPath(): Maps the virtual path in the requested URL to a physical path on the server for the current request.
•SaveAs(): Saves details of the current HTTP request to a file on the web server (which can prove helpful for debugging purposes).
•ValidateInput(): If the validation feature is enabled via the Validate attribute of the page directive, this method can be called to check all user input data (including cookie data) against a predefined list of potentially dangerous input data.
Obtaining Brower Statistics
The first interesting aspect of the HttpRequest type is the Browser property, which provides access to an underlying HttpBrowserCapabilities object. HttpBrowserCapabilities in turn exposes numerous members that allow you to programmatically investigate statistics regarding the browser that sent the incoming HTTP request.
Create a new ASP.NET website named FunWithPageMembers (again, elect to use the File System option). Your first task is to build a UI that allows users to click a Button web control (named

CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES |
1197 |
btnGetBrowserStats) to view various statistics about the calling browser. These statistics will be generated dynamically and attached to a Label type (named lblOutput). The Click event handler is as follows:
protected void btnGetBrowserStats_Click(object sender, EventArgs e)
{
string theInfo = "";
theInfo += string.Format("<li>Is the client AOL? {0}</li>", Request.Browser.AOL);
theInfo += string.Format("<li>Does the client support ActiveX? {0}</li>", Request.Browser.ActiveXControls);
theInfo += string.Format("<li>Is the client a Beta? {0}</li>", Request.Browser.Beta);
theInfo += string.Format("<li>Does the client support Java Applets? {0}</li>", Request.Browser.JavaApplets);
theInfo += string.Format("<li>Does the client support Cookies? {0}</li>", Request.Browser.Cookies);
theInfo += string.Format("<li>Does the client support VBScript? {0}</li>", Request.Browser.VBScript);
lblOutput.Text = theInfo;
}
Here you are testing for a number of browser capabilities. As you would guess, it is (very) helpful to discover a browser’s support for ActiveX controls, Java applets, and client-side VBScript code. If the calling browser does not support a given web technology, your *.aspx page would be able to take an alternative course of action.
Access to Incoming Form Data
Other aspects of the HttpResponse type are the Form and QueryString properties. These two properties allow you to examine the incoming form data using name/value pairs, and they function identically to classic ASP. Recall from our earlier discussion of classic ASP that if the data is submitted using HTTP GET, the form data is accessed using the QueryString property, whereas data submitted via HTTP POST is obtained using the Form property.
While you could most certainly make use of the HttpRequest.Form and HttpRequest. QueryString properties to access client-supplied form data on the web server, these old-school techniques are (for the most part) unnecessary. Given that ASP.NET supplies you with server-side web controls, you are able to treat HTML UI elements as true objects. Therefore, rather than obtaining the value within a text box as follows:
protected void btnGetFormData_Click(object sender, System.EventArgs e)
{
//Get value for a widget with ID txtFirstName. string firstName = Request.Form("txtFirstName");
//Use this value in your page...
}
you can simply ask the server-side widget directly via the Text property for use in your program:
protected void btnGetFormData_Click(object sender, System.EventArgs e)
{
//Get value for a widget with ID txtFirstName. string firstName = txtFirstName.Text;
//Use this value in your page...
}

1198 CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES
Not only does this approach lend itself to solid OO principles, but also you do not need to concern yourself with how the form data was submitted (GET or POST) before obtaining the values. Furthermore, working with the widget directly is much more type-safe, given that typing errors are discovered at compile time rather than runtime. Of course, this is not to say that you will never need to make use of the Form or QueryString property in ASP.NET; rather, the need to do so has greatly diminished and is usually optional.
The IsPostBack Property
Another very important member of HttpRequest is the IsPostBack property. Recall that “postback” refers to the act of returning to a particular web page while still in session with the server. Given this definition, understand that the IsPostBack property will return true if the current HTTP request has been sent by a currently logged-on user and false if this is the user’s first interaction with the page. Typically, the need to determine whether the current HTTP request is indeed a postback is
most helpful when you wish to execute a block of code only the first time the user accesses a given page. For example, you may wish to populate an ADO.NET DataSet when the user first accesses an *.aspx file and cache the object for later use. When the caller returns to the page, you can avoid the need to hit the database unnecessarily (of course, some pages may require that the DataSet always be updated upon each request, but that is another issue). Assuming your *.aspx file has handled the page’s Load event (described in detail later in this chapter), you could programmatically test for postback conditions as follows:
protected void Page_Load(object sender, System.EventArgs e)
{
//Fill DataSet only the very first time
//the user comes to this page.
if (!IsPostBack)
{
// Populate DataSet and cache it!
}
// Use cached DataSet.
}
Interacting with the Outgoing HTTP Response
Now that you have a better understanding of how the Page type allows you to interact with the incoming HTTP request, the next step is to see how to interact with the outgoing HTTP response. In ASP.NET, the Response property of the Page class provides access to an instance of the HttpResponse type. This type defines a number of properties that allow you to format the HTTP response sent back to the client browser. Table 31-6 lists some core properties.
Table 31-6. Properties of the HttpResponse Type
Property |
Meaning in Life |
Cache |
Returns the caching semantics of the web page (e.g., expiration time, |
|
privacy, vary clauses) |
ContentEncoding |
Gets or sets the HTTP character set of the output stream |
ContentType |
Gets or sets the HTTP MIME type of the output stream |
Cookies |
Gets the HttpCookie collection sent by the current request |
IsClientConnected |
Gets a value indicating whether the client is still connected to the server |

CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES |
1199 |
Property |
Meaning in Life |
Output |
Enables custom output to the outgoing HTTP content body |
OutputStream |
Enables binary output to the outgoing HTTP content body |
StatusCode |
Gets or sets the HTTP status code of output returned to the client |
StatusDescription |
Gets or sets the HTTP status string of output returned to the client |
SuppressContent |
Gets or sets a value indicating that HTTP content will not be sent to the |
|
client |
|
|
Also, consider the partial list of methods supported by the HttpResponse type described in Table 31-7.
Table 31-7. Methods of the HttpResponse Type
Method |
Meaning in Life |
AddCacheDependency() |
Adds an object to the application catch (see Chapter 33) |
Clear() |
Clears all headers and content output from the buffer stream |
End() |
Sends all currently buffered output to the client, and then closes the |
|
socket connection |
Flush() |
Sends all currently buffered output to the client |
Redirect() |
Redirects a client to a new URL |
Write() |
Writes values to an HTTP output content stream |
WriteFile() |
Writes a file directly to an HTTP content output stream |
|
|
Emitting HTML Content
Perhaps the most well-known aspect of the HttpResponse type is the ability to write content directly to the HTTP output stream. The HttpResponse.Write() method allows you to pass in any HTML tags and/or text literals. The HttpResponse.WriteFile() method takes this functionality one step further, in that you can specify the name of a physical file on the web server whose contents should be rendered to the output stream (this is quite helpful to quickly emit the contents of an existing *.htm file).
To illustrate, assume you have added another Button type to your current *.aspx file that implements the server-side Click event handler like so:
protected void btnHttpResponse_Click(object sender, EventArgs e)
{
Response.Write("<b>My name is:</b><br>"); Response.Write(this.ToString());
Response.Write("<br><br><b>Here was your last request:</b><br>"); Response.WriteFile("MyHTMLPage.htm");
}
The role of this helper function (which you can assume is called by some server-side event handler) is quite simple. The only point of interest is the fact that the HttpResponse.WriteFile() method is now emitting the contents of a server-side *.htm file within the root directory of the website.
Again, while you can always take this old-school approach and render HTML tags and content using the Write() method, this approach is far less common under ASP.NET than with classic ASP.

1200 CHAPTER 31 ■ BUILDING ASP.NET WEB PAGES
The reason is (once again) due to the advent of server-side web controls. Thus, if you wish to render a block of textual data to the browser, your task is as simple as assigning a string to the Text property of a Label widget.
Redirecting Users
Another aspect of the HttpResponse type is the ability to redirect the user to a new URL:
protected void btnSomeTraining_Click(object sender, EventArgs e)
{
Response.Redirect("http://www.intertech.com");
}
If this event handler is invoked via a client-side postback, the user will automatically be redirected to the specified URL.
■Note The HttpResponse.Redirect() method will always entail a trip back to the client browser. If you simply wish to transfer control to an *.aspx file in the same virtual directory, the HttpServerUtility.Transfer() method (accessed via the inherited Server property) is more efficient.
So much for investigating the functionality of System.Web.UI.Page. We will examine the role of the System.Web.UI.Control base class in the next chapter. Next up, let’s examine the life and times of a Page-derived object.
■Source Code The FunWithPageMembers files are included under the Chapter 31 subdirectory.
The Life Cycle of an ASP.NET Web Page
Every ASP.NET web page has a fixed life cycle. When the ASP.NET runtime receives an incoming request for a given *.aspx file, the associated System.Web.UI.Page-derived type is allocated into memory using the type’s default constructor. After this point, the framework will automatically fire a series of events. By default, the Load event is automatically accounted for, where you can add your custom code:
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("Load event fired!");
}
}
Beyond the Load event, a given Page is able to intercept any of the core events in Table 31-8, which are listed in the order in which they are encountered (consult the .NET Framework 3.5 SDK documentation for details on all possible events that may fire during a page’s lifetime).
