Working with the HTTP Runtime


Creating a Custom BuildProvider
When you write an ASP.NET page and save the page to your computer’s file system, the ASP.NET page gets compiled dynamically into a .NET class in the background. The page is compiled dynamically by a BuildProvider. The ASP.NET Framework includes a number of BuildProviders. Each BuildProvider is responsible for compiling a file with a particular extension that is located in a particular type of folder. For example, there are BuildProviders for Themes, Master Pages, User Controls, and Web Services. When a BuildProvider builds, it builds a new class in the Temporary ASP.NET Files folder. Any class added to the folder becomes available to your application automatically. When you use Visual Web Developer, any public properties and methods of the class appear in Intellisense.

You can create your own BuildProviders. This can be useful in a variety of different scenarios. For example, imagine that you find yourself building a lot of ASP.NET pages that display forms. You can tediously build each ASP.NET page by hand by adding all the necessary form and validation controls. Alternatively, you can create a new BuildProvider that takes an XML file and generates the form pages for you automatically.


Creating a Simple BuildProvider
Let’s start by creating a really simple BuildProvider. The new BuildProvider will be named the SimpleBuildProvider. Whenever you create a file that has the extension .simple, the SimpleBuilderProvider builds a new class with the same name as the file in the background. The dynamically compiled class also includes a single method named DoSomething() that doesn’t actually do anything.

LISTING App_Code\CustomBuildProviders\SimpleBuildProvider.cs
using System;
using System.Web.Compilation;
using System.CodeDom;
using System.IO;
namespace AspNetUnleashed
{
public class SimpleBuildProvider : BuildProvider
{
public override void GenerateCode(AssemblyBuilder ab)
{
string fileName = Path.GetFileNameWithoutExtension(this.VirtualPath);
string snippet = “public class “ + fileName + @”
{
public static void DoSomething(){}
}”;
ab.AddCodeCompileUnit(this, new CodeSnippetCompileUnit(snippet));
}
}
}

All BuildProviders must inherit from the base BuildProvider class. Typically, you override the BuildProvider class GenerateCode() method. This method is responsible for generating the class that gets added to the Temporary ASP.NET Files folder. An instance of the AssemblyBuilder class is passed to the GenerateCode() method. You add the class that you want to create to this AssemblyBuilder by calling the AssemblyBuilder.AddCodeCompileUnit() method.

In Listing, a CodeSnippetCompileUnit is used to represent the source code for the class. Any code that you represent with the CodeSnippetCompileUnit is added, verbatim, to the dynamically generated class. This approach is problematic. Unfortunately, you can use the SimpleBuildProvider in Listing only when building a C# application. It doesn’t work with a Visual Basic .NET application. Because the code represented by the CodeSnippetCompileUnit is C# code, using the SimpleBuildProvider with a Visual Basic .NET application would result in compilation errors. The SimpleBuildProvider would inject C# code into a Visual Basic .NET assembly. The proper way to write the SimpleBuildProvider class would be to use the CodeDom. The CodeDom enables you to represent .NET code in a language neutral manner. When you represent a block of code with the CodeDom, the code can be converted to either C# or Visual Basic .NET code automatically. You learn how to use the CodeDom when we build a more complicated BuildProvider in the next section. For now, just realize that we are taking a shortcut to keep things simple.

When you add the SimpleBuildProvider to your project, it is important that you add the file to a separate subfolder in your App_Code folder and you mark the folder as a separate code folder in the web configuration file. For example, in the sample code on the CD that accompanies this book, the SimpleBuildProvider is located in the App_Code\ CustomBuildProviders folder. You must add a BuildProvider to a separate subfolder because a BuildProvider must be compiled into a different assembly than the other code in the App_Code folder. This makes sense because a BuildProvider is actually responsible for compiling the other code in the App_Code folder.

The web configuration file in Listing defines the CustomBuildProviders folder and registers the SimpleBuildProvider.

LISTING Web.Config
<configuration>
<system.web>
<compilation>
<codeSubDirectories>
<add directoryName=”CustomBuildProviders”/>
</codeSubDirectories>
<buildProviders>
<add extension=”.simple” type=”AspNetUnleashed.SimpleBuildProvider” />
</buildProviders>
</compilation>
</system.web>
</configuration>

