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

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

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

178 C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

Creating a Component

The next example demonstrates a simple component that reads a random Sherlock Holmes quote from an XML file. (This XML file is available on the Internet and freely reusable via the GNU Public License.) The component consists of two classes—a Quotation class that represents a single quote and a SherlockQuotes class that allows you to read a random quote. Both of these classes are placed in the SherlockLib namespace.

The first listing shows the SherlockQuotes class, which loads an XML file containing quotes in QEL (Quotation Exchange Language, an XML dialect) when it’s instantiated. The SherlockQuotes class provides a public GetRandom() quote method that the web-page code can use.

using System; using System.Xml;

namespace SherlockLib

{

public class SherlockQuotes

{

private XmlDocument quoteDoc; private int quoteCount;

public SherlockQuotes(string fileName)

{

quoteDoc = new XmlDocument(); quoteDoc.Load(fileName);

quoteCount = quoteDoc.DocumentElement.ChildNodes.Count;

}

public Quotation GetRandomQuote()

{

int i;

Random x = new Random(); i = x.Next(quoteCount-1);

return new Quotation( quoteDoc.DocumentElement.ChildNodes[i] );

}

}

}

Each time a random quotation is obtained, it is stored in a Quotation object. The listing for the Quotation class is as follows:

using System; using System.Xml;

namespace SherlockLib

{

public class Quotation

{

private string qsource; public string Source

{

get {return qsource;} set {qsource = value;}

}

private string date; public string Date

{

C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

179

get {return date;} set {date = value;}

}

private string quotation; public string QuotationText

{

get {return quotation;} set {quotation = value;}

}

public Quotation(XmlNode quoteNode)

{

if ( (quoteNode.SelectSingleNode("source")) != null) qsource = quoteNode.SelectSingleNode("source").InnerText;

if ( (quoteNode.Attributes.GetNamedItem("date")) != null) date = quoteNode.Attributes.GetNamedItem("date").Value;

quotation = quoteNode.FirstChild.InnerText;

}

}

}

Using a Component Through the App_Code Directory

The simplest way to quickly test this class is to copy the source code files to the App_Code subdirectory in a web application. You can take this step in Windows Explorer or use Visual Studio (Website Add Existing Item).

Now you might want to import the SherlockLib namespace in your web page to make its classes more readily available, as shown here:

using SherlockLib;

Finally, you can use the class in your web-page code just as you would use a class from the

.NET Framework. Here’s an example that displays the quotation information on a web page:

protected void Page_Load(object sender, System.EventArgs e)

{

// Put user code to initialize the page here. SherlockQuotes quotes = new

SherlockQuotes(Server.MapPath("./sherlock-holmes.xml")); Quotation quote = quotes.GetRandomQuote();

Response.Write("<b>" + quote.Source + "</b> (<i>" + quote.Date + "</i>)"); Response.Write("<blockquote>" + quote.QuotationText + "</blockquote>");

}

When you run this application, you’ll see something like what’s shown in Figure 5-8. Every time you refresh the page, you’ll see a different quote.

Note When you use the App_Code directory, you face another limitation—you can use only one language. This limitation results from the way that ASP.NET performs its dynamic compilation. Essentially, all the classes in the App_Code directory are compiled into a single directory, so you can’t mix C# and VB.

180 C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

Figure 5-8. Using the component in your web page

Using a Component Through the Bin Directory

Assuming that your component provides a significant piece of functionality and that it may be reused in different applications, you’ll probably want to create it using a separate project. This way, your component can be reused, tested, and versioned separately from the web application.

To create a separate component, you need to use Visual Studio to create a class library project. Although you can create this using a separate instance of Visual Studio, it’s often easier to load both your class library project and your web application into Visual Studio at once to assist in debugging. This allows you to easily modify both the web application and the component code at the same time and single-step from a web-page event handler into a method in your component. To set this up, create your web application first. Then, select File Add New Project, and select the Class Library project type (see Figure 5-9). Notice that when you create a class library project, you don’t specify a virtual directory. Instead, you choose a physical directory on your local hard drive where the source files will be stored.

Note If you are using the scaled-down version of Visual Studio known as Visual Web Developer 2005 Express Edition, you won’t be able to create class library projects. Your only alternative is to use the App_Code approach or build the class library with another version of Visual Studio.

