Introduction
ASP+ configuration information is stored in XML-based configuration files. Using built-in features of IIS 5.0 and IE 5.0 such as the FileSystemObject, the XML Document Object Model (DOM) and XML Data Islands, we can easily develop a rich tool for modifying and editing these configuration files. In this three part lesson we will build a web based tool for loading, modifying, and saving the ASP+ config.web configuration file.
In part one (this lesson) we will discuss the contents and structure of the config.web file, then build an ASP+ file for loading the config.web file into a client side DOM object (data island). Part two will feature data-binding the XML DOM object to client intrinsic controls, providing a rich editing environment. Finally, in part three we will post the XML data back to the server and save the new config.web file back to the ASP+ application.
Note: ASP+, config.web, and many other items in this discussion are part of Microsoft’s .NET framework, which is still in development and subject to change. Information for these articles has been obtained from preliminary documentation, also subject to change, and from hands on experience working with the .NET framework SDK technology preview.
To run the samples included with this lesson you will need to install the .NET Framework Technology Preview. Visit http://msdn.microsoft.com/net/ for more information.
Welcome to config.web
ASP developers are used to using global configuration files in the form of global.asa and other home built files. ASP+ adds on to this concept of global configuration by providing a true configuration file in the config.web file. Rather than being a global event handler for handling state configuration, the config.web file is a real configuration file for storing system and application settings independent of state.
The config.web file is XML-based, which means that it inherits all of the wonderful features that are the nature of XML files, such as being human-readable and human-writeable, as well as being customizable (extensible). This feature of being extensible means that in addition to storing all of the system configuration items, you can add your own configuration criteria and settings within the configuration system.
Following is an example of a config.web file:
<!-- CONFIG.WEB FILE --> <configuration> <configsections> <add names="httpmodules" type="System.Web.Config.HttpModulesConfigHandler"/> <add names="httphandlers" type="System.Web.Config.HttpHandlerConfigHandler"/> <add names="sessionstate" type="System.Web.Config.SessionStateConfigHandler"/> <add names="globalization" type="System.Web.Config.GlobalizationConfigHandler"/> <!-- ADDITIONAL CONFIGSECTION DECLARATIONS GO HERE --> </configsections> <httpmodules> <!-- http module subelements go here --> </httpmodules> <httphandlers> <!-- http handlers subelements go here --> </httphandlers> <sessionstate> <!-- session state subelements go here --> </sessionstate> <globalization> <!-- session state subelements go here --> </globalization> <!-- ADDITIONAL CONFIG SECTIONS GO HERE --> </configuration> |
Notice that all configuration information must reside between the <configuration> and </configuration> tags. The file is made up of two main parts. The first part is the <configsections> section which specifies the configuration section handler declarations. The remainder of the file contains the actual configuration sections, as specified in the <configsections> part. The configuration section handler declarations specify the name of each configuration section, and the .NET assembly and class that will handle that configuration information. For Example:
<configuration> <configsections> <add name="debugmode" type="System.Web.Config.SingleTagSectionHandler" /> <add name="globalization" type="System.Web.Config.SingleTagSectionHandler" /> <add name="assemblies" type="System.Web.UI.AssembliesSectionHandler" /> <add name="security" type="System.Web.Config.SecurityConfigHandler" /> </configsections> <debugmode enable="true" /> <globalization requestencoding="us-ascii" responseencoding="iso-8859-1" /> <assemblies> <add assembly="System.Data.dll"/> <add assembly="System.dll"/> <add assembly="System.Drawing.dll"/> <add assembly="*"/> </assemblies> <security> <authorization> <allow users="*" /> </authorization> </security> </configuration> |
The above example demonstrates the use of four configuration sections.
ASP+ Standard Section Handlers
ASP+ ships with several standard section handlers that can be used to process configuration settings within config.web files. The .NET SDK documents each of these standard section handlers in detail with sample code to demonstrate proper syntax. The following is a summary of these standard section handlers:
- <browsecaps> : Configures the settings for the browser capabilities component.
- <compilation> : Contains all the compilation settings.
- <configuration> : Root configuration section for all ASP+ configuration files.
- <configsections> : Contains a list that indicates the configuration section handlers associated with each configuration section.
- <globalization> : Configures the globalization settings of the application.
- <httphandlerfactories> : Maps incoming URL requests to IHttpHandlerFactories.
- <httphandlers> : Maps incoming URL requests to IHttpHandler classes.
- <httpmodules> : Adds, removes or clears Http Modules within an application.
- <processmodel> : Configures the ASP+ process model settings on IIS Web Server systems.
- <security> : Configures all security settings used by ASP+.
- <sessionstate> : Configures the session state HttpModule.
- <tracing> : Configures the ASP+ trace service.
Custom Configuration Settings
As I stated above, it is possible to extend the configuration file with your own configuration settings. To do so, all you have to do is create your own Configuration Section Handler, which must be a class that implements the System.Web.Config.IConfigSectionHandler interface.
The IConfigSectionHandler interface is defined as follows:
namespace System.Web.Config { public interface IConfigSectionHandler { public Object Create(Object parent, ConfigInput[] input, String path); } } |
For more information on how to add custom configuration settings, see the .NET Framework SDK.
Accessing Configuration Settings
Accessing the configuration settings found in the config.web file is extremely simple and quite straight forward. One thing to remember is that the configuration settings are hierarchical, thus it is possible to have configuration files for specific URLs.
Accessing the configuration settings is accomplished by using the GetConfig() method exposed on the HttpContext object. For example:
SessionStateConfig config = (SessionStateConfig) Context.GetConfig("sessionstate"); if (config.CookieLess == true) { // Cookieless sessions are activated… } if (config.InProc == true) { // InProc session state is being used…. } |
The above example shows how to access configuration information for the current URL being processed. To access configuration information for a specific URL, use the following example:
SessionStateConfig config = (SessionStateConfig) Context.GetConfig("sessionstate", "/MyDir/MyApp.aspx"); if (config.CookieLess == true) { // Cookieless sessions are activated… } if (config.InProc == true) { // InProc session state is being used…. } |
Building our Editor
Now that we’re squared away on the config.web file, what it is and what it looks like, let’s get started building our ASP+ based editor tool. In this lesson we are only going as far as loading the config.web document into a DOM object on the client. In follow-on lessons we’ll parse and modify the XML data, then return it to the server and save it.
DOM vs. File System Object vs. XmlTextReader
Our task of loading the config.web file into a client side DOM object can be solved quite simply if we were to establish an XML data island on our page and load the config.web file directly into it. However, for our tool we’re going to load the file on the server side first, then send it down to the client. This will allow us to add some important server side pre-processing and post-processing to our tool.
Loading the config.web file on the server side can be accomplished several ways including using either the file system object, or the XML DOM object itself, or using the new XmlTextReader object. Each method has its own advantages. For example, we may want to include some additional information to the user of our tool such as last file save date and time. This information is easily obtainable using the file system object. On the other hand, we might be storing that information ourselves, along with versioning and other data directly in the XML file. Using the DOM in this case would likely be preferable. Then again, we might be concerned about speed and performance, and the XmlTextReader object can give us all that without the overhead of the DOM object.
Loading the XML File
To become familiar with loading the XML document on the server, let’s first build a page specifically for loading XML using the XmlTextReader class. This class provides direct parsing and tokenizing of XML. The object created here is not a DOM object (we’ll cover that in a minute). Instead, the XmlTextReader provides fast, stream based access to the XML data without the overhead of the DOM object.
Below is our .aspx file we’ll use to test loading the config.web file and for becoming more familiar with the XmlTextReader class:
<%@ Import Namespace="System" %> <%@ Import Namespace="System.IO" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="System.NewXml" %> <script language="C#" runat=server ID=main_Script> private void Page_Load(Object sender, EventArgs e) { ReadConfigWebFile myReadConfigWebFile = new ReadConfigWebFile(); output.InnerHtml = myReadConfigWebFile.Start(Server.MapPath("config.web")); } public class ReadConfigWebFile{ private string myDocument = ""; TextWriter writer; public string Start(String args){ myDocument = args; StringWriter writer = new StringWriter(); Console.SetOut(writer); XmlTextReader reader = null; try{ // Load the config.web file with the XmlTextReader Console.WriteLine ("Reading file: {0} ... please wait.", myDocument); reader = new XmlTextReader (myDocument); Console.WriteLine ("File: {0} sucessfully read!", myDocument); } catch (Exception e){ // Write an error if the file was not read properly Console.WriteLine ("Failed reading file: {0} !", myDocument); Console.WriteLine ("Exception: {0}", e.ToString()); } finally{ // State we are done. Console.WriteLine("Loading of file: {0} is complete.", myDocument); // Destroy the reader object if (reader != null) reader.Close(); } return writer.ToString(); } } </script> <html> <head></head> <body> <table align="left"> <tr><th><span></span></th></tr> <tr><td><h4><xmp id="output" runat="server"/></h4></td></tr> </table> </body> </html> |
Let’s quickly review what is being done here. First, we’re importing the System.IO, System.Xml, and System.NewXml assemblies. We’ll need these for reading the XML document and for communicating back to the user.
Next we have the Page_Load handler and the ReadConfigWebFile class. The Page_Load handler fires off the Start method of the ReadConfigWebFile class, passing to it the name of the configuration file we are going to read (in this case, config.web). Notice that the Page_Load handler is also going to write to the page the results of our loading.
The ReadConfigWebFile class is quite simple, but may look completely foreign to you if you’re used to the ASP method of using the DOM. Here, in the Start method, we are using three main objects: myDocument, which is the name of the configuration file we passed in, the Console object that we pass messages to, and the XmlTextReader object used for reading the XML data.
Our “try” structure is going to attempt to open the XML document “config.web” simply by creating a new XmlTextReader object, passing into it the file path and name of our configuration file. Using the XmlTextReader object is that simple! If the load is successful, then processing continues to the “finally” struct, otherwise the “catch” struct will be processed where we will learn what the error message is.
Lastly, we’ll return our message, which will be displayed on the screen.
Moving to the DOM
Now that we’re using the XmlTextReader object, what if we wanted to create a DOM object out of it? Using the same example as above, let’s add just a few lines to introduce DOM functionality.
<%@ Import Namespace="System" %> <%@ Import Namespace="System.IO" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="System.NewXml" %> <script language="C#" runat=server ID=main_Script> private void Page_Load(Object sender, EventArgs e) { ReadConfigWebFile myReadConfigWebFile = new ReadConfigWebFile(); output.InnerHtml = myReadConfigWebFile.Start(Server.MapPath("config.web")); } public class ReadConfigWebFile{ private string myDocument = ""; TextWriter writer; public string Start(String args){ myDocument = args; StringWriter writer = new StringWriter(); Console.SetOut(writer); XmlTextReader reader = null; try{ // Load the config.web file with the XmlTextReader Console.WriteLine ("Reading file: {0} ... please wait.", myDocument); reader = new XmlTextReader (myDocument); // Create the XmlDocument from the XmlTextReader XmlDocument xmldocument = new XmlDocument(); xmldocument.Load (reader); Console.WriteLine ("File: {0} sucessfully read!", myDocument); Console.WriteLine ("DOM object loaded successfully ..."); Console.WriteLine (); Console.WriteLine ("XML Document Contents:"); xmldocument.Save(writer); } catch (Exception e){ // Write an error if the file was not read properly Console.WriteLine ("Failed reading file: {0} !", myDocument); Console.WriteLine ("Exception: {0}", e.ToString()); } finally{ // State we are done. Console.WriteLine("Loading of file: {0} is complete.", myDocument); // Destroy the reader object if (reader != null) reader.Close(); } return writer.ToString(); } } </script> <html> <head></head> <body> <table align="left"> <tr><th><span></span></th></tr> <tr><td><h4><xmp id="output" runat="server"/></h4></td></tr> </table> </body> </html> |
The lines in red above are the only chances necessary to create a DOM object! Now that we have our DOM object, we can move on and pass it to our client side XML data-island. But first, we’ll make one last modification to the page. Instead of writing out to the page as we have been, we’ll change the content type of the page to “text/xml” and write the XML data directly to the response stream. This will allow us to load directly into our data island. The modified code now looks like this:
<%@ Import Namespace="System" %> <%@ Import Namespace="System.IO" %> <%@ Assembly Name="System.Xml" %> <%@ Import Namespace="System.NewXml" %> <script language="C#" runat=server ID=main_Script> private void Page_Load(Object sender, EventArgs e) { ReadConfigWebFile myReadConfigWebFile = new ReadConfigWebFile(); Response.ContentType = "text/xml"; Response.Write("<?xml version='1.0' encoding='ISO-8859-1'?>"); Response.Write(myReadConfigWebFile.Start(Server.MapPath("config.web"))); } public class ReadConfigWebFile{ private string myDocument = ""; TextWriter writer; public string Start(String args){ myDocument = args; StringWriter writer = new StringWriter(); Console.SetOut(writer); XmlTextReader reader = null; try{ // Load the config.web file with the XmlTextReader reader = new XmlTextReader (myDocument); // Create the XmlDocument from the XmlTextReader XmlDocument xmldocument = new XmlDocument(); xmldocument.Load (reader); xmldocument.Save(writer); } catch (Exception e){ // Write an error if the file was not read properly Console.WriteLine ("Failed reading file: {0} !", myDocument); Console.WriteLine ("Exception: {0}", e.ToString()); } finally{ // Destroy the reader object if (reader != null) reader.Close(); } return writer.ToString(); } } </script> |
Notice the use of the System.Web.HttpResponse object above. We can simply use standard ASP Response statements to do so. In order to stay compliant we need to add the <?xml version=’1.0′> tag to our XML document. That way this file can be used by more than just our editor tool.
Now, the last step is to build our HTML file with the XML Data Island to load into. For now we’ll use a simple HTML file as such:
<html> <head><title>ASP+ Config.Web Editor</title> <script> function showXML(){ alert(myData.xml); } </script></head> <body> <h1>ASP+ Config.Web Ediotr</h1> <xml id="myData" src="sample3.aspx"></xml> <button id="myButton" onClick="showXML();">Show XML</button> </body> </html> |
Here our XML Data Island is identified using the <xml id=”mydata” src=”,,,”></xml> structure. Notice how the source is pointing to our previous example “sample3.aspx”. You can see how flexible this method is since we can dynamically build the XML on the server and load it into a client side DOM object. By clicking on the button the content of the Data Island is shown so that we can verify that we have successfully loaded the XML. Now we’re ready to build our editing tool that will allow us to modify the XML document and save it back to the server.
Summary
Today we’ve examined the ASP+ config.web file and learned about it’s capabilities. We then learned that using the XmlTextReader object on the server we can easily read XML documents onto the server. We also looked at how to send that XML data down to the client as XML. Using this technique we can build rich applications that build XML on the server and leverage client capabilities to handle the resulting XML data.
In our next lesson we will take our editor to the next step by creating a client side editing environment in Internet Explorer using the XML Data Island and data binding features of IE. We’ll discover how to use the DOM on the client side to modify the XML data, and return the XML data back to the server.
Supporting Materials
A zip file containing the code from this article is available for download: source.zip (6 kb)