The web configuration file in Listing associates the SimpleBuildProvider with the file extension .simple. Whenever you add a file with the .simple extension to the App_Code folder, the SimpleBuildProvider automatically compiles a new class based on the file. Build Providers execute at different times depending on the type of folder. Build Providers associated with the App_Code folder execute immediately after a new file is saved. (Oddly, the Build Provider executes twice.) Build Providers associated with the Web or App_Data folders execute when a file is requested. For example, adding the file in Listing to your App_Code folder causes the SimpleBuildProvider to create a new class named Mike.

LISTING App_Code\Mike.simple
Hello!
Hello!
Hello!

The actual content of the file that you create doesn’t matter. The SimpleBuildProvider ignores everything about the file except for the name of the file. You can see the new file created by the SimpleBuildProvider by navigating to the Sources_App_Code folder contained in the folder that corresponds to your application in the Temporary ASP.NET Files folder. The contents of the auto-generated file are contained in Listing.

LISTING mike.simple.72cecc2a.cs
#pragma checksum “C:\Chapter27\Code\CS\App_Code\Mike.simple” “{406ea660-64cf-4c82-
b6f0-42d48172a799}” “AD2E00BE337DD88E4E4B07F6B4580617”
public class Mike
{
public static void DoSomething(){}
}
Any class added to the Temporary ASP.NET Files folder is available in your application
automatically. For example, the page in Listing 27.5 uses the Mike class.
LISTING 27.5 ShowSimpleBuildProvider.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
Mike.DoSomething();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show SimpleBuildProvider</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
</div>
</form>
</body>
</html>

Creating a Data Access Component BuildProvider
In the previous section, we created a simple but useless BuildProvider. In this section, we create a complicated but useful BuildProvider. The DataBuildProvider generates a data access component automatically from an XML file. For example, if you add the XML file in Listing to your project, then the DataBuildProvider generates the class in Listing automatically.

LISTING App_Code\Movie.data
<Movies>
<add name=”Title” />
<add name=”Director” />
<add name=”BoxOfficeTotals” type=”Decimal” />
</Movies>
#pragma checksum “C:\Documents and Settings\Steve\My Documents\ASP.NET 3.5
Unleashed\Chapter27\Code\CS\App_Code\Movie.data” “{406ea660-64cf-4c82-b6f0-
42d48172a799}” “2E0F31E6B8F9D4B8687F94F0305E6D15”
//———————————————————————————————————————
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.1378
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//———————————————————————————————————————
namespace Data {
using System;
public partial class Movie {
private string _Title;
private string _Director;
private Decimal _BoxOfficeTotals;
public Movie() {
}
public virtual string Title {
get {
return this._Title;
}
set {
this._Title = value;
}
}
public virtual string Director {
get {
return this._Director;
}
set {
this._Director = value;
}
}
public virtual Decimal BoxOfficeTotals {
1410
LISTING 27.7 Continued
get {
return this._BoxOfficeTotals;
}
set {
this._BoxOfficeTotals = value;
}
}
/// <summary>Returns List of Movie</summary>
public static System.Collections.Generic.List<Movie>
Select(System.Data.SqlClient.SqlConnection con) {
System.Collections.Generic.List<Movie> results = new
System.Collections.Generic.List<Movie>();
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.
SqlCommand();
cmd.Connection = con;
string cmdText = “SELECT Title,Director,BoxOfficeTotals FROM Movies”;
cmd.CommandText = cmdText;
System.Data.SqlClient.SqlDataReader reader = cmd.ExecuteReader();
int counter;
for (counter = 0; reader.Read(); counter = (counter + 1)) {
Movie record = new Movie();
record.Title = ((string)(reader[“Title”]));
record.Director = ((string)(reader[“Director”]));
record.BoxOfficeTotals = ((Decimal)(reader[“BoxOfficeTotals”]));
results.Add(record);
}
return results;
}
/// <summary>Returns List of Movie</summary>
public static System.Collections.Generic.List<Movie> Select(string
connectionStringName) {
System.Collections.Generic.List<Movie> results = new
System.Collections.Generic.List<Movie>();
System.Configuration.ConnectionStringSettings conStringSettings =
System.Web.Configuration.WebConfigurationManager.ConnectionStrings
[connectionStringName];
string conString = conStringSettings.ConnectionString;
System.Data.SqlClient.SqlConnection con = new
System.Data.SqlClient.SqlConnection();
con.ConnectionString = conString;
try {
con.Open();
results = Movie.Select(con);
}
finally {
con.Close();
}
return results;
}
}
}