Once you’ve added the code to your class library project, you can compile your component by right-clicking the project in the Solution Explorer and choosing Build. This generates a DLL assembly that contains all the component classes.

To allow your web application to use this component, you need to add an assembly reference to the component. This allows Visual Studio to provide its usual syntax checking and IntelliSense. Otherwise, it will interpret your attempts to use the class as mistakes and refuse to compile your code.

C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

181

Figure 5-9. Adding a class library project to a solution

To add a reference, choose Website Add Reference from your web application. The Add Reference dialog box includes several tabs:

.NET: This allows you to add a reference to a .NET assembly. You can choose from the list of well-known assemblies that are stored in the registry. Typically, you’ll use this tab to add a reference to an assembly that’s included as part of the .NET Framework.

COM: This allows you to add a reference to a legacy COM component. You can choose from a list of shared components that are installed in the Windows system directory. When you add a reference to a COM component, .NET automatically creates an intermediary wrapper class known as an interop assembly. You use the interop assembly in your .NET code, and the interop assembly interacts with the legacy component.

Projects: This allows you to add a reference to a .NET class library project that’s currently loaded in Visual Studio. Visual Studio automatically shows a list of eligible projects. This is often the easiest way to add a reference to one of your own custom components.

Browse: This allows you to hunt for a compiled .NET assembly file (or a COM component) on your computer. This is a good approach for testing custom components if you don’t have the source project or you don’t want to load it into Visual Studio where you might inadvertently modify it.

Figure 5-10 compares two ways to add a reference to the SherlockLib component—by adding a reference to a currently loaded project and by adding a reference to the compiled DLL file.

182 C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

Figure 5-10. Adding a reference to SherlockLib.dll

Once you add the reference, the corresponding DLL file will be automatically copied to the Bin directory of your current project. You can verify this by checking the Full Path property of the reference in the Properties window or just by browsing to the directory in Windows Explorer. The nice thing is that this file will automatically be overwritten with the most recent compiled version of the assembly every time you run the web application.

It really is that easy. To use another component—either from your own business tier, from a third-party developer, or from somewhere else—all you need to do is add a reference to that assembly.

Tip ASP.NET also allows you to use assemblies with custom controls just as easily as you use assemblies with custom components. This allows you to bundle reusable user interface output and functionality into selfcontained packages so that they can be used over and over again within the same or multiple applications.

Part 4 has more information about this technique.

Extending the HTTP Pipeline

As explained earlier, the pipeline of application events isn’t limited to requests for .aspx web forms. It also applies if you request web services or even create your own handlers to deal with custom file types.

Why would you want to create your own handler? For the most part, you won’t. However, sometimes it’s convenient to use a lower-level interface that still provides access to useful objects such as Response and Request but doesn’t use the full control-based web form model. One example is if you want to create a web resource that dynamically renders a custom graphic (a technique demonstrated in Chapter 30). In this situation, you simply need to receive a request, check the URL parameters, and then return raw image data as a JPEG or GIF file. By avoiding the full web control model, you save some overhead, because ASP.NET does not need to go through as many steps (such as creating the web-page objects, persisting view state, and so on).

ASP.NET makes scenarios like these remarkably easy through its pluggable architecture. You can “snap in” new handlers for specialized file types just by adding configuration settings. But first, you need to take a closer look at the HTTP pipeline.

C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

183

HTTP Handlers and HTTP Modules

Every request into an ASP.NET application is handled by a specialized component known as an HTTP handler. The HTTP handler is the backbone of the ASP.NET request processing framework. ASP.NET uses different HTTP handlers to serve different file types. For example, the handler for web pages creates the page and control objects, runs your code, and renders the final HTML. The handler for web services has a slightly simpler task—it simply deserializes the SOAP message and invokes the corresponding code.

All HTTP handlers are defined in the <httpHandlers> section of a configuration file. The core set of HTTP handlers is defined in the root web.config file. Here’s an excerpt of that file:

<httpHandlers>

<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/> <add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/> <add verb="*" path="*.cs" type="System.Web.HttpForbiddenHandler"/>

<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>

...

</httpHandlers>

Inside the <httpHandlers> section you can place <add> elements that register new handlers and <remove> elements to unregister existing handlers. In this example, four classes are registered. All requests for trace.axd are handed to the TraceHandler, which renders an HTML page with a list of all the recently collected trace output (as described in Chapter 3). Requests for files that end in

