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, say, Server.MapPath in the code without incurring compile errors.
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.
Table 12-8 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 URL.
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.
In 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.
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 12-9 lists the 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. |
Only the first two overloads listed in Table 12-9 are supported in ASP.NET 1.x.
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 can be used later at will. Figure 12-4 shows this approach in action the main page generates the boldfaced text, while the child page's output is shown in normal font sandwiched between the main page output.
Figure 12-4: The response text generated by Execute can be automatically embedded in the main response or cached in a writer object.
The source code for the main page in Figure 12-4 is as follows:
void Page_Load(object sender, EventArgs e) { Response.Write("Response generated before Execute is called<hr>"); Server.Execute("child.aspx"); Response.Write("<hr>Response generated after the call to Execute."); }
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 in 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. |
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). In ASP.NET 2.0 only, you can also directly indicate the HTTP handler to invoke with the same security issues that were mentioned for Execute.
Any 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.
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 be hardly 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 different from those automatically set by Visual Studio .NET. The utility is the Type Library Importer (tlbimp.exe) and 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=localhost;UID=sa;") 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 model (STA). This situation is detected and throws an exception. 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. |
Running STA components in a multithreaded apartment environment (MTA) 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 Visual Basic 6 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 important, a thread switch can happen only when the particular thread in the pool that can serve the STA component is available. As you see, this situation is heralding poor performance issues, and possibly even 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.