LISTING ShowDataBuildProvider.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
grdMovies.DataSource = Data.Movie.Select(“Movies”);
grdMovies.DataBind();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView
id=”grdMovies”
Runat=”server” />
</div>
</form>
</body>
</html>

Unlike the SimpleBuildProvider created in the previous section, the DataBuildProvider uses the CodeDom to represent code. This means that you can use the DataBuildProvider in both Visual Basic .NET and C# applications. The DataBuildProvider generates the data access component in different languages automatically. For example, if you use the DataBuildProvider in a C# application, the BuildProvider generates the code in Listing in C#.

LISTING 27.9 DataBuildProvider.cs (Partial)
using System;
using System.Collections.Generic;
using System.Web.Compilation;
using System.CodeDom;
using System.Xml;
using System.IO;
using System.Web.Hosting;
namespace AspNetUnleashed
{
public class DataBuildProvider : BuildProvider
{
string _className;
public override void GenerateCode(AssemblyBuilder ab)
{
// Load the XML file
XmlDocument xmlData = new XmlDocument();
xmlData.Load(HostingEnvironment.MapPath(this.VirtualPath));
// Generate code from XML document
CodeCompileUnit dataCode = GetDataCode(xmlData);
// Add the code
ab.AddCodeCompileUnit(this, dataCode);
}
private CodeCompileUnit GetDataCode(XmlDocument xmlData)
{
// Add class
_className = Path.GetFileNameWithoutExtension(this.VirtualPath);
CodeTypeDeclaration dataType = new CodeTypeDeclaration(_className);
dataType.IsPartial = true;
// Add constructor
AddConstructor(dataType);
// Add properties
AddProperties(dataType, xmlData);
// Add Select method
AddSelect(dataType, xmlData);
// Add Select with conString overload
1414
LISTING 27.9 Continued
AddSelectConString(dataType, xmlData);
// Create namespace
CodeNamespace dataNS = new CodeNamespace(“Data”);
// Add class to namespace
dataNS.Types.Add(dataType);
// Create code unit
CodeCompileUnit dataCode = new CodeCompileUnit();
// Add namespace to code unit
dataCode.Namespaces.Add(dataNS);
// Add default namespaces
dataNS.Imports.Add(new CodeNamespaceImport(“System”));
return dataCode;
}
}
}


Creating a Custom ExpressionBuilder
An ExpressionBuilder class generates one expression from another expression. Typically, you use an ExpressionBuilder to look up a particular value given a particular key. The ASP.NET Framework includes the following ExpressionBuilder classes:

 AppSettingsExpressionBuilder—Retrieves values from the appSettings section of the web configuration file.
ConnectionStringsExpressionBuilder—Retrieves values from the connectionStrings section of the web configuration file.
ResourceExpressionBuilder—Retrieves values from resource files.

The ConnectionStringsExpressionBuilder has been used throughout this book whenever a connection string has needed to be retrieved. You use the following syntax when working with an ExpressionBuilder:
<%$ ConnectionStrings:MyDatabase %>

The <%$ and %> tags are used to mark an expression that should be parsed by an ExpressionBuilder. The prefix ConnectionStrings is mapped to the particular ExpressionBuilder class that is responsible for parsing the expression. ExpressionBuilders must always be used with control properties. For example, you cannot display a connection string in a page like this:
<%$ ConnectionStrings:MyDatabase %>

Instead, you must display the connection string like this:

<asp:Literal Id=”ltlConnectionString” Text=’<%$ ConnectionStrings:MyDatabase %>’ Runat=”server” />

You can create a custom ExpressionBuilder when none of the existing ExpressionBuilder classes do what you need. For example, you might want to store your application settings in a custom section of the web configuration file. In that case, you might want to create a custom ExpressionBuilder that grabs values from the custom configuration section.