.config or .cs are handled by the HttpForbiddenHandler, which always generates an exception informing the user that these file types are never served. And files ending in .aspx are handled by the PageHandlerFactory. In this case, PageHandlerFactory isn’t actually an HTTP handler. Instead, it’s a factory class that will create the appropriate HTTP handler. This extra layer allows the factory to create a different handler or configure the handler differently depending on other information about the request.

ASP.NET also uses another ingredient in page processing, called HTTP modules. HTTP modules participate in the processing of a request by handling application events, much like the global.asax file. A given request can flow through multiple HTTP modules, but it always ends with a single HTTP handler. Figure 5-11 shows how the two interact.

Figure 5-11. The ASP.NET request processing architecture

184 C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

ASP.NET uses a core set of HTTP modules to enable platform features such as caching, authentication, and error pages. You can add or remove HTTP modules with <add> and <remove> tags in the <httpModules> section of a configuration file. Here’s an excerpt showing some of the HTTP modules that are defined in the machine.config file:

<httpModules>

<add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/> <add name="Session" type="System.Web.SessionState.SessionStateModule"/> <add name="WindowsAuthentication"

type="System.Web.Security.WindowsAuthenticationModule"/> <add name="FormsAuthentication"

type="System.Web.Security.FormsAuthenticationModule"/>

...

</httpModules>

One of the benefits of HTTP modules and HTTP handlers is that they provide an extensible architecture that allows you to easily plug in your own handlers and modules. In the past, developers who needed these sort of features were forced to author their own ISAPI extensions (which play the same role as HTTP handlers) or ISAPI filters (which play the same role as HTTP modules). Both of these components are dramatically more complex to create.

Note ISAPI extensions and filters plug directly into IIS. HTTP handlers and modules play the same role, but they plug into ASP.NET. For example, imagine you create and register a custom HTTP handler. When the client issues a request for that file type, it will flow first from IIS to ASP.NET (through the ASP.NET ISAPI extension). Then ASP.NET will create and execute your handler. As a result, your handlers and modules never interact with IIS.

Creating a Custom HTTP Handler

If you want to work at a lower level than the web form model to support a specialized form of processing, you can implement your own HTTP handler.

To create a custom HTTP handler, you simply need to author a class that implements the IHttpHandler interface. You can place this class in the App_Code directory, or you can compile it as part of a stand-alone DLL assembly (in other words, a separate class library project). If you use the second approach, you need to add a reference to the project (as described earlier in this chapter). This step instructs Visual Studio to copy the compiled assembly into the Bin directory.

The IHttpHandler requires your class to implement two members, which are shown in Table 5-4.

Table 5-4. IHttpHandler Members

Member

Description

ProcessRequest()

ASP.NET calls this method when a request is received. It’s where the HTTP

 

handlers perform all the processing. You can access the intrinsic ASP.NET

 

objects (such as Request, Response, and Server) through the HttpContext

 

object that’s passed to this method.

IsReusable

After ProcessRequest() finishes its work, ASP.NET checks this property to

 

determine whether a given instance of an HTTP handler can be reused. If

 

you return true, the HTTP handler object can be reused for another request

 

of the same type current. If you return false, the HTTP handler object will

 

simply be discarded.

 

 

C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

185

The following code shows one of the simplest possible HTTP handlers you can create. It simply returns a fixed block of HTML with a message.

using System; using System.Web;

namespace HttpExtensions

{

public class SimpleHandler : IHttpHandler

{

public void ProcessRequest(System.Web.HttpContext context)

{

HttpResponse response = context.Response; response.Write("<html><body><h1>Rendered by the SimpleHandler") ; response.Write("</h1></body></html>") ;

}

public bool IsReusable

{

get {return true;}

}

}

}

Note If you create this extension as a class library project, you’ll need to add a reference to the System.Web.dll assembly, which contains the bulk of the ASP.NET classes. Without this reference, you won’t be able to use types such as IHttpHandler and HttpContext. (To add the reference, right-click the project name in the Solution Explorer, choose Add Reference, and find the assembly in the list in the .NET tab.)

Configuring a Custom HTTP Handler

Once you’ve created your HTTP handler class and made it available to your web application (either by placing it in the App_Code directory or by adding a reference), you’re ready to use your extension. The next step is to alter the web.config file for the web application so that it registers your HTTP handler. Here’s an example:

<httpHandlers>

<add verb="*" path="test.simple" type="HttpExtensions.SimpleHandler,HttpExtensions" />

</httpHandlers>

When you register an HTTP handler, you specify three important details. The verb attribute indicates whether the request is an HTTP POST or HTTP GET request (use * for all requests). The path attribute indicates the file extension that will invoke the HTTP handler. In this example, the web.config section links the SimpleHandler class to the filename test.simple. Finally, the type

attribute identifies the HTTP handler class. This identification consists of two portions. First is the fully qualified class name (in this example, HttpExtensions.SimpleHandler). That portion is followed by a comma and the name of the DLL assembly that contains the class (in this example, HttpExtensions.dll). Note that the .dll extension is always assumed, and you don’t include it in the name.

186 C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

If you’re using the App_Code approach instead of a separately compiled assembly, you can omit the DLL name entirely, because ASP.NET generates it automatically.

<httpHandlers>

<add verb="*" path="test.simple" type="HttpExtensions.SimpleHandler" />

</httpHandlers>

Visual Studio doesn’t allow you to launch your HTTP handler directly. Instead, you need to run your web project and then type in a URL that includes test.simple. For example, if your web application URL is set to http://localhost:19209/Chapter05 in the local server, you need to manually change it to http://localhost:19209/Chapter05/test.simple. You’ll see the HTML shown in Figure 5-12.

Figure 5-12. Running a custom HTTP handler

Using a custom HTTP handler isn’t as convenient when you deploy your web application to an IIS web server. The problem is that the .simple extension won’t be recognized by IIS. This means that by default it’s handled by IIS on its own, not by ASP.NET. IIS simply checks for a file with that name, and if it exists, IIS returns the raw data from the file. If it doesn’t, IIS returns an error message. To change this behavior, you need to add an IIS file mapping for your application that explicitly tells IIS to send all .simple requests to ASP.NET. Chapter 18 walks you through the process.

Registering HTTP Handlers Without Configuring IIS

Instead of using a web.config or machine.config file, ASP.NET provides an alternate approach for registering HTTP handlers—you can use the recognized extension .ashx. All requests that end in

.ashx are automatically recognized as requests for a custom HTTP handler.

To create an .ashx file in Visual Studio, select Website Add New Item and choose Generic Handler (see Figure 5-13).

The .ashx file begins with a WebHandler directive. This WebHandler directive indicates the class that should be exposed through this file. Here’s an example:

<%@ WebHandler Language="C#" Class="HttpExtensions.SimpleHandler" %>

The class name can correspond to a class in the App_Code directory or a class in a reference assembly. Alternatively, you can define the class directly in the .ashx file (underneath the WebHandler directive). Either way, when a client requests the .ashx file, the corresponding HTTP handler class is executed. If you save the previous example as the file simple.ashx, whenever the client requests simple.ashx your custom web handler will be executed. Best of all, the .ashx file type is registered in IIS, so you don’t need to perform any IIS configuration when you deploy your application.

C H A P T E R 5 A S P. N E T A P P L I C AT I O N S

187

Figure 5-13. Creating an .ashx file

Whether you use a configuration file or an .ashx file is mostly a matter of preference. However,

.ashx files are usually used for simpler extensions that are designed for a single web application. Configuration files also give you a little more flexibility. For example, you can register an HTTP handler to deal with all requests that end with a given extension, whereas an .ashx file only serves a request if it has a specific filename. Also, you can register an HTTP handler for multiple applications (by registering it in the web.config file and installing the assembly in the GAC). To achieve the same effect with an .ashx file, you need to copy the .ashx file to each virtual directory.

Creating an Advanced HTTP Handler

In the previous example, the HTTP handler simply returns a block of static HTML. However, you can create much more imaginative handlers. For example, you might read data that has been posted to the page or that has been supplied in the query string and use that to customize your rendered output. Here’s a more sophisticated example that displays the source code for a requested file. It uses the file I/O support that’s found in the System.IO namespace.

using System; using System.Web; using System.IO;

namespace HttpExtensions

{

public class SourceHandler : IHttpHandler

{

public void ProcessRequest(System.Web.HttpContext context)

{

// Make the HTTP context objects easily available. HttpResponse response = context.Response; HttpRequest request = context.Request; HttpServerUtility server = context.Server;