With ASP.NET, configuration files are stored along with other filessuch as data files, Web pages, and executable fileswithin a single directory. The beauty of this is that simply copying the directory is all that a user or administrator needs to do. You don't need to locate and move any extra files to make an application work. The configuration files, always called config.web, are editable in any standard text editor and are human-readable . A user or administrator can easily update or alter the config.web file with a text editor, XML parser, or scripting language. These updates are immediately detected by the system, which is another great feature because it eliminates the need to reboot the system or restart the Web server. The configuration files in ASP.NET provide a complete array of commands and tools that enable developers to completely define their own configuration settings, control when they are processed , and ultimately control how those settings affect the object model. ASP.NET configuration files are XML-based text files. Each config.web file applies its configuration settings to the directory in which it resides as well as any subdirectories. If unique or different settings are needed in lower directories, a user or administrator only needs to include another config.web file in that directory. This file applies its settings to the files in that directory and in its subdirectories. When you run the application, the config.web files are applied in a top-down hierarchy that results in unique settings each time someone logs on. ASP.NET calculates these settings only once; the results are cached for future logons and the configuration files are monitored for changes. If changes are made, the cache is dumped and new settings are calculated. For example, the configuration settings for the URL http://yourserver/yourapplication/yourdir/page.aspx would be computed by applying the settings in the following order: C:\WinNT\Microsoft.NET\Framework\v.1.00\config.web Configuration settings for machine. D:\yourapplication\config.web <=Overridden by Application Configuration Settings D:\yourapplication\yourdir\config.web <=Overridden by Sub-Directory Config Settings It is not necessary for every directory to have a config.web file. If one is not present, the configuration settings are inherited from the parent directory's config.web file. A Word About XMLBecause the configuration files for ASP.NET are XML-based text files, it makes sense to spend a little time discussing XML. Please read Chapter 8 for more detail concerning XML. To begin with, XML elements are made up of a start tag, an end tag, and data in between. The data in between, referred to as the value of the element, is described by the start and end tags. For example, in the following example, the XML element is a restaurant element with value Joe's . <restaurant>Joe's</restaurant> Using an element name such as restaurant makes it possible to distinguish between another element with the value Joe's . <barber>Joe's</barber> The different element tag names make it easy to distinguish between Joe's Restaurant and Joe's Barber Shop. XML tag names are case sensitive, so each of the following tags represents a different element: <Restaurant> <restaurant> <RESTAURANT> XML AttributesXML elements can also contain attributes. An attribute is a name-value pair separated by an equal sign (=). In the following example, Zip = "32771" is an attribute of the City element. Attributes can be used to attach additional, secondary information to an element. Attributes can accept default values but elements cannot. Each attribute can be specified only once, but it can appear in any order. <City Zip = "32771">Sanford</City> Creating a Well- Formed XML ElementXML is a highly structured language, so it is very important that all XML be well formed. In other words, every XML element must have a start tag and an end tag, and must use the proper syntax. Writing XML DocumentsA very basic XML document is simply an XML element that might or might not include nested XML elements. The following demonstrates a valid XML document without nested elements: <movies> <movie year= "1942"> <title>Casablanca</title> <stars>Bogart and Bergman</stars> </movie> </movies> It's important to remember four key things when constructing a basic XML document:
Creating a Well-Formed XML DocumentThe following code checks your XML code to see whether it is well formed using a special function called CheckWellFormed() : Function checkWellFormed() { if (XMLsource.value != "") { xmlid.loadXML(XMLsource.value); if (xmlid.parseError.reason == "") alert("Your XML is well formed"); else alert(xmlid.parseError.reason); } else alert("Please enter an XML document."); } Configuration Section Handlers and SectionsBecause ASP.NET config.web files are XML-based text files, they can contain standard XML document elements, including well-formed tags, comments, text, and so on. You may use ANSI, UTF-8, or Unicode, because the system detects the encoding automatically. The root element of the config.web file is always a tag. ASP.NET and end-user settings are encapsulated within this tag: <configuration> <Configuration settings go here....> </configuration> Encapsulated within this tag are usually two types of elements:
The work of processing the data in the config.web file is delegated to " configuration section handlers -.NET." These section handlers are framework classes that implement the ConfigurationSectionHandler interface. These declarations need to appear only once (usually in the root config.web file for the machine) because, as noted earlier, the child directories automatically inherit these declarations. The declaration for configuration section handlers is made using "add" tags within a tag called <configsections> , which is located in the config.web file. The "add" tag specifies a tag name that denotes a specific area of configuration data and the IConfigurationSectionHandler class that processes it. In short, different classes of configuration data have their own specific section handlers, and the "add" tags specify which handler should be used. Also nested > tag>>inside the ASP.NET <configuration> tag are configuration tag sections. Each configuration section requires the appropriate section handler. The section handler is defined in the config hierarchy. In the following example, <httpmodules> is the configuration section that defines the "httphandlers" configuration data. At runtime, the System.Web.Configuration.HttpModulesConfigurationHandler class reads the content in the <httpmodules> tag. <configuration> <configsections> <add name="httpmodules" type="System.Web.Configuration.HttpModulesConfigurationHandler" /> </configsections> <httpmodules> <add type="System.Web.Sessionstate.CookielessSessionModule" /> <add type="System.Web.Caching.OutputCacheModule" /> <add type="System.Web.SessionState.SessionStateModule" /> <add type="System.Web.Security.WindowsAuthenticationModule"/> <add type="System.Web.Security.CookieAuthenticationModule"/> <add type="System.Web.Security.PassportAuthenticationModule"/> <add type="System.Web.Security.CustomAuthenticationModule"/> <add type="System.Web.Security.UrlAuthorizationModule"/> <add type="System.Web.Security.FileAuthorizationModule"/> </httpmodules> </configuration>> tag>> Standard ASP.NET Configuration SectionASP.NET comes with a nice collection of standard configuration section handlers. These are used to process the configuration settings in the config.web file.
HttpHandler classes are low-level request/response APIs. To take advantage of these, your user-written classes need to support the System.Web.IHTTPHandler interface and use the ProcessRequest() method. Common uses for handlers include such tasks as filtering and CGI-like applications, especially those that return binary data. When ASP.NET receives an HTTP request, it is processed by a specific class that implements IHttpHandler . Handler factories assign each request to one handler, which processes the request. Three standard IHttpHandlerFactory classes ship with ASP.NET: PageHandlerFactory , RestrictedResourceFactory , and WebServiceHandlerFactory . Of course, you are free to create and register your own custom handlers and factories that meet your specific requirements. The declarations> tag>>> tag>> for HTTP handlers and factories are found in the config.web file. The <httphandlers> are defined in the same way in which the <httpmodules> configuration section was defined earlier, and this is where handlers and factories can be added or removed. For example, requests for .aspx files are sent by ASP.NET to the PageHandlerFactory class: <httphandlers> ... <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" /> ... </httphandlers> Of course, subdirectories inherit the settings for HttpHandlerFactory and HttpHandler . The IHttpHandler interface can be used to create a custom HttpHandler . Two methods are used. The IsReusable method enables a factory to query a handler to determine whether multiple requests can be serviced with one instance of the handler. The ResponseRequest() method responds to HTTP text, which gives access to the built-in Request and Response mechanisms. In the following example, request data is ignored and the phrase "Hello World!" is sent as a response to the user. Public Class SimpleHandler : Inherits IHttpHandler Public Sub ProcessRequest(context As HttpContext) context.Response.Write("Hello World!") End Sub Public Function IsReusable() As Boolean Return(True) End Function End Class The same handler in C# is public class SimpleHandler implements IHttpHandler { public function ProcessRequest(context:HttpContext) : void { context.Response.Write("Hello World!"); } public function IsReusable() : Boolean { return true; } } After the file has been compiled, the handler assembly is placed in the application's /bin directory. The class is included in the HttpHandlers section of the config.web file. It then can be sent requests. In our example, all requests for SimpleHandler.aspx will be routed to the SimpleHandler class. <sessionstate>This configuration section enables users to set values to suit their own preferences. These settings last for the length of the session or until modified by the user. In the following example, the values for user preferences are initialized by the Session_Start event included in the global.asax. They can be changed again by the user on the customization page with the Submit_Click event handler. The following global.asax file contains the Session_Start event and the default user preferences that are loaded when the session begins: Sub Session_Start() Session("BackColor") = "beige" ... End Sub From the customization page, the user has the option to change the look of the page by setting user preferences. These are set by the Submit_Click event: Protected Sub Submit_Click(sender As Object, E As EventArgs) Session("BackColor") = BackColor.Value ... Response.Redirect(State("Referer").ToString()) End Sub The GetStyle method is used to retrieve the values: Protected GetStyle(key As String) As String Return(Session(key).ToString()) End Sub The GetStyle method is used to set session-specific styles: <style> body { font: <%=GetStyle("FontSize")%> <%=GetStyle("FontName")%>; background-color: <%=GetStyle("BackColor")%>; } a { color: <%=GetStyle("LinkColor")%> } </style> You can use the <sessionstate> section in the config.web file to configure session state features. For example, if you feel that the default timeout of 20 minutes is not enough, it can be reconfigured with <sessionstate timeout="40" /> ASP.NET uses cookies by default to identify requests belonging to one session; however, if cookies are not available, a session identifier can be added to the URL. You can enable this as follows : <sessionstate cookies="true" /> ASP.NET has a lot of flexibility when it comes to storing session state. It can store session state data in the same process that processes the request, as ASP does. It also gives you the option to store session state data in an external process. This can be on the same machine or on another machine. To use this feature, do the following:
The following example assumes that the state service is running on the same machine as the Web server ( "myserver" ) and uses the default port ( "42424" ): <sessionstate inproc="false" server="myserver" port="42424" /> Table 7.1 shows the attributes of sessionstate. Table 7.1. The sessionstate Attributes
<globalization>With ASP.NET, you tag (XML)> tag (XML)>> tag;attributes>>can use the configuration file to set a truly globalized environment or to localize particular applications. The following is an example of the <globalization> section of the config.web file: <configuration> <globalization> fileencoding="utf-8" requestencoding="utf-8" responseencoding="utf-8" culture="en-us" uiculture="de" /> </configuration> The fileencoding attributes in the <globalization> section determines the encoding of the .aspx files. The requestencoding and the responseencoding , respectively, determine the encodings for requests and responses. The configuration settings can be overridden at the page level and are valid for only that page. Use the following syntax: %@Page Culture="fr" UICulture="fr" ResponseEncoding="utf-8% The preceding defines a page that uses French culture settings and a utf-8 response. Within a page, you can use ThreadCurrentCulture and ThreadUICulture to change the settings with the program. Table 7.2 shows the attributes available for globalization. Table 7.2. Globalization Attributes
<compilation>The compilation section of the config.web file specifies which assemblies to load. Assemblies are stored in the \bin file. An assembly is simply a business component that has been compiled using a C#, Visual Basic, or JavaScript compiler. A business component is just a class that can be run from a Web form. A class can be written in C#, Visual Basic, or JavaScript. In the following Visual Basic example, our simple class is called HelloWorld : Imports System Imports System.Text Namespace HelloWorld Public Class HelloObj Private _name As String Public Sub New MyBase.New() _name = Nothing End Sub Public Property FirstName As String Get Return(_name) End get Set _name = value End Set End Property Public Function SayHello() As String Dim sb As New StringBuilder("Hello ") If (_name <> Nothing) Then sb.Append(_name) Else sb.Append("World") End If sb.Append("!") Return(sb.ToString()) End Function End Class End Namespace The HelloWorld class has one public constructor, a String property called FirstName and a SayHello method that prints a greeting using the FirstName string. Use the /t and /out options of the C# compiler to compile code. The /t option tells the computer to build a DLL (library). The /out option tells the computer where to put the assembled file. As you saw earlier, the final assembly should be in the \bin file. The following is the compilation command: csc /t:library /out:..\..\..\bin\HelloObj.dll HelloObj.cs For Visual Basic, the equivalent compilation command is vbc /t:library /out:..\..\..\..\bin\HelloObjVB.dll HelloObj.vb For JScript, the equivalent compilation command is jsc /out:..\..\..\..\bin\HelloObjJS.dll HelloObj.js The component is now available for the application. Using the Import directive <%@ Import Namespace="HelloWorld" %> makes the class available for use in the Web form. Only a simple namespace import is required because the ASP.NET, by default, preloads assemblies specified in the config.web file as in the following example: <configuration> <compilation> <assemblies> <!The following assemblies are loaded explicitly from the global cache> <add assembly="System.Data"/> <add assembly="System.Web.Services"/> <add assembly="System.Drawing"/> <!This tells ASP.NET to load all assemblies from /bin> <add assembly="*"/> </assemblies> </compilation> </configuration> Because assemblies loaded from the /bin directory are limited in scope to the application where they are running, peer applications can use different assemblies with the same class or namespace names without a conflict. Table 7.3 shows the compilation attributes that are available. Table 7.3. Compilation Attributes
<trace>ASP.NET provides two levels of tracingpage-level tracing and application-level tracingto make it easy for developers to insert debugging print statements into their code. At the page level, developers can use the TraceContext built-in attribute to write custom debugging statements. ASP.NET also inserts helpful information, including important statistics about page execution, start/end of lifecycle methods, Load() and Dispose() , and inputs/outputs (from external code) to a page. You can leave these statements in the production code for a page without impacting the page's performance, because tracing can be explicitly enabled or disabled for a page. At the page level, debugging statements can be written directly to the page output. To use this feature, include the following directive at the top of the page: <%@ Page Trace="true"%> This code sorts the trace statements by time, which is the default. You might want to sort by category, in which case, the following attribute should be included: <%@ Page Trace="true" TraceMode="SortByCategory" %> Another debugging tool that you will find useful is the TraceContext property. This property can be used to output debugging statements to your page when tracing is enabled. You can write debugging statements using Trace.Write and Trace.Warn . The statements each take category and message strings as arguments. The statements are identical except that Trace.Warn outputs in red. The following is an example of Trace.Write and Trace.Warn : Trace.Write("Custom Trace","Beginning User Code..."); ... Trace.Warn("Custom Trace","Array count is null!"); If you want to leave these statements in your code for later use, simply use this command or take the Trace= statement completely out of the code: Trace="false" If you have additional debugging code that needs to execute only when tracing has been enabled, ASP.NET allows the use of a Boolean statement, Trace.IsEnabled , which returns true only if tracing is enabled for the page. The following is an example of Trace.IsEnabled : if (Trace.IsEnabled) { for (int i=0; i<ds.Tables["Categories"].Rows.Count; i++) { Trace.Write("ProductCategory", ds.Tables["Categories"].Rows[i][0].ToString()); } } Application-level tracing provides a view of several requests to an application's pages at once. It displays inputs and outputs such as form variables, query-string variables , or headers. It also shows important statistics regarding page execution, such as application state, session state, and so on. When you use application-level tracing, page-level tracing is automatically enabled for pages in the application. The config.web file is used to enable application-level tracing. To enable application-level tracing, place the following in the config.web file: <configuration> <trace enabled="true"/> </configuration> The default setting for application-level tracing is to collect information on up to 10 requests. However, you can change this and other attributes in the configuration file. The following table is a list of the attributes supported in the trace configuration section:
The following example shows how to set these values: <configuration> <trace enabled="true" tracemode="sortbycategory" requestlimit="40" pageoutput="false"/> </configuration> After you have made requests to the application, you can access trace.axd and it lists the requests in time order. It displays a table listing each request. If you click the View Details link, you can look at the details of each request. The trace application presents the following detailed information for each request:
<security>Security is an important part of most Web applications today. ASP.NET offers a variety of features to help you build the desired level of security into your applications. At the heart of security is the capability to identify the user requesting access to the application. This is called authentication . Authentication is accomplished when a user is required to present credentials , often in the form of a user name and password combination. After the user's identity is authenticated, the next step is to determine the user's level of authorization . In other words, to what parts of the application does this user have access? ASP.NET works in conjunction with IIS to provide authentication and authorization services to applications. Impersonation is the term used to describe the situation when a server application executes code using the identity of the requesting user. It is important that a server application be capable of controlling when and whether this will occur. Another security- related feature is the capability of an application to present content based on the user's identity or on a particular role that user might have. ASP.NET can dynamically check whether the current user belongs to a group . For example, you might want to generate content based on whether a user is a manager or to which department the user belongs. ASP.NET authentication services are subject to the authentication services provided by IIS (Internet Information Systems). ASP.NET supports using Basic, Digest, Windows, and Microsoft Passport authentication services. ASP.NET also provides for forms-based authentication. This authentication service uses cookies to authenticate users. It also enables an application to do its own credential checking. As for authorization, ASP.NET provides two types of services: checks against ACLs (Access Control Lists) or permissions and URL authorization. In the first case, the authorization is based not on the identity of the user, but on the ACLs that reside in the .aspx file. For example, if an application allows anonymous access using the MYMACHINE account, a request for an ASP.NET page (for example, "/default.aspx") is authorized with a check against the ACLs on that file (for example, C:\inetpub\ wwwroot \default.aspx), which verifies that the MYMACHINE account has access to the file. This is called file authorization and it is performed automatically. With URL authorization, the anonymous user is checked against the configuration data for the ASP.NET application. In this case, ASP.NET checks to see whether the anonymous user has access to /default.aspx. Although the differences might seem small, this additional control enables you to use forms-based authentication schemes or passport authentication, where the user might need access from different machines or domain accounts. File authorization is always performed against the authenticated account provided by IIS. To ASP users, this should sound very familiar because it is what you are used to, and it works exactly as ASP does today. To use an ASP.NET authentication service, you must use the <authentication> element in the <security> section of the applications config.web file. The <authentication> element can have its mode attribute set to one of the following values:
The following is an example of an authentication value set to "Cookie" : <configuration> <security> <authentication mode="Cookie"/> </security> </configuration> <processmodel>This configuration section configures the ASP.NET process model on IIS Web Server Systems. <browsercaps>This section controls the settings of the browser capabilities component. Retrieving ConfigurationAs an ASP.NET developer, you can retrieve configuration settings from within an application by using either very specifically identified properties or general configuration APIs (Application Programming Interfaces). The following code shows how to access the <browsercaps> configuration section with the Browser property of the System.Web.UI.Page class. This is a listing of attributes that describe the capabilities of the browser client that is currently accessing the page. The actual < browsercaps > section data is included in the machine-level config.web file. <%@ Page Language="VB" %> <html> <body style="font: 10pt verdana"> <h3>Retrieving Browser Capabilities</h3> Boolean ActiveXControls = <%=Request.Browser.ActiveXControls.ToString()%><br> Boolean AOL = <%=Request.Browser.AOL.ToString()%><br> Boolean BackgroundSounds = <%=Request.Browser.BackgroundSounds.ToString()%><br> Boolean Beta = <%=Request.Browser.Beta.ToString()%><br> String Browser = <%=Request.Browser.Browser%><br> Boolean CDF = <%=Request.Browser.CDF.ToString()%><br> Boolean Cookies = <%=Request.Browser.Cookies.ToString()%><br> Boolean Crawler = <%=Request.Browser.Crawler.ToString()%><br> Boolean Frames = <%=Request.Browser.Frames.ToString()%><br> Boolean JavaApplets = <%=Request.Browser.JavaApplets.ToString()%><br> Boolean JavaScript = <%=Request.Browser.JavaScript.ToString()%><br> Int32 MajorVersion = <%=Request.Browser.MajorVersion.ToString()%><br> Double MinorVersion = <%=Request.Browser.MinorVersion.ToString()%><br> String Platform = <%=Request.Browser.Platform%><br> Boolean Tables = <%=Request.Browser.Tables.ToString()%><br> String Type = <%=Request.Browser.Type%><br> Boolean VBScript = <%=Request.Browser.VBScript.ToString()%><br> String Version = <%=Request.Browser.Version%><br> Boolean Win16 = <%=Request.Browser.Win16.ToString()%><br> Boolean Win32 = <%=Request.Browser.Win32.ToString()%><br> </body> </html> The same code example in C# looks like this: <%@ Page Language="C#" %> <html> <body style="font: 10pt verdana"> <h3>Retrieving Browser Capabilities</h3> Boolean ActiveXControls = <%=Request.Browser.ActiveXControls.ToString()%><br> Boolean AOL = <%=Request.Browser.AOL.ToString()%><br> Boolean BackgroundSounds = <%=Request.Browser.BackgroundSounds.ToString()%><br> Boolean Beta = <%=Request.Browser.Beta.ToString()%><br> String Browser = <%=Request.Browser.Browser%><br> Boolean CDF = <%=Request.Browser.CDF.ToString()%><br> Boolean Cookies = <%=Request.Browser.Cookies.ToString()%><br> Boolean Crawler = <%=Request.Browser.Crawler.ToString()%><br> Boolean Frames = <%=Request.Browser.Frames.ToString()%><br> Boolean JavaApplets = <%=Request.Browser.JavaApplets.ToString()%><br> Boolean JavaScript = <%=Request.Browser.JavaScript.ToString()%><br> Int32 MajorVersion = <%=Request.Browser.MajorVersion.ToString()%><br> Double MinorVersion = <%=Request.Browser.MinorVersion.ToString()%><br> String Platform = <%=Request.Browser.Platform%><br> Boolean Tables = <%=Request.Browser.Tables.ToString()%><br> String Type = <%=Request.Browser.Type%><br> Boolean VBScript = <%=Request.Browser.VBScript.ToString()%><br> String Version = <%=Request.Browser.Version%><br> Boolean Win16 = <%=Request.Browser.Win16.ToString()%><br> Boolean Win32 = <%=Request.Browser.Win32.ToString()%><br> </body> </html> Another method of accessing configuration data from any configuration section is to use the GetConfig method of the System.Web.HttpContext class. The data returned by GetConfig is dependent on the SectionHandler that is mapped to the configuration section. The following code shows how to access configuration data using GetConfig . The section being accessed is customconfig , and we assume the configuration section handler will return an object of type CustomConfigSettings with property "Enabled" . You aren't assuming the return type, you are casting the returned object to the CustomConfigSettings type so that you can assign it to config . If you don't do the cast you will get a compiler error, if you try to cast to the wrong type you will get a runtime error. In C#, the code would be CustomConfigSettings config = (CustomConfigSettings) Context.GetConfig("customconfig"); if (config.Enabled == true) { // do something here... } In Visual Basic the code would be Dim config As CustomConfigSettings = CType(Context.GetConfig("customconfig"), CustomConfigSettings) If config.Enabled = True Then ' do something here... End If In JScript the code would be var config:CustomConfigSettings = CustomConfigSettings(Context.GetConfig("customconfig")); if (config.Enabled == true) { // do something here... } Defining a Custom Configuration SectionBecause configuration files are very well suited for storing custom application settings, such as database strings, file paths, or remote Web service URLs, ASP.NET provides a convenient configuration section handler, System.Web.Configuration.DictionarySectionHandler , which you can map to a custom configuration section of your choosing. For example, the following config.web file maps the DictionarySectionHandler to a section named " teams " , which is used to store file paths: <configuration> <configsections> <add name="teams" type= "System.Web.Configuration.DictionarySectionHandler" /> </configsections> <teams> <add key="Dallas" value="place file path here" /> </teams> </configuration> The DictionarySectionHandler returns a Hashtable containing the key/value pairs defined in the "teams" section. You can access this data using the GetConfig method of the HttpContext class from within the application code. In Visual Basic, the code would be Dim dsn As String = CType((Ctype(Context.GetConfig("teams"), Hashtable)("Dallas"), String) In C#, the code would be String dsn = (String) ((Hashtable) Context.GetConfig("teams"))["Dallas"]; |