LISTING App_Code\LookupExpressionBuilder.cs
using System;
using System.CodeDom;
using System.Web.UI;
using System.ComponentModel;
using System.Web.Compilation;
using System.Xml;
using System.Web.Hosting;
using System.Web.Caching;
namespace AspNetUnleashed
{
public class LookupExpressionBuilder : ExpressionBuilder
{
public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
object parsedData, ExpressionBuilderContext context)
{
CodeTypeReferenceExpression refMe = new
CodeTypeReferenceExpression(base.GetType());
CodePrimitiveExpression expression = new
CodePrimitiveExpression(entry.Expression);
return new CodeMethodInvokeExpression(refMe, “GetEvalData”, new
CodeExpression[] { expression });
}
public override object EvaluateExpression(object target, BoundPropertyEntry
entry, object parsedData, ExpressionBuilderContext context)
{
return GetEvalData(entry.Expression);
}
public override bool SupportsEvaluate
{
get
{
return true;
CHAPTER 27 Working with the HTTP Runtime
1417
27
Creating a Custom ExpressionBuilder
}
}
public static string GetEvalData(string expression)
{
XmlDocument lookupDoc =
(XmlDocument)HostingEnvironment.Cache[“Lookup”];
if (lookupDoc == null)
{
lookupDoc = new XmlDocument();
string lookupFileName = HostingEnvironment.MapPath
(“~/Lookup.config”);
lookupDoc.Load(lookupFileName);
CacheDependency fileDepend = new CacheDependency(lookupFileName);
HostingEnvironment.Cache.Insert(“Lookup”, lookupDoc, fileDepend);
}
string search = String.Format(“//add[@key=’{0}’]”, expression);
XmlNode match = lookupDoc.SelectSingleNode(search);
if (match != null)
return match.Attributes[“value”].Value;
return “[no match]”;
}
}
}

Before you can use the LookupExpressionBuilder class, you need to register it in the web configuration file. The web configuration file in Listing includes an <expressionBuilders> section that registers the LookupExpressionBuilder class for the prefix lookup.

LISTING Web.Config
<configuration>
<system.web>
<compilation>
<expressionBuilders>
<add expressionPrefix=”lookup” type=”AspNetUnleashed.LookupExpressionBuilder” />
</expressionBuilders>
</compilation>
</system.web>
</configuration>

The LookupExpressionBuilder uses an XML file named Lookup.config to contain a database of lookup values. This file contains key and value pairs. A sample Lookup.config file is contained in Listing 27.12.

LISTING Lookup.config
<lookup>
<add key=”WelcomeMessage” value=”Welcome to our Web site!” />
<add key=”Copyright” value=”All content copyrighted by the company.” />
</lookup>

Finally, the page in Listing.13 uses the LookupExpressionBuilder. It contains a Literal control that displays the value of a lookup expression named WelcomeMessage.

LISTING ShowLookupExpressionBuilder.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show LookupExpressionBuilder</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Literal ID=”Literal1” Text=”<%$ lookup:WelcomeMessage %>” runat=”Server” />
</div>
</form>
</body>
</html>

You create a custom ExpressionBuilder by inheriting a new class from the base ExpressionBuilder class.  The ExpressionBuilder class has the following methods:
GetCodeExpression—Returns the code that is used to evaluate an expression.
EvaluateExpression—Evaluates the expression in the case of no-compile ASP.NET pages.
ParseExpression—Returns a parsed version of the expression.

The ExpressionBuilder class also supports the following property:
SupportsEvaluate—When true, the ExpressionBuilder can be used in no-compile ASP.NET pages.

Creating HTTP Handlers
An HTTP Handler is a .NET class that executes whenever you make a request for a file at a certain path. Each type of resource that you can request from an ASP.NET application has a corresponding handler. For example, when you request an ASP.NET page, the Page class executes. The Page class is actually an HTTP Handler because it implements the IHttpHandler interface. Other examples of HTTP Handlers are the TraceHandler class, which displays applicationlevel trace information when you request the Trace.axd page and the ForbiddenHandler class, which displays an Access Forbidden message when you attempt to request source code files from the browser.

You can implement your own HTTP handlers. For example, imagine that you want to store all your images in a database table. However, you want use normal HTML <img> tags to display images in your web pages. In that case, you can map any file that has a .gif or .jpeg extension to a custom image HTTP handler. The image HTTP handler can retrieve images from a database automatically whenever an image request is made. Or imagine that you want to expose an RSS feed from your website. In that case, you can create a RSS HTTP Handler that displays a list of blog entries or articles hosted on your website.

You can create an HTTP Handler in two ways. You can either create something called a  Generic Handler, or you can implement the IHttpHandler interface in a custom class. This section explores both methods of creating an HTTP Handler.

Creating a Generic Handler
The easiest way to create a new HTTP Handler is to create a Generic Handler. When you create a Generic Handler, you create a file that ends with the extension .ashx. Whenever you request the .ashx file, the Generic Handler executes. You can think of a Generic Handler as a very lightweight ASP.NET page. A Generic Handler is like an ASP.NET page that contains a single method that renders content to the browser. You can’t add any controls declaratively to a Generic Handler. A Generic Handler also doesn’t support events such as the Page Load or Page PreRender events.

LISTING ImageTextHandler.ashx
<%@ WebHandler Language=”C#” Class=”ImageTextHandler” %>
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
public class ImageTextHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Get parameters from querystring
string text = context.Request.QueryString[“text”];
string font = context.Request.QueryString[“font”];
string size = context.Request.QueryString[“size”];
// Create Font
Font fntText = new Font(font, float.Parse(size));
// Calculate image width and height
Bitmap bmp = new Bitmap(10, 10);
Graphics g = Graphics.FromImage(bmp);
SizeF bmpSize = g.MeasureString(text, fntText);
int width = (int)Math.Ceiling(bmpSize.Width);
int height = (int)Math.Ceiling(bmpSize.Height);
bmp = new Bitmap(bmp, width, height);
g.Dispose();
// Draw the text
g = Graphics.FromImage(bmp);
g.Clear(Color.White);
g.DrawString(text, fntText, Brushes.Black, new PointF(0, 0));
g.Dispose();
1422 CHAPTER 27 Working with the HTTP Runtime
LISTING 27.14 Continued
// Save bitmap to output stream
bmp.Save(context.Response.OutputStream, ImageFormat.Gif);
}
public bool IsReusable
{
get
{
return true;
}
}
}

The ImageTextHandler in Listing includes one method and one property. The ProcessRequest() method is responsible for outputting any content that the handler renders to the browser. In Listing, the image text, font, and size are retrieved from query string fields. You specify the image that you want to return from the handler by making a request that looks like this:
/ImageTextHandler.ashx?text=Hello&font=Arial&size=30

Next, a bitmap is created with the help of the classes from the System.Drawing namespace. The bitmap is actually created twice. The first one is used to measure the size of the bitmap required for generating an image that contains the text. Next, a new bitmap of the correct size is created, and the text is drawn on the bitmap. After the bitmap has been created, it is saved to the HttpResponse object’s OutputStream so that it can be rendered to the browser.  The handler in Listing also includes an IsReusable property. The IsReusable property indicates whether the same handler can be reused over multiple requests. You can improve your application’s performance by returning the value True. Because the handler isn’t maintaining any state information, there is nothing wrong with releasing it back into the pool so that it can be used with a future request. The page in Listing illustrates how you can use the ImageTextHandler.ashx file.

This page contains three HTML <img> tags that pass different query strings to the handler

LISTING ShowImageTextHandler.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show ImageTextHandler</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<img src=”ImageTextHandler.ashx?text=Some Text&font=WebDings&size=42” />
<br />
<img src=”ImageTextHandler.ashx?text=Some Text&font=Comic Sans MS&size=42” />
<br />
<img src=”ImageTextHandler.ashx?text=Some Text&font=Courier New&size=42” />
</div>
</form>
</body>
</html>

Implementing the IHttpHandler Interface
The big disadvantage of a Generic Handler is that you cannot map a Generic Handler to a particular page path. For example, you cannot execute a Generic Handler whenever someone requests a file with the extension .gif. If you need more control over when an HTTP Handler executes, then you can create a class that implements the IHttpHandler interface. After you create a class that For example, the class in Listing represents an Image HTTP Handler. This handler retrieves an image from a database table and renders the image to the browser.

