Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp 2008 And The .NET 3.5 Platform [eng]

.pdf
Скачиваний:
78
Добавлен:
16.08.2013
Размер:
22.5 Mб
Скачать

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).

CHAPTER 31 BUILDING ASP.NET WEB PAGES

1201

Table 31-8. Select Events of the Page Type

Event

Meaning in Life

PreInit

The framework uses this event to allocate any web controls, apply themes,

 

establish the master page, and set user profiles. You may intercept this

 

event to customize the process.

Init

The framework uses this event to set the properties of web controls to their

 

previous values via postback or view state data.

Load

When this event fires, the page and its controls are fully initialized, and

 

their previous values are restored. At this point, it is safe to interact with

 

each web widget.

“Event that triggered

There is of course, no event of this name. This “event” simply refers to

the postback”

whichever event caused the browser to perform the postback to the web

 

server (such as a Button click).

PreRender

All control data binding and UI configuration has occurred and the

 

controls are ready to render their data into the outbound HTTP response.

Unload

The page and its controls have finished the rendering process, and the

 

page object is about to be destroyed. At this point, it is a runtime error to

 

interact with the outgoing HTTP response. You may, however, capture

 

this event to perform any page-level cleanup (close file or database

 

connections, perform any form of logging activity, dispose of objects, etc.).

 

 

When a C# programmer needs to handle events beyond Load, you might be surprised to find that there is no IDE support to do so! Rather, you must manually author a method in your code file taking the name Page_NameOfEvent. For example, here is how you can handle the Unload event:

public partial class Default : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

Response.Write("Load event fired!");

}

protected void Page_Unload(object sender, EventArgs e)

{

//No longer possible to emit data to the HTTP

//response, so we will write to a local file.

System.IO.File.WriteAllText(@"C:\MyLog.txt", "Page unloading!");

}

}

Note Each event of the Page type works in conjunction with the System.EventHandler delegate; therefore, the subroutines that handle these events always take an Object as the first parameter and an EventArgs as the second parameter.

The Role of the AutoEventWireup Attribute

When you wish to handle events for your page, you will need to update your <script> block or code-behind file with an appropriate event handler. However, if you examine the <%@Page%> directive, you will notice a specific attribute named AutoEventWireUp, which by default is set to true: