In the all-encompassing container represented by the HttpContext object, a few popular objects also find their place. Among them are Server, Request, and Response.
They are old acquaintances for ASP developers and, indeed, they are
feature-rich elements of the ASP.NET programming toolkit. The set of
properties and methods still makes these objects a fundamental resource
for developers. Let’s learn more about them, starting with the Server object.
The functionality of the ASP intrinsic Server object in ASP.NET is implemented by the HttpServerUtility
class. An instance of the type is created when ASP.NET begins to
process the request and is then stored as part of the request context.
The bunch of helper methods that HttpServerUtility provides are publicly exposed to modules and handlers—including global.asax, pages, and Web services—through the Server property of the HttpContext
object. In addition, to maintain ASP.NET coding as close as possible to
the ASP programming style, several other commonly used ASP.NET objects
also expose their own Server property. In this way, developers can use in the code, say, Server.MapPath without incurring compile errors.
Properties of the HttpServerUtility Class
This class provides two properties, named MachineName and ScriptTimeout. The MachineName property returns the machine name, whereas ScriptTimeout
gets and sets the time in seconds that a request is allowed to be
processed. This property accepts integers and defaults to 90 seconds;
however, it is set to a virtually infinite value if the page runs with
the attribute debug=true, as shown here:
this.Server.ScriptTimeout = 30000000;
The ScriptTimeout property is explicitly and automatically set in the constructor of the dynamically created class that represents the page.
Methods of the HttpServerUtility Class
Table 1 lists all methods exposed by the HttpServerUtility
class. As you can see, they constitute a group of helper methods that
come in handy at various stages of page execution. The class provides a
couple of methods to create instances of COM components and a few others
to deal with errors. Another group of methods relates to the decoding
and encoding of content and URLs.
Table 1. Methods of the Server Object
Method | Description |
---|
ClearError | Clears the last exception that was thrown for the request. |
CreateObject | Creates an instance of the specified COM object. |
CreateObjectFromClsid | Creates an instance of the COM object identified by the specified CLSID. The class identifier is expressed as a string. |
Execute | Passes
control to the specified page for execution. The child page executes
like a subroutine. The output can be retained in a writer object or
automatically flushed in the parent response buffer. |
GetLastError | Returns the last exception that was thrown. |
HtmlDecode | Decodes a string that has been encoded to eliminate invalid HTML characters. For example, it translates < into <. |
HtmlEncode | Encodes a string to be displayed in a browser. For example, it encodes < into <. |
MapPath | Returns the physical path that corresponds to the specified virtual path on the Web server. |
Transfer | Works
as a kind of server-side redirect. It terminates the execution of the
current page and passes control to the specified page. Unlike Execute, control is not passed back to the caller page. |
UrlDecode | Decodes
a string encoded for HTTP transmission to the server in a URL. The
decoded string can be returned as a string or output to a writer. |
UrlEncode | Encodes
a string for HTTP transmission to a client in a URL. The encoded string
can be returned as a string or output to a writer. |
UrlPathEncode | Encodes
only the path portion of a URL string, and returns the encoded string.
This method leaves the query string content intact. |
HTML and URL encoding
are ways of encoding characters to ensure that the transmitted text is
not misunderstood by the receiving browser. HTML encoding, in
particular, replaces <, >, &, and quotes with equivalent HTML
entities such as <, >, &, and ". It also encodes blanks, punctuation characters, and in general, all characters not allowed in an HTML
stream. On the other hand, URL encoding is aimed at fixing the text
transmitted in URL strings. In URL encoding, the same critical
characters are replaced with different character entities than in HTML
encoding.
Starting with ASP.NET 2.0, two new static methods have been added to encode and decode a token. The UrlTokenEncode method accepts a byte array containing Base64 data and converts it into a URL-encoded token. UrlTokenDecode does the reverse.
Embedding Another Page’s Results
The Execute method allows you to consider an external page as a subroutine. When the execution flow reaches the Server.Execute
call, control is passed to the specified page. The execution of the
current page is suspended, and the external page is spawned. The
response text generated by the child execution is captured and processed
according to the particular overload of Execute that has been used. Table 2 lists the overloads of the Execute method.
Table 2. Overloads of the Execute Method
Overload | Description |
---|
Execute(string); | You pass the URL of the page, and the response text is automatically embedded in the main page. |
Execute(string, TextWriter); | The response text is accumulated in the specified text writer. |
Execute(string, bool); | The same description as for previous item, except that you can choose whether to preserve the QueryString and Form collections. True is the default setting. |
Execute(IHttpHandler, TextWriter, bool); | You indicate the HTTP handler to transfer the current request to. The response is captured by the text writer. |
Execute(string, TextWriter, bool); | The response text is captured by the specified text writer, and the QueryString and Form collections are either preserved or not preserved, as specified. |
Note that ASP.NET 1.x supports only the first two overloads listed in Table 2.
It is important to note that if a TextWriter
object is specified, the response text of the child execution is
accumulated into the writer object so that the main page output can be
used later at will. Figure 1
shows this in action—the main page generates the bold-faced text, while
the child page’s output is shown in normal font sandwiched between the
main page output.
The source code for the main page in Figure 1 is as follows:
void Page_Load(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
builder.Append("<b>Response generated before
Execute is called</b><hr/>");
StringWriter writer = new StringWriter();
Server.Execute("child.aspx", writer);
builder.Append(writer.ToString());
builder.Append("<hr/><b>Response generated after
the call to Execute.</b>");
Label1.Text = builder.ToString();
}
It’s interesting to look at the internal implementation of the Execute method. Both the main and child pages are run by the same HttpApplication object as if they were the same request. What happens within the folds of Execute
is a sort of context switch. First, the method obtains an HTTP handler
from the application factory to serve the new request. The original
handler of the main request is cached and replaced with the new handler.
The spawned page inherits the context of the parent; when finished, any
modification made to Session or Application is immediately visible to the main page.
The handler switching
makes the whole operation extremely fast, as there’s no need to create a
new object to serve the request. When the child page returns, the
original handler is restored. The execution of the main page continues
from the point at which it was stopped, but it uses the context
inherited from the child page.
Caution
ASP.NET directly calls the handler indicated by the Execute
method without reapplying any authentication and authorization logic.
If your security policy requires clients to have proper authorization to
access the resource, the application should force reauthorization. You
can force reauthorization by using the Response.Redirect method instead of Execute. When Redirect
is called, the browser places a new request in the system, which will
be authenticated and authorized as usual by IIS and ASP.NET. As an
alternative, you can verify whether the user has permission to call the
page by defining roles and checking the user’s role before the
application calls the Execute method. |
Server-Side Redirection
The Transfer method differs from the Execute
method in that it terminates the current page after executing the
specified page. The new page runs as if it was the originally requested
one. The Transfer method has the following overloads:
public void Transfer(string);
public void Transfer(string, bool);
public void Transfer(IHttpHandler, bool);
The string parameter indicates the destination URL. The Boolean parameter indicates what to do with regard to the QueryString and Form collections. If true,
the collections are preserved; otherwise, they are cleared and made
unavailable to the destination page (which is the recommended approach).
Starting with ASP.NET 2.0, you can also directly indicate the HTTP
handler to invoke, with the same security issues that were mentioned for
Execute.
All the code that might be following the call to Transfer in the main page is never executed. In the end, Transfer
is just a page redirect method. However, it is particularly efficient
for two reasons. First, no roundtrip to the client is requested, as is
the case, for example, with Response.Redirect. Second, the same HttpApplication that was serving the caller request is reused, thus limiting the impact on the ASP.NET infrastructure.
Late-Bound COM Objects
The HttpServerUtility class provides you with the ability to create late-bound COM objects in much the same way you do in ASP. The methods are CreateObject and CreateObjectFromClsid.
Objects can be created either from the string representation of the
class CLSID or from the progID. The following code creates an instance
of a COM component using the CLSID:
' Only in VB (and in non-strict mode) can you call methods
' on an Object variable beyond the members of the Object class.
' The code here will work written in C#, but it will hardly be usable
Dim strClsid As String = "42754580-16b7-11ce-80eb-00aa003d7352"
Dim comObj As Object = Server.CreateObject(strClsid)
When assigned to a variable declared of type Object, an object is said to be late bound—as
opposed to early-bound, strongly typed objects. Late-bound objects can
hold references to any object, but they lack many advantages of
early-bound objects. Early-bound objects should be used whenever
possible because they are significantly faster than late-bound objects
and provide strong type checking, thus making your code easier to
develop, read, and maintain.
Primarily for
backward-compatibility reasons, you might sometimes create late-bound
instances of COM objects. Using COM objects in ASP.NET applications is a
common necessity in real-world projects. The best way to import COM
functionality in .NET applications entails the use of managed
wrappers—special classes that expose the type library of the COM class
as a .NET class. Managed wrappers are usually created by Visual Studio
.NET when you reference a COM object in your project.
Note
A
command-line utility is also available should you need to generate the
class assembly using a particular namespace, language, or file name that
is different from those automatically set by Visual Studio .NET. The
utility is the Type Library Importer (tlbimp.exe), and it is located in the installation path of Visual Studio .NET. |
Although it’s not an especially effective approach, the Server.CreateObject
method can be used to create a late-bound instance of a COM component.
The ideal language for late binding is Visual Basic .NET; however, bear
in mind that late binding is supported only if the Strict option is Off (the default).
The following code shows how to fill an ADO Recordset object using the ASP programming style:
<%@ Page Language="VB" AspCompat="true" %>
<script runat="server">
Sub Page_Load(sender as object, e as EventArgs)
Dim rs As Object = Server.CreateObject("ADODB.Recordset")
rs.Open("SELECT firstname, lastname FROM employees", _
"PROVIDER=sqloledb;DATABASE=northwind;SERVER=(local);" + _
"UID=...;PWD=...;")
Dim sb As StringBuilder = New StringBuilder("")
While Not rs.EOF
sb.Append(rs.Fields("lastname").Value.ToString())
sb.Append(", ")
sb.Append(rs.Fields("firstname").Value.ToString())
sb.Append("<br/>")
rs.MoveNext
End While
Response.Write(sb.ToString())
End Sub
</script>
Note the use of the AspCompat attribute in the @Page directive. Apartment-threaded COM components can be created only in ASP.NET pages that have the AspCompat attribute set to true. Before an attempt to create the object is made, the CreateObject method checks the threading model of the component. If the page is already working in ASP compatibility mode—that is, the AspCompat attribute is true—the object is created, regardless of the threading model of the component. If the AspCompat attribute is set to false (the default), CreateObject reads the threading model of the COM component from the registry. If the threading model is apartment or no threading model is specified, an exception is thrown; otherwise, the object is successfully created.
Note also that the use of the AspCompat
attribute is not strictly necessary with the ADO library because the
ADO library supports both the apartment and free-threading models.
Note
COM components developed using Visual Basic 6.0 need the AspCompat
attribute to be used in ASP.NET pages because they typically use the
single-threaded apartment (STA) model. This situation is detected, and
an exception is thrown. Note, though, that if your code instantiates the COM object through a managed wrapper (instead of creating the instance using CreateObject),
the runtime will no longer be able to detect the apartment nature of
the component and does not throw an exception. A managed wrapper saves
you from a run-time exception but not from the need of setting AspCompat to true. |
The Importance of AspCompat
Running STA
components in a multithreaded apartment (MTA) environment such as
ASP.NET is strongly discouraged for performance reasons. The AspCompat attribute is designed specifically to avoid this critical situation. Let’s see how and why.
To process HTTP
requests, normally ASP.NET uses a pool of threads from an MTA. Objects
in an MTA execute on any thread and allow any number of methods to occur
simultaneously. Single-threaded apartment COM components (that is, all
VB6 COM components) execute on the particular thread in which they were
created and allow only one method to execute at a time. Until special
countermeasures are taken, when you run an STA component in an MTA
environment continued thread switching is likely to happen. More
importantly, a thread switch can happen only when the particular thread
in the pool that can serve the STA component is available. As you can
see, this situation is heralding poor performance issues, and possibly
even a deadlock.
By setting the AspCompat attribute to true,
you force ASP.NET to use an STA thread pool to accommodate the COM
object on a per-page basis. In this case, both the caller thread and the
callee component live in the same apartment and extra overhead is
involved. As a matter of fact, ASP.NET pages that contain STA COM
components run better in STA mode than in an otherwise generally faster
MTA mode.
Because the AspCompat
attribute is processed after the instance of the page class is created,
you should also avoid creating instances of STA COM objects in the page
constructor. If you don’t avoid this, the page will be served by an MTA
thread regardless of the value of AspCompat and you’ll probably experience poor performance.
Setting AspCompat to true has another advantage—it makes ASP’s intrinsic objects (ObjectContext, Request, Response,
and so on) available to the COM component. ASP.NET creates unmanaged
ASP intrinsic objects and passes them to the COM components used in the
page.