LISTING App_Code\ImageHandler.cs
using System;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
namespace AspNetUnleashed
{
public class ImageHandler : IHttpHandler
{
const string connectionStringName = “Images”;
public void ProcessRequest(HttpContext context)
{
// Don’t buffer response
context.Response.Buffer = false;
// Get file name
string fileName = VirtualPathUtility.GetFileName(context.Request.Path);
// Get image from database
string conString = WebConfigurationManager.ConnectionStrings
[connectionStringName].ConnectionString;
SqlConnection con = new SqlConnection(conString);
SqlCommand cmd = new SqlCommand(“SELECT Image FROM Images WHERE
FileName=@FileName”, con);
cmd.Parameters.AddWithValue(“@fileName”, fileName);
using (con)
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.
SequentialAccess);
if (reader.Read())
{
int bufferSize = 8040;
byte[] chunk = new byte[bufferSize];
long retCount;
long startIndex = 0;
retCount = reader.GetBytes(0, startIndex, chunk, 0, bufferSize);
while (retCount == bufferSize)
{
context.Response.BinaryWrite(chunk);
startIndex += bufferSize;
retCount = reader.GetBytes(0, startIndex, chunk, 0,
bufferSize);
}
byte[] actualChunk = new Byte[retCount - 1];
Buffer.BlockCopy(chunk, 0, actualChunk, 0, (int)retCount - 1);
context.Response.BinaryWrite(actualChunk);
}
}
}
public bool IsReusable
{
get { return true; }
}
}
}

implements the IHttpHandler interface, you need to register the class in the web configuration file. The web configuration file in Listing includes an httpHandlers section that associates the .gif, .jpeg, and .jpg extensions with the Image handler.

LISTING Web.Config
<configuration>
<connectionStrings>
<add name=”Images” connectionString=”Data Source=.\SQLExpress;Integrated
Security=True;AttachDBFileName=|DataDirectory|ImagesDB.mdf; User Instance=True”/>
</connectionStrings>
<system.web>
<httpHandlers>
<add path=”*.gif” verb=”*”
type=”AspNetUnleashed.ImageHandler” validate=”false” />
<add path=”*.jpeg” verb=”*”
type=”AspNetUnleashed.ImageHandler” validate=”false” />
<add path=”*.jpg” verb=”*”
1426 CHAPTER 27 Working with the HTTP Runtime
LISTING 27.17 Continued
type=”AspNetUnleashed.ImageHandler” validate=”false” />
</httpHandlers>
</system.web>
</configuration>

When you register a handler, you specify the following four attributes:
path—Enables you to specify the path associated with the handler. You can use wildcards in the path expression.
verb—Enables you to specify the HTTP verbs, such as GET or POST, associated with the handler. You can specify multiple verbs in a comma-separated list. You can represent any verb with the * wildcard.
type—Enables you to specify the name of the class that implements the handler.
validate—Enables you to specify whether the handler is loaded during application startup. When true, the handler is loaded at startup. When false, the handler is not loaded until a request associated with the handler is made. This second option can improve your application’s performance when a handler is never used.

The page in Listinguses the ImageHandler to render its images. The page enables you to upload new images to a database named ImagesDB. The page also displays existing images

<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
protected void btnAdd_Click(object sender, EventArgs e)
{
if (upFile.HasFile)
{
srcImages.Insert();
}
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<style type=”text/css”>
.fileList li
{
margin-bottom:5px;
}
</style>
<title>Image Upload</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Label id=”lblFile” Text=”Image File:” AssociatedControlID=”upFile” Runat=”server” />
<asp:FileUpload id=”upFile” Runat=”server” />
<asp:Button id=”btnAdd” Text=”Add Image” OnClick=”btnAdd_Click” Runat=”server” />
<hr />
<asp:GridView id=”grdImages” DataSourceID=”srcImages” AutoGenerateColumns=”false”
ShowHeader=”false” GridLines=”None” Runat=”server”>
<Columns>
<asp:ImageField DataImageUrlField=”FileName” DataAlternateTextField=”FileName” />
</Columns>
</asp:GridView>
<asp:SqlDataSource id=”srcImages” ConnectionString=”<%$ ConnectionStrings:Images %>”
SelectCommand=”SELECT FileName FROM Images” InsertCommand=”INSERT Images (FileName,Image) VALUES (@FileName,@FileBytes)” Runat=”server”>
<InsertParameters>
<asp:ControlParameter Name=”FileName” ControlID=”upFile” PropertyName=”FileName” />
<asp:ControlParameter Name=”FileBytes” ControlID=”upFile” PropertyName=”FileBytes” />
</InsertParameters>
</asp:SqlDataSource>
</div>
</form>
</body>
</html>

Registering Extensions with Internet Information Server
The web server included with Visual Web Developer maps all requests to the ASP.NET Framework. For example, if you create an HTTP Handler that handles requests for .gif files, then you don’t have to do anything special when using the handler with the Visual Web Developer web server. Internet Information Server, on the other hand, does not map all requests to the ASP.NET Framework. In particular, it does not map requests for .gif files to ASP.NET. If you want to use a special extension for a handler, then you must configure Internet Information Server to map that extension to the ASP.NET Framework. If you are serving your pages with Internet Information Server 6.0 (included with Windows Server 2003), then you can create something called a wildcard application mapping. A wildcard application mapping enables you to map all page requests to an application such as the ASP.NET Framework. Follow these steps to configure a wildcard mapping for ASP.NET:

1. Open Internet Information Services by selecting Start, Control Panel, Administrative Tools, Internet Information Services.
2. Open the property sheet for a particular website or virtual directory.
3. Open the Application Configuration dialog box by selecting the Directory tab and clicking the Configuration button.
4. Select the Mappings tab.
5. Click the Insert button at the bottom of the Mappings tab to open the Add/Edit Application Extension Mapping dialog box .
6. In the Executable field, enter the path to the ASP.NET ISAPI DLL. (You can copy and paste this path from the Application Mapping for the .aspx extension.)

After you complete these steps, all requests made for any type of file are handled by the ASP.NET Framework. If you make a request for a .gif image, then any handlers that you have registered in the web configuration file for the .gif extension will execute. Earlier versions of Internet Information Server, such as the version included with Microsoft Windows XP, do not support wildcard application mappings. You must map each file extension that you want to associate with the ASP.NET Framework one by one.

Follow these steps to map the .gif extension to the ASP.NET Framework:
1. Open Internet Information Services by selecting Start, Control Panel, Administrative Tools, Internet Information Services.
2. Open the property sheet for a particular website or virtual directory.
3. Open the Application Configuration dialog box by selecting the Directory tab and clicking the Configuration button.
4. Select the Mappings tab.
5. Click the Add button to open the Add/Edit Application Extension Mapping dialog box.
6. In the Executable field, enter the path to the ASP.NET ISAPI DLL. (You can copy and paste this path from the Application Mapping for the .aspx extension.)
7. In the Extension field, enter .gif.

After you complete these steps, requests for .gif images are handled by the ASP.NET Framework. If you have registered an HTTP handler for the .gif extension in the web configuration file, then the HTTP Handler will execute whenever someone makes a request for a .gif file.

Creating an Asynchronous HTTP Handler
When you create an HTTP Handler by creating either a Generic Handler or implementing the IHttpHandler interface, you are creating a synchronous handler. In this section, you learn how to create an asynchronous handler. The advantage of creating an asynchronous handler is scalability. The ASP.NET Framework maintains a limited pool of threads that are used to service requests. When the ASP.NET Framework receives a request for a file, it assigns a thread to handle the request. If the ASP.NET Framework runs out of threads, the request is queued until a thread becomes available. If too many threads are queued, then the framework rejects the page request with a 503 Server Too Busy response code. If you execute an HTTP Handler asynchronously, then the current thread is released back into the thread pool so that it can be used to service another page request. While the asynchronous handler is executing, the ASP.NET framework can devote its attention to handling other requests. When the asynchronous handler completes its work, the framework reassigns a thread to the original request and the handler can render content to the browser.

You can configure the ASP.NET thread pool with the httpRuntime element in the web configuration file. You can modify the appRequestQueueLimit, minFreeThreads, and minLocalRequestFreeThreads attributes to control how many requests the ASP.NET Framework queues before giving up and sending an error. You create an asynchronous HTTP handler by implementing the IHttpAsyncHandler interface. This interface derives from the IHttpHandler interface and adds two additional methods:
BeginProcessRequest—Called to start the asynchronous task.
EndProcessRequest—Called when the asynchronous task completes.

Working with HTTP Applications and HTTP Modules
Whenever you request an ASP.NET page, the ASP.NET Framework assigns an instance of the HttpApplication class to the request. This class performs the following actions in the following order:
1. Raises the BeginRequest event.
2. Raises the AuthenticateRequest event.
3. Raises the AuthorizeRequest event.
4. Calls the ProcessRequest() method of the Page class.
5. Raises the EndRequest event.

This is not a complete list of HttpApplication events. There are a lot of them! The entire page execution lifecycle happens during the fourth step. For example, the Page Init, Load, and PreRender events all happen when the Page class ProcessRequest() method is called. The HttpApplication object is responsible for raising application events. These application events happen both before and after a page is executed.

If you want to handle HttpApplication events, there are two ways to do it. You can create a Global.asax file, or you can create one or more custom HTTP Modules.

Creating a Global.asax File
By default, the ASP.NET Framework maintains a pool of HttpApplication objects to service incoming page requests. A separate HttpApplication instance is assigned to each request. If you prefer, you can create a custom HttpApplication class. That way, an instance of your custom class is assigned to each page request. You can create custom properties in your derived class. These properties can be accessed
from any page, control, or component. You also can handle any application events in your custom HttpApplication class.

Listing Global.asax
<%@ Application Language=”C#” %>
<%@ Import Namespace=”System.Data” %>
<%@ Import Namespace=”System.Data.SqlClient” %>
<%@ Import Namespace=”System.Web.Configuration” %>
<script runat=”server”>
private string _conString;
private SqlConnection _con;
private SqlCommand _cmdSelect;
private SqlCommand _cmdInsert;
public override void Init()
{
// initialize connection
_conString = WebConfigurationManager.ConnectionStrings[“Log”].
ConnectionString;
_con = new SqlConnection(_conString);
// initialize select command
_cmdSelect = new SqlCommand(“SELECT COUNT(*) FROM Log WHERE Path=@Path”,
_con);
_cmdSelect.Parameters.Add(“@Path”, SqlDbType.NVarChar, 500);
// initialize insert command
_cmdInsert = new SqlCommand(“INSERT Log (Path) VALUES (@Path)”, _con);
_cmdInsert.Parameters.Add(“@Path”, SqlDbType.NVarChar, 500);
}
public int NumberOfRequests
{
get
{
int result = 0;
_cmdSelect.Parameters[“@Path”].Value = Request.
AppRelativeCurrentExecutionFilePath;
try
{
_con.Open();
result = (int)_cmdSelect.ExecuteScalar();
}
finally
{
_con.Close();
}
return result;
}
}
void Application_BeginRequest(object sender, EventArgs e)
{
// Record new request
_cmdInsert.Parameters[“@Path”].Value = Request.
AppRelativeCurrentExecutionFilePath;
try
{
_con.Open();
_cmdInsert.ExecuteNonQuery();
}
finally
{
_con.Close();
}
}
</script>

Creating Custom HTTP Modules
An HTTP Module is a .NET class that executes with each and every page request. You can use an HTTP Module to handle any of the HttpApplication events that you can handle in the Global.asax file. Behind the scenes, the ASP.NET Framework uses HTTP Modules to implement many of the standard features of the framework. For example, the ASP.NET Framework uses the FormsAuthenticationModule to implement Forms authentication and the WindowsAuthenticationModule to implement Windows authentication. Session state is implemented with an HTTP Module named the SessionStateModule. Page output caching is implemented with an HTTP Module named the OutputCacheModule, and the Profile object is implemented with an HTTP Module named the ProfileModule. When a new instance of an HttpApplication class is created, the HttpApplication loads all of the HTTP Modules configured in the web configuration file. Each HTTP Module subscribes to one or more HttpApplication events. For example, when the HttpApplication object raises its AuthenticateRequest event, the FormsAuthenticationModule executes its code to authenticate the current user.

1 comment:

  1. Slotyro Casino, Las Vegas - Mapyro
    Slotyro Casino is 김포 출장안마 an entertainment venue 논산 출장마사지 in Las Vegas, Nevada. See map 광명 출장안마 and 서울특별 출장샵 read reviews of rooms, restaurants, and other fun things to do in Las Vegas  Rating: 4 · 의정부 출장샵 ‎13 votes · ‎Price range: $$

    ReplyDelete