WHAT’S IN THIS CHAPTER?
WROX.COM CODE DOWNLOADS FOR THIS CHAPTER
Please note that all the code examples in this chapter are available as a part of this chapter’s code download on the book’s website at www.wrox.com on the Download Code tab.
ASP.NET uses an XML file-based configuration system that is flexible, accessible, and easy to use. The XML configuration file allows an administrator, the person who takes care of the web applications after they are built and deployed, to configure ASP.NET applications quite easily by working either directly with the various configuration files or by using GUI tools that, in turn, interact with configuration files. Before examining the various GUI-based tools in detail in Appendix D, this chapter will first take an in-depth look at how to work directly with the XML configuration files to change the behavior of your ASP.NET applications.
The journey examining ASP.NET configuration in depth starts with an overview of configuration in ASP.NET.
ASP.NET configuration is stored in two primary XML-based files in a hierarchal fashion. XML is used to describe the properties and behaviors of various aspects of ASP.NET applications.
The ASP.NET configuration system supports two kinds of configuration files:
Because the configuration files are based upon XML, the elements that describe the configuration are, therefore, case-sensitive. Moreover, the ASP.NET configuration system follows camel-casing naming conventions. If you look at the session state configuration example shown in Listing 28-1, for example, you can see that the XML element that deals with session state is presented as <sessionState>.
LISTING 28-1: Session state configuration
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<system.web>
<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1; user id=sa; password=P@55worD"
cookieless="false"
timeout="20" />
</system.web>
</configuration>
The benefits of having an XML configuration file instead of a binary metabase include the following:
Every ASP.NET server installation includes a series of configuration files, such as the machine.config file. This file is installed as a part of the default .NET Framework installation. You can find machine.config and the other server-specific configuration files in C:\Windows\Microsoft.NET\Framework\v4.0.30319\CONFIG. They represent the default settings used by all ASP.NET web applications installed on the server.
Some of the server-wide configuration files include the following:
The system-wide configuration file, machine.config, is used to configure common .NET Framework settings for all applications on the machine. As a rule, editing or manipulating the machine.config file is not a good idea unless you know what you are doing. Changes to this file can affect all applications on your computer (Windows, web, and so on).
In addition to the machine.config file, the .NET Framework installer also installs two more files called machine.config.default and machine.config.comments. The machine.config.default file acts as a backup for the machine.config file. If you want to revert to the factory setting for machine.config, simply copy the settings from the machine.config.default to the machine.config file.
The machine.config.comments file contains a description for each configuration section and explicit settings for the most commonly used values. The machine.config.default and machine.config.comments files are not used by the .NET Framework run time; they’re installed in case you want to revert to default factory settings and default values.
You will also find a root-level web.config file in place within the same CONFIG folder as the machine.config. When making changes to settings on a server-wide basis, you should always attempt to make these changes in the root web.config file rather than in the machine.config file. You will find that files like the machine.config.comments and the machine.config.default files also exist for the web.config files (web.config.comments and web.config.default).
By default, your ASP.NET web applications run under a full trust setting. You can see this setting by looking at the <securityPolicy> and <trust> sections in the root-level web.config file. Listing 28-2 presents these sections.
LISTING 28-2: The root web.config file showing the trust level
<configuration>
<location allowOverride="true">
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal" />
<trustLevel name="High" policyFile="web_hightrust.config" />
<trustLevel name="Medium" policyFile="web_mediumtrust.config" />
<trustLevel name="Low" policyFile="web_lowtrust.config" />
<trustLevel name="Minimal" policyFile="web_minimaltrust.config" />
</securityPolicy>
<trust level="Full" originUrl="" />
</system.web>
</location>
</configuration>
The other policy files are defined at specific trust levels. These levels determine the code-access security (CAS) allowed for ASP.NET. To change the trust level in which ASP.NET applications can run on the server, you simply change the <trust> element within the document or within your application’s instance of the web.config file. For example, you can change to a medium trust level using the code shown in Listing 28-3.
LISTING 28-3: Changing the trust level to medium trust
<configuration>
<location allowOverride="false">
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal" />
<trustLevel name="High" policyFile="web_hightrust.config" />
<trustLevel name="Medium" policyFile="web_mediumtrust.config" />
<trustLevel name="Low" policyFile="web_lowtrust.config" />
<trustLevel name="Minimal" policyFile="web_minimaltrust.config" />
</securityPolicy>
<trust level="Medium" originUrl="" />
</system.web>
</location>
</configuration>
In this case, not only does this code mandate use of the web_mediumtrust.config file, but also (by setting the allowOverride attribute to false) it forces this trust level upon every ASP.NET application on the server. Individual application instances are unable to change this setting by overriding it in their local web.config files because this setting is in the root-level web.config file.
If you look through the various trust level configuration files (such as the web_mediumtrust.config file), notice that they define what kinds of actions you can perform through your code operations. For example, the web_hightrust.config file allows for open FileIO access to any point on the server as illustrated in Listing 28-4.
LISTING 28-4: The web_hightrust.config file’s definition of FileIO CAS
<IPermission
class="FileIOPermission"
version="1"
Unrestricted="true"
/>
If, however, you look at the medium trust web.config file (web_mediumtrust.config), you see that this configuration file restricts ASP.NET to only those FileIO operations within the application directory. Listing 28-5 presents this definition.
LISTING 28-5: FileIO restrictions in the web_mediumtrust.config file
<IPermission
class="FileIOPermission"
version="1"
Read="$AppDir$"
Write="$AppDir$"
Append="$AppDir$"
PathDiscovery="$AppDir$"
/>
Seeing in which trust level you can run your ASP.NET applications and changing the <trust> section to enable the appropriate level of CAS is always a good idea.
Unlike the machine.config file, nearly all ASP.NET applications have their own copy of configuration settings stored in a file called web.config. If the web application spans multiple subfolders, each subfolder can have its own web.config file that inherits or overrides the parent’s file settings.
To update servers in your farm with these new settings, you simply copy this web.config file to the appropriate application directory. ASP.NET takes care of the rest — no server restarts and no local server access is required — and your application continues to function normally, except that it now uses the new settings applied in the configuration file.
When the ASP.NET run time applies configuration settings for a given web request, machine.config (as well as any of the web.config files’ configuration information) is merged into a single unit, and that information is then applied to the given application. Configuration settings are inherited from any parent web.config file or machine.config, which is the root configuration file or the ultimate parent. Figure 28-1 presents an example of this.
The configuration for each web application is unique; however, settings are inherited from the parent. For example, if the web.config file in the root of your website defines a session timeout of 10 minutes, then that particular setting overrides the default ASP.NET setting inherited from the machine.config or the root web.config file. The web.config files in the subdirectories or subfolders can override these settings or inherit the settings (such as the 10-minute session timeout).
Note that these inheritance/override rules can be blocked in most cases by using the allowOverride="false" mechanism shown earlier in Listing 28-3.
ASP.NET automatically detects when configuration files, such as machine.config or web.config, are changed. This logic is implemented based on listening for file-change notification events provided by the operating system.
When an ASP.NET application is started, the configuration settings are read and stored in the ASP.NET cache. A file dependency is then placed on the entry within the cache in the machine.config and/or web.config configuration file. When the configuration file update is detected in the machine.config, ASP.NET creates a new application domain to service new requests. The old application domain is destroyed as soon as it completes servicing all its outstanding requests.
The main difference between machine.config and web.config is the filename. Other than that, their schemas are the same. Configuration files are divided into multiple groups. The root-level XML element in a configuration file is named <configuration>. This pseudo-web.config file has a section to control ASP.NET, as shown in Listing 28-6.
LISTING 28-6: A pseudo-web.config file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<configSections>
<section name="[sectionSettings]" type="[Class]"/>
<sectionGroup name="[sectionGroup]">
<section name="[sectionSettings]" type="[Class]"/>
</sectionGroup>
</configSections>
</configuration>
The root element in the XML configuration file is always <configuration>. Each of the section handlers and settings are optionally wrapped in a <sectionGroup>. A <sectionGroup> provides an organizational function within the configuration file. It allows you to organize configuration into unique groups — for instance, the <system.web> section group is used to identify areas within the configuration file specific to ASP.NET.
The <configSections> section is the mechanism to group the configuration section handlers associated with each configuration section. When you want to create your own section handlers, you must declare them in the <configSections> section. The <httpModules> section has a configuration handler that is set to System.Web.Caching.HttpModulesSection, and the <sessionState> section has a configuration handler that is set to the System.Web.SessionState.SessionStateSection classes, as shown in Listing 28-7.
LISTING 28-7: HTTP module configuration setting from the machine.config file
<configSections>
<sectionGroup>
<section name="httpModules"
type="System.Web.Configuration.HttpModulesSection,
System.Web, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"/>
</sectionGroup>
</configSections>
The ASP.NET applications depend on a few common configuration settings. These settings are common to both the web.config and machine.config files. In this section, you look at some of these common configuration settings.
In very early ASP.NET releases, all the connection string information was stored in the <appSettings> section. The latest versions of ASP.NET include a section called <connectionStrings> that stores a variety of connection-string information. Even though storing connection strings in the <appSettings> element works fine, it poses the following challenges:
Because the connection-string information is stored independently of the appSettings section, it can be retrieved using the strongly typed collection method ConnectionStrings. Listing 28-8 gives an example of how to store connection strings.
LISTING 28-8: Storing a connection string
<configuration>
<connectionStrings>
<add
name="ExampleConnection"
connectionString="server=401kServer;database=401kDB;
uid=WebUser;pwd=P@$$worD9" />
</connectionStrings>
</configuration>
Listing 28-9 shows how to retrieve the connection string (ExampleConnection) in your code.
LISTING 28-9: Retrieving a connection string
VB
Protected Sub Page_Load(sender As Object, e As EventArgs)
. . .
Dim dbConnection As New SqlConnection( _
ConfigurationManager.ConnectionStrings("ExampleApplication") _
.ConnectionString)
. . .
End Sub
C#
protected void Page_Load(Object sender, EventArgs e)
{
. . .
SqlConnection dbConnection = new
SqlConnection(ConfigurationManager.ConnectionStrings["ExampleApplication"]
.ConnectionString);
. . .
}
This type of construction has a lot of power. Instead of hard-coding your connection strings into every page within your ASP.NET application, you can store one instance of the connection string centrally (in the web.config file, for example). Now, if you have to make a change to this connection string, you can make this change in only one place rather than in multiple places.
Because web-based applications utilize the stateless HTTP protocol, you must store the application-specific state or user-specific state where it can persist. The Session object is the common store where user-specific information is persisted. Session store is implemented as a Hashtable and stores data based on key-value pair combinations.
ASP.NET has the capability to persist the session store data in InProc, StateServer, SqlServer, or Custom. The Custom setting gives the developer a lot more control regarding how the session state is persisted in a permanent store. For example, out-of-the-box ASP.NET does not support storing session data on non-Microsoft databases such as Oracle or NoSQL databases such as MongoDB. If you want to store the session data in any of these databases or in a custom store such as an XML file, you can implement that by writing a custom provider class. (See the section “Custom State Store” later in this chapter or Chapter 21 to learn more about the session state features in ASP.NET 4.5.)
You can configure the session information using the <sessionState> element presented in Listing 28-10.
LISTING 28-10: Configuring session state
<sessionState
mode="StateServer"
cookieless="false"
timeout="20"
stateConnectionString="tcpip=ExampleSessionStore:42424"
stateNetworkTimeout="60"
sqlConnectionString=""
/>
The following list describes some of the attributes for the <sessionState> element shown in the preceding code:
Multiple web servers working as a group are called a web farm. If you would like to scale out your ASP.NET application into multiple servers inside a web farm, ASP.NET supports this kind of deployment out of the box. However, the session data needs to be persisted in an out-of-process session state such as StateServer or SQLServer.
Both StateServer and SQLServer support the out-of-process session state. However, the StateServer stores all the session information in a Windows Service, which stores the session data in memory. Using this option, if the server that hosts the session state service goes down in the web farm, all the ASP.NET clients that are accessing the website fail; there is no way to recover the session data.
You can configure the session state service using the Services window available by choosing Start ⇒ Control Panel ⇒ System and Security ⇒ Administrative Tools ⇒ Services if you are using Windows 7. In Windows 8, you enter and choose View Local Services in the Settings tab of the Search charm (as shown in Figure 28-2).
Alternatively, you can start the session state service by using the command prompt and entering the net start command, like this:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\> net start aspnet_state
When you choose the SQLServer option, session data is stored in a Microsoft SQL Server database. Even if SQL Server goes down, the built-in SQL Server recovery features enable you to recover all the session data. Configuring ASP.NET to support SQL Server for session state is just as simple as configuring the Windows Service. The only difference is that you run a T-SQL script that ships with ASP.NET, InstallSqlState.sql. The T-SQL script that uninstalls ASP.NET SQL Server support, called UninstallSqlState.sql, is also included. The install and uninstall scripts are available in the Framework folder. Listing 28-11 shows an example of using the SQL Server option.
LISTING 28-11: Using the SQLServer option for session state
<configuration>
<system.web>
<sessionState
mode="SQLServer"
sqlConnectionString="data source=ExampleSessionServer;
user id=ExampleWebUser;password=P@55worD"
cookieless="false"
timeout="20"
/>
</system.web>
</configuration>
ASP.NET accesses the session data stored in SQL Server via stored procedures. By default, all the session data is stored in the Temp DB database. However, you can modify the stored procedures so they are stored in tables in a full-fledged database other than Temp DB.
Because the connection strings are stored in the strongly typed mode, the connection string information can be referenced in other parts of the configuration file. For example, when configuring session state to be stored in SQL Server, you can specify the connection string in the <connectionStrings>section, and then you can specify the name of the connection string in the <sessionState> element, as shown in Listing 28-12.
LISTING 28-12: Configuring session state with a connection string
<configuration>
<connectionStrings>
<add name = "ExampleSqlSessionState"
connectionString = "data source=ExampleSessionServer;
user id=ExampleWebUser;password=P@55worD" />
</connectionStrings>
<system.web>
<sessionState
mode="SQLServer"
sqlConnectionString="ExampleSqlSessionState"
cookieless="false"
timeout="20"
/>
</system.web>
</configuration>
The session state in ASP.NET 4.5 is based on a pluggable architecture with different providers that inherit the SessionStateStoreProviderBase class. If you want to create your own custom provider or use a third-party provider, you must set the mode to Custom.
You specify the custom provider assembly that inherits the SessionStateStoreProviderBase class, as shown in Listing 28-13.
LISTING 28-13: Working with your own session state provider
<configuration>
<system.web>
<sessionState mode="Custom" customProvider="CustomStateProvider">
<providers>
<add name="CustomStateProvider"
type="CustomStateProviderAssembly,
CustomStateProviderNamespace.CustomStateProvider"/>
</providers>
</sessionState>
</system.web>
</configuration>
In the previous example, you have configured the session state mode as Custom because you have specified the provider name as CustomStateProvider. From there, you add the provider element and include the type of the provider with namespace and class name.
ASP.NET supports the dynamic compilation of ASP.NET pages, web services, HttpHandlers, ASP.NET application files (such as the Global.asax file), source files, and so on. These files are automatically compiled on demand when they are first required by an ASP.NET application.
Any changes to a dynamically compiled file causes all affected resources to become automatically invalidated and recompiled. This system enables developers to quickly develop applications with a minimum of process overhead because they can just click Save to immediately cause code changes to take effect within their applications.
You can configure the ASP.NET compilation settings using the <compilation> section in the web.config or machine.config files. The ASP.NET engine compiles the page when necessary and saves the generated code in code cache. This cached code is used when executing the ASP.NET pages. Listing 28-14 shows the syntax for the <compilation> section.
LISTING 28-14: The <compilation> section
<!-- compilation Attributes -->
<compilation
tempDirectory="" [String]
debug="false" [true|false]
strict="false" [true|false]
explicit="true" [true|false]
batch="true" [true|false]
optimizeCompilations="false" [true|false]
urlLinePragmas="false" [true|false]
batchTimeout="900" [in Seconds][number]
maxBatchSize="1000" [number]
maxBatchGeneratedFileSize="1000" [number]
numRecompilesBeforeAppRestart="15" [number]
defaultLanguage="vb" [String]
targetFramework="" [String]
assemblyPostProcessorType="" [String]
>
<assemblies>
<add assembly="" [String, Required, Collection Key] />
</assemblies>
<buildproviders>
<add extension="" [String, Required, Collection Key]
type="" [String, Required] />
</buildproviders>
<folderLevelBuildProviders
<add name="" [String, Required, Collection Key]
type="" [String, Required] />
</folderLevelBuildProviders>
<expressionBuilders
<add expressionPrefix="" [String, Required, Collection Key]
type="" [String, Required] />
</expressionBuilders>
<codeSubDirectories>
<add directoryName="" [String, Required, Collection Key] />
</codeSubDirectories>
</compilation>
Now take a more detailed look at these <compilation> attributes:
When the ASP.NET application fails, the ASP.NET page can show the default error page with the source code and line number of the error. However, this approach has a few problems:
Displaying too much error information could provide important implementation details that in most cases you want to keep from the public. Figure 28-3 shows an example.
However, ASP.NET provides excellent infrastructure to prevent this kind of error information. The <customErrors> section provides a means for defining custom error messages in an ASP.NET application. The syntax is as follows:
<customErrors defaultRedirect="[url]" mode="[on/off/remote]">
<error statusCode="[statuscode]" redirect="[url]" />
</customErrors>
In Chapter 20, you see the authentication process in detail. In this section, you can review configuration-specific information. Authentication is a process that verifies the identity of the user and establishes the identity between the server and a request. Because HTTP is a stateless protocol, the authentication information is persisted somewhere in the client or the server; ASP.NET supports both of these.
You can store the server-side information in Session objects. When it comes to the client side, you have many options:
ASP.NET supports following authentication methods out of the box:
If you want to disable authentication, you can use the setting mode = "None":
<authentication mode="None" />
ASP.NET relies on IIS’s infrastructure to implement Windows authentication, and Windows authentication enables you to authenticate requests using Windows Challenge/Response semantics. When the web server receives a request, it initially denies access to the request (which is a challenge). This triggers the browser to pop up a window to collect the credentials; the request responds with a hashed value of the Windows credentials, which the server can then choose to authenticate.
To implement Windows authentication, you configure the appropriate website or virtual directory using IIS. You can then use the <authentication> element to mark the web application or virtual directory with Windows authentication. Listing 28-15 illustrates this process.
LISTING 28-15: Setting authentication to Windows authentication
<configuration>
<system.web>
<authentication mode="Windows" />
</system.web>
</configuration>
Forms authentication is the widely used authentication mechanism. To configure forms authentication you use the <authentication> section along with the <forms> subsection. Listing 28-16 shows the structure of an <authentication> section that deals with forms authentication in the configuration file.
LISTING 28-16: The <authentication> section working with forms authentication
<configuration>
<system.web>
<authentication mode="Forms">
<forms
name=".ASPXAUTH" [String]
loginUrl="login.aspx" [String]
defaultUrl="default.aspx" [String]
protection="All" [All|None|Encryption|Validation]
timeout="30" [in Minutes][number]
path="/" [String]
requireSSL="false" [true|false]
slidingExpiration="true" [true|false]
enableCrossAppRedirects="false" [true|false]
cookieless="UseDeviceProfile"
[UseUri|UseCookies|AutoDetect|UseDeviceProfile]
domain="" [String]
ticketCompatibilityMode="Framework20" [Framework20|Framework40]>
<credentials passwordFormat="SHA1" [Clear|SHA1|MD5]>
<user name="" [String, Required, CollectionKey]
password="" [String, Required] />
</credentials>
</forms>
</authentication>
</system.web>
</configuration>
Each attribute is shown in detail in the following list:
Many application types require the capability to work with anonymous users, although this is especially true for e-commerce web applications. In these cases, your site must support both anonymous and authenticated users. When anonymous users are browsing the site and adding items to a shopping cart, the web application needs a way to uniquely identify these users. For example, if you look at busy e-commerce websites such as Amazon.com or BN.com, they do not have a concept called anonymous users. Rather these sites assign a unique identity to each user.
In early versions of ASP.NET, no out-of-the box feature existed to enable a developer to achieve this identification of users. Most developers used SessionID to identify users uniquely. They experienced a few pitfalls inherent in this method. Later, ASP.NET included anonymous identity support using the <anonymousIdentification> section in the configuration file. Listing 28-17 shows the <anonymousIdentification> configuration section settings.
LISTING 28-17: Working with anonymous identification in the configuration file
<configuration>
<system.web>
<anonymousIdentification
enabled="false" [true|false]
cookieName=".ASPXANONYMOUS" [String]
cookieTimeout="100000" [in Minutes][number]
cookiePath="/" [String]
cookieRequireSSL="false" [true|false]
cookieSlidingExpiration="true" [true|false]
cookieProtection="Validation" [None|Validation|Encryption|All]
cookieless="UseCookies"
[UseUri|UseCookies|AutoDetect|UseDeviceProfile]
domain="" [String]
/>
</system.web>
</configuration>
The enabled attribute within the <anonymousIdentification> section specifies whether the anonymous access capabilities of ASP.NET are enabled. The other attributes are comparable to those in the <authentication> section from Listing 28-16. When you are working with anonymous identification, the possibility exists that the end users will have cookies disabled in their environments. When the end users don’t enable cookies, their identities are stored in the URL strings within their browsers.
The authorization process verifies whether a user has the privilege to access the resource he is trying to request. ASP.NET supports both file and URL authorization. The authorization process dictated by an application can be controlled by using the <authorization> section within the configuration file. The <authorization> section, as presented in Listing 28-18, can contain subsections that either allow or deny permission to a user, a group of users contained within a specific role in the system, or a request that is coming to the server in a particular fashion (such as an HTTP GET request). Optionally, you can also use the <location> section to grant special authorization permission to only a particular folder or file within the application.
LISTING 28-18: Authorization capabilities from the configuration file
<authorization>
<allow users="" roles="" verbs="" />
<deny users="" roles="" verbs="" />
</authorization>
The URL authorization is a service provided by URLAuthorizationModule (inherited from HttpModule) to control the access to resources such as .aspx files. The URL authorization is very useful if you want to allow or deny certain parts of your ASP.NET application to certain people or roles.
For example, you might want to restrict the administration part of your ASP.NET application only to administrators and deny access to others. You can achieve this task very easily with URL authorization. URL authorization can be configurable based on the user, the role, or HTTP verbs such as the HTTP GET request or the HTTP POST request.
You can configure URL authorization in the web.config file with <allow> and <deny> attributes. For example, the code in Listing 28-19 shows how you can allow the user Jason and deny the groups Sales and Marketing access to the application.
LISTING 28-19: Allowing and denying entities from the <authorization> section
<system.web>
<authorization>
<allow users="Jason" />
<deny roles="Sales, Marketing" />
</authorization>
</system.web>
The <allow> and <deny> elements support users, roles, and verbs values. As you can see from the previous code example, you can add multiple users and groups by separating them with commas.
Two special characters, an asterisk (*) and a question mark (?), are supported by URLAuthorizationModule. The asterisk symbol represents all users (anonymous and registered) and the question mark represents only anonymous users. The following code example in Listing 28-20 denies access to all anonymous users and grants access to anyone contained within the Admin role.
LISTING 28-20: Denying anonymous users
<system.web>
<authorization>
<allow roles="Admin" />
<deny users="?" />
</authorization>
</system.web>
You can also grant or deny users or groups access to certain HTTP methods. In the example in Listing 28-21, access to the HTTP GET method is denied to the users contained within the Admin role, whereas access to the HTTP POST method is denied to all users.
LISTING 28-21: Denying users and roles by verb
<system.web>
<authorization>
<deny verbs="GET" roles="Admin" />
<deny verbs="POST" users="*" />
</authorization>
</system.web>
It’s possible to construct the authorization section within the configuration file so that what is specified can be applied to a specific file or directory using the <location> element. For example, suppose you have a root directory called Home within your application, and nested within that root directory you have a subdirectory called Documents. Suppose you want to allow access to the Documents subdirectory only to those users contained within the Admin role. Listing 28-22 illustrates this scenario.
LISTING 28-22: Granting access to the Documents subdirectory for the Admin role
<configuration>
<location path="Documents">
<system.web>
<authorization>
<allow roles="Admin" />
<deny users="*" />
</authorization>
</system.web>
</location>
</configuration>
You can also set the security for a single file as presented in Listing 28-23.
LISTING 28-23: Granting access to a specific file for the Admin role
<configuration>
<location path="Documents/Default.aspx">
<system.web>
<authorization>
<allow roles="Admin" />
<deny users="*" />
</authorization>
</system.web>
</location>
</configuration>
ASP.NET’s configuration system is quite flexible in terms of applying configuration information to a specific application or folder. Even though the configuration system is flexible, in some cases you may want to limit the configuration options that a particular application on the server can control. For example, you could decide to change the way in which the ASP.NET session information is stored. This lock-down process can be achieved using the <location> attributes allowOverride and allowDefinition, as well as the path attribute.
Listing 28-24 illustrates this approach. A <location> section in this machine.config file identifies the path "Default Web Site/ExampleApplication" and allows that application to override the <trace> setting through the use of the allowOverride attribute.
LISTING 28-24: Allowing a <trace> section to be overridden in a lower configuration file
<configuration>
<location path="Default Web Site/ExampleApplication" allowOverride="true">
<trace enabled="false"/>
</location>
</configuration>
The trace attribute can be overridden because the allowOverride attribute is set to true. You are able to override the tracing setting in the ExampleApplication’s web.config file and enable the local <trace> element, thereby overriding the settings presented in Listing 28-24.
However, if you had written the attribute as allowOverride="false" in the <location> section of the machine.config file, the web.config file for ExampleApplication is unable to override that specific setting.
When an ASP.NET application has been deployed, the <pages> section of the configuration file enables you to control some of the default behaviors for each ASP.NET page. These behaviors include options such as whether you should buffer the output before sending it or whether session state should be enabled for the entire application. Listing 28-25 shows an example of using the <pages> section.
LISTING 28-25: Configuring the <pages> section
<configuration>
<system.web>
<pages
buffer="true" [true]|false]
enableSessionState="true" [False|ReadOnly|True]
enableViewState="true" [true]|false]
enableViewStateMac="true" [true]|false]
enableEventValidation="true" [true]|false]
smartNavigation="false" [true]|false]
autoEventWireup="true" [true]|false]
maintainScrollPositionOnPostBack="false" [true]|false]
pageBaseType="System.Web.UI.Page" [String]
userControlBaseType="System.Web.UI.UserControl" [String]
pageParserFilterType="" [String]
validateRequest="true" [true]|false]
masterPageFile="" [String]
theme="" [String]
styleSheetTheme="" [String]
maxPageStateFieldLength="-1" [number]
compilationMode="Always" [Auto|Never|Always]
viewStateEncryptionMode="Auto" [Auto|Always|Never]
asyncTimeout="45" [in Seconds][number]
renderAllHiddenFieldsAtTopOfForm="true" [true]|false]
clientIDMode="Predictable" [Inherit|AutoID|Predictable|Static]
controlRenderingCompatibilityVersion="4.0" [Version] >
<namespaces autoImportVBNamespace="true" [true|false] >
<add namespace="" [String, Required, CollectionKey] />
</namespaces>
<controls />
<tagMapping />
<ignoreDeviceFilters />
</pages>
</system.web>
</configuration>
The following list gives you some of the ASP.NET page configuration information elements in detail:
<configuration>
<location path="ExampleApplicationAdmin">
<system.web>
<pages
masterPageFile="~/ExampleApplicationAdminMasterPage.master"
/>
</system.web>
</location>
</configuration>
ASP.NET supports include files in both the machine.config and the web.config files. When configuration content is to be included in multiple places or inside the location elements, an include file is an excellent way to encapsulate the content.
Any section in a configuration file can include content from a different file using the configSource attribute in the <pages> section. The value of the attribute indicates a virtual relative filename to the include file. Listing 28-26 is an example of such a directive.
LISTING 28-26: Adding content to the web.config file
<configuration>
<system.web>
<pages configSource="SystemWeb.config" />
</system.web>
</configuration>
The configuration include files can contain information that applies to a single section, and a single include file cannot contain more than one configuration section or a portion of a section. If the configSource attribute is present, the section element in the source file should not contain any other attribute or any child element.
Nevertheless, the include file is not a full configuration file. It should contain only the include section, as presented in Listing 28-27.
LISTING 28-27: The SystemWeb.config file
<pages authentication mode="Forms" />
The configSource attribute cannot be nested. An include file cannot nest another file inside it using the configSource attribute.
The general configuration settings specify how long a given ASP.NET resource, such as a page, is allowed to execute before being considered timed-out. The other settings specify the maximum size of a request (in kilobytes) or whether to use fully qualified URLs in redirects. To specify these settings you use the <httpRuntime> section within a configuration file. The <httpRuntime> element is applied at the ASP.NET application at the folder level. Listing 28-28 shows the default values used in the <httpRuntime> section.
LISTING 28-28: The <httpRuntime> section
<configuration>
<system.web>
<httpRuntime
useFullyQualifiedRedirectUrl="false"
enable="true"
executionTimeout="90"
maxRequestLength="4096"
requestLengthDiskThreshold="512"
appRequestQueueLimit="5000"
minFreeThreads="8"
minLocalRequestFreeThreads="4"
enableKernelOutputCache="true" />
</system.web>
</configuration>
The enable attribute specifies whether the current ASP.NET application is enabled. When set to false, the current ASP.NET application is disabled, and all the clients trying to connect to this site receive the HTTP 404 — File Not Found exception. This value should be set only at the machine or application level. If you set this value in any other level (such as subfolder level), it is ignored. This great feature enables the administrators to bring down the application for whatever reason without starting or stopping IIS. The default value is true.
The useFullyQualifiedRedirectUrl attribute specifies whether the client-side redirects should include the fully qualified URL. When you are programming against the mobile devices, some devices require specifying fully qualified URLs. The default value is false.
The executionTimeout setting specifies the timeout option for an ASP.NET request time-out. The value of this attribute is the amount of time in seconds during which a resource can execute before ASP.NET times out the request. The default setting is 110 seconds. If you have a particular ASP.NET page or web service that takes longer than 110 seconds to execute, you can extend the time limit in the configuration.
The maxRequestLength attribute specifies the maximum upload size accepted by ASP.NET run time. For example, if the ASP.NET application is required to process huge files, then this setting is the one you will want to change. The default is 4096. This number represents kilobytes (KB or around 4MB).
Web applications are prone to attacks these days. The attacks range from a script injection attack to a denial of service (DoS) attack. The DoS is a typical attack that bombards the web server with requests for large files. This huge number of requests ultimately brings down the web server. The maxRequestLength attribute could save you from a DoS attack by setting a restriction on the size of requests.
ASP.NET 4.5 includes a setting called requestLengthDiskThreshold. This setting enables an administrator to configure the file upload buffering behavior without affecting the programming model. Administrators can configure a threshold below which requests will be buffered into memory. After a request exceeds the limit, it is transparently buffered on disk and consumed from there by whatever mechanism is used to consume the data. The valid values for this setting are numbers between 1 and Int32.MaxSize in KB.
When file buffering is enabled, the files are uploaded to the codegen folder. The default path for the codegen folder is the following:
[WinNT\Windows]\Microsoft.NET\Framework[version]\Temporary ASP.NET Files\
[ApplicationName]
The files are buffered using a random name in a subfolder within the codegen folder called Uploads. The location of the codegen folder can be configured on a per-application basis using the tempDirectory attribute of the <compilation> section.
ASP.NET run time uses free threads available in its thread pool to fulfill requests. The minFreeThreads attribute indicates the number of threads that ASP.NET guarantees is available within the thread pool. The default number of threads is eight. For complex applications that require additional threads to complete processing, this attribute simply ensures that the threads are available and that the application will not be blocked while waiting for a free thread to schedule more work. The minLocalRequestFreeThreads attribute controls the number of free threads dedicated for local request processing; the default is four.
The appRequestQueueLimit attribute specifies the maximum number of requests that ASP.NET queues for the current ASP.NET application. ASP.NET queues requests when it does not have enough free threads to process them. The minFreeThreads attribute specifies the number of free threads the ASP.NET application should maintain, and this setting affects the number of items stored in the queue.
The enableKernelOutputCache specifies whether the output caching is enabled at the IIS kernel level (Http.sys). At present, this setting applies only to web servers IIS6 and higher.
When a request for an ASP.NET page is received by IIS, it passes the request to an unmanaged DLL called aspnet_isapi.dll. The aspnet_isapi.dll further passes the request to a separate worker process — aspnet_wp.exe if you are working with IIS5 — which runs all the ASP.NET applications. With IIS6 and higher, however, all the ASP.NET applications are run by the w3wp.exe process. The ASP.NET worker process can be configured using the <processModel> section in the machine.config file.
The code example in Listing 28-29 shows the default format for the <processModel> section.
LISTING 28-29: The structure of the <processModel> element
<processModel
enable="true|false"
timeout="hrs:mins:secs|Infinite"
idleTimeout="hrs:mins:secs|Infinite"
shutdownTimeout="hrs:mins:secs|Infinite"
requestLimit="num|Infinite"
requestQueueLimit="num|Infinite"
restartQueueLimit="num|Infinite"
memoryLimit="percent"
cpuMask="num"
webGarden="true|false"
userName="username"
password="password"
logLevel="All|None|Errors"
clientConnectedCheck="hrs:mins:secs|Infinite"
responseDeadlockInterval="hrs:mins:secs|Infinite"
responseRestartDeadlockInterval="hrs:mins:secs|Infinite"
comAuthenticationLevel="Default|None|Connect|Call|
Pkt|PktIntegrity|PktPrivacy"
comImpersonationLevel="Default|Anonymous|Identify|
Impersonate|Delegate"
maxWorkerThreads="num"
maxIoThreads="num"
autoConfig="true|false"
minWorkerThreads="num"
minIoThreads="num"
serverErrorMessageFile=""
pingFrequency="hrs:mins:secs|Infinite"
pingTimeout="hrs:mins:secs|Infinite"
maxAppDomains="number"
/>
The following section looks at each of these attributes in more detail:
In the same context as the ASP.NET worker process, multiple websites within the given web server can host multiple websites, and each of these sites can be bound to a particular version of a .NET Framework. This is typically done using the aspnet_regiis.exe utility. The aspnet_regiis.exe utility ships with each version of the framework.
This utility has multiple switches. Using the -s switch allows you to install the current version of the .NET Framework run time on a given website. Listing 28-30 shows how to install .NET Framework version 2.0 on the ExampleApplication website.
LISTING 28-30: Installing .NET Framework version 2.0 on the ExampleApplication website
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>
aspnet_regiis -s W3SVC/1ROOT/ExampleApplication
Every web application must store some application-specific information for its runtime use. The <appSettings> section of the web.config file provides a way to define custom application settings for an ASP.NET application. The section can have multiple <add> sub-elements. Its syntax is as follows:
<appSettings>
<add key="[key]" value="[value]"/>
</appSettings>
The <add> sub-element supports two attributes:
Listing 28-31 shows how to store an application-specific connection string. The key value is set to ApplicationInstanceID, and the value is set to the ASP.NET application instance and the name of the server on which the application is running.
LISTING 28-31: Application instance information
<appSettings>
<add key="ApplicationInstanceID" value="Instance1onServerOprta"/>
</appSettings>
ASP.NET includes APIs (ASP.NET Management Objects) to manipulate the configuration information settings in machine.config and web.config files. ASP.NET Management Objects provide a strongly typed programming model that addresses targeted administrative aspects of a .NET Web Application Server. They also govern the creation and maintenance of the ASP.NET web configuration. Using the ASP.NET Management Objects, you can manipulate the configuration information stored in the configuration files in the local or remote computer. These can be used to script any common administrative tasks or to write installation scripts.
All the ASP.NET Management Objects are stored in the System.Configuration and System.Web.Configuration namespaces. You can access the configuration using the WebConfigurationManager class. The System.Configuration.Configuration class represents a merged view of the configuration settings from the machine.config and hierarchical web.config files. The System.Configuration and System.Web.Configuration namespaces have multiple classes that enable you to access nearly all the settings available in the configuration file. The main difference between the System.Configuration and System.Web.Configuration namespaces is that the System.Configuration namespace contains all the classes that apply to all the .NET applications. On the other hand, the System.Web.Configuration namespace contains the classes that are applicable only to ASP.NET web applications. Table 28-1 shows the important classes in System.Configuration and their uses.
CLASS NAME | PURPOSE |
Configuration | Enables you to manipulate the configuration stored in the local computer or a remote one |
ConfigurationElementCollection | Enables you to enumerate the child elements stored inside the configuration file |
AppSettingsSection | Enables you to manipulate the <appSettings> section of the configuration file |
ConnectionStringsSettings | Enables you to manipulate the <connectionStrings> section of the configuration file |
ProtectedConfigurationSection | Enables you to manipulate the <protectedConfiguration> section of the configuration file |
ProtectedDataSection | Enables you to manipulate the <protectedData> section of the configuration file |
Table 28-2 shows some of the classes from the System.Web.Configuration namespace and their uses.
CLASS NAME | PURPOSE |
AuthenticationSection | Enables you to manipulate the <authentication> section of the configuration file |
AuthorizationSection | Enables you to manipulate the <authorization> section of the configuration file |
CompilationSection | Enables you to manipulate the <compilation> section of the configuration file |
CustomErrorsSection | Enables you to manipulate the <customErrors> section of the configuration file |
FormsAuthenticationConfiguration | Enables you to manipulate the <forms> section of the configuration file |
GlobalizationSection | Enables you to manipulate the <globalization> section of the configuration file |
HttpHandlersSection | Enables you to manipulate the <httpHandlers> section of the configuration file |
HttpModulesSection | Enables you to manipulate the <httpModules> section of the configuration file |
HttpRuntimeSection | Enables you to manipulate the <httpRuntime> section of the configuration file |
MachineKeySection | Enables you to manipulate the <machineKey> section of the configuration file |
MembershipSection | Enables you to manipulate the <membership> section of the configuration file |
PagesSection | Enables you to manipulate the <pages> section of the configuration file |
ProcessModelSection | Enables you to manipulate the <processModel> section of the configuration file |
WebPartsSection | Enables you to manipulate the <webParts> section of the configuration file |
All the configuration classes are implemented based on simple object-oriented based architecture that has an entity class that holds all the data and a collection class that has methods to add, remove, enumerate, and so on. You start your configuration file programming with a simple connection string enumeration, as shown in the following section.
In a web application, you can store multiple connection strings. Some of them are used by the system and the others may be application-specific. You can write a very simple ASP.NET application that enumerates all the connection strings stored in the web.config file, as shown in Listing 28-32.
LISTING 28-32: The web.config file
<?xml version="1.0" ?>
<configuration>
<appSettings>
<add key="symbolServer" value="192.168.1.1" />
</appSettings>
<connectionStrings>
<add name="ExampleApplication"
connectionString="server=ExampleApplicationServer;
database=ExampleApplicationDB;uid=WebUser;pwd=P@$$worD9"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<compilation debug="false" targetFramework="4.5" />
<authentication mode="None" />
</system.web>
</configuration>
As shown in Listing 28-32, one application setting points to the symbol server, and one connection string is stored in the web.config file. Use the ConnectionStrings collection of the System.Web.Configuration.WebConfigurationManager class to read the connection strings, as shown in Listing 28-33.
LISTING 28-33: Binding the ConnectionStrings collection properties to a GridView control
VB
Protected Sub Page_Load(sender As Object, e As EventArgs)
GridView1.DataSource =
System.Web.Configuration.WebConfigurationManager.ConnectionStrings
GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
GridView1.DataSource =
System.Web.Configuration.WebConfigurationManager.ConnectionStrings;
GridView1.DataBind();
}
As shown in Listing 28-33, you’ve bound the ConnectionStrings property collection of the WebConfigurationManager class into the GridView control. The WebConfigurationManager class returns an instance of the Configuration class and the ConnectionStrings property is a static (shared in Visual Basic) property. Therefore, you are just binding the property collection into the GridView control. Figure 28-4 shows the list of connection strings stored in the ASP.NET application.
Adding a connection string at run time is also a very easy task. If you do it as shown in Listing 28-34, you get an instance of the configuration object. Then you create a new connectionStringSettings class. You add the new class to the collection and call the update method. Listing 28-34 shows examples of this in both VB and C#.
LISTING 28-34: Adding a connection string
VB
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
' Get the file path for the current web request
Dim webPath As String = Request.ApplicationPath
Try
' Get configuration object of the current web request
Dim config As Configuration = _
System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(webPath)
' Create new connection setting from text boxes
Dim newConnSetting As New _
ConnectionStringSettings(txtName.Text, txtValue.Text, txtProvider.Text)
' Add the connection string to the collection
config.ConnectionStrings.ConnectionStrings.Add(newConnSetting)
' Save the changes
config.Save()
Catch cEx As ConfigurationErrorsException
lblStatus.Text = "Status: " & cEx.ToString()
Catch ex As System.UnauthorizedAccessException
' The ASP.NET process account must have read/write access to the directory
lblStatus.Text = "Status: " & "The ASP.NET process account must have" & _
"read/write access to the directory"
Catch eEx As Exception
lblStatus.Text = "Status: " & eEx.ToString()
End Try
ShowConnectionStrings()
End Sub
Protected Sub ShowConnectionStrings()
GridView1.DataSource = System.Web.Configuration.WebConfigurationManager.ConnectionStrings
GridView1.DataBind()
End Sub
C#
protected void Button1_Click(object sender, EventArgs e)
{
// Get the file path for the current web request
string webPath = Request.ApplicationPath;
// Get configuration object of the current web request
Configuration config =
System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(webPath);
// Create new connection setting from text boxes
ConnectionStringSettings newConnSetting = new
ConnectionStringSettings(txtName.Text, txtValue.Text, txtProvider.Text);
try
{
// Add the connection string to the collection
config.ConnectionStrings.ConnectionStrings.Add(newConnSetting);
// Save the changes
config.Save();
}
catch (ConfigurationErrorsException cEx)
{
lblStatus.Text = "Status: " + cEx.ToString();
}
catch (System.UnauthorizedAccessException uEx)
{
// The ASP.NET process account must have read/write
// access to the directory
lblStatus.Text = "Status: " + "The ASP.NET process account must have" +
"read/write access to the directory";
}
catch (Exception eEx)
{
lblStatus.Text = "Status: " + eEx.ToString();
}
// Reload the connection strings in the list box
ShowConnectionStrings();
}
protected void ShowConnectionStrings()
{
GridView1.DataSource = System.Web.Configuration.WebConfigurationManager.ConnectionStrings;
GridView1.DataBind();
}
The OpenMachineConfiguration method of the System.Configuration.ConfigurationManager class provides a way to manipulate the machine.config file. The OpenMachineConfiguration method is a static method.
Listing 28-35 shows a simple example that enumerates all the section groups stored in the machine.config file. As shown in this listing, you’re getting an instance of the configuration object using the OpenMachineConfiguration method. Then you are binding the SectionGroups collection with the GridView control.
LISTING 28-35: Configuration groups from machine.config
VB
Protected Sub Page_Load(sender As Object, e As EventArgs)
' List all the SectionGroups in machine.config file
Dim configSetting As Configuration =
System.Configuration.ConfigurationManager.OpenMachineConfiguration()
GridView1.DataSource = configSetting.SectionGroups
GridView1.DataBind()
End Sub
C#
protected void Page_Load(Object sender, EventArgs e)
{
// List all the SectionGroups in machine.config file
Configuration configSetting =
System.Configuration.ConfigurationManager.OpenMachineConfiguration();
GridView1.DataSource = configSetting.SectionGroups;
GridView1.DataBind();
}
ASP.NET stores configuration information inside the registry using the Data Protection API (or DPAPI). For example, Listing 28-36 shows how you can store a process model section’s username and password information inside the registry.
LISTING 28-36: Storing the username and password in the registry and then referencing these settings in the machine.config file
<processModel
userName="registry:HKLM\SOFTWARE\ExampleApp\Identity\ASPNET_SETREG,userName"
password="registry:HKLM\SOFTWARE\ExampleApp\Identity\ASPNET_SETREG,password"
/>
ASP.NET 4.5 includes a system for protecting sensitive data stored in the configuration system. It uses industry-standard XML encryption to encrypt specified sections of configuration that contain any sensitive data.
Developers often feel apprehensive about sticking sensitive items such as connection strings, passwords, and more in the web.config file. For this reason, ASP.NET makes possible the storing of these items in a format that is not readable by any human or machine process without intimate knowledge of the encryption techniques and keys used in the encryption process.
One of the most encrypted items in the web.config file is the <connectionStrings> section. Listing 28-37 shows an example of a web.config file with an exposed connection string.
LISTING 28-37: A standard connection string exposed in the web.config file
<?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings>
<add name="AdventureWorks"
connectionString="Server=localhost;Integrated Security=True;Database=AdventureWorks"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<compilation debug="false" />
<authentication mode="Forms">
<forms name="Wrox" loginUrl="Login.aspx" path="/">
<credentials passwordFormat="Clear">
<user name="JasonGaylord" password="Reindeer" />
</credentials>
</forms>
</authentication>
</system.web>
</configuration>
In this case, you might want to encrypt this connection string to the database. To accomplish this, the install of ASP.NET provides a tool called aspnet_regiis.exe. You find this tool at C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319. To use this tool to encrypt the <connectionStrings> section, open a command prompt and navigate to the specified folder using cd C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319. Another option is to open the Visual Studio 2012 command prompt as an administrator. After you are in one of these environments, you use the syntax presented in Listing 28-38 to encrypt the <connectionStrings> section.
LISTING 28-38: Encrypting the <connectionStrings> section
aspnet_regiis -pe "connectionStrings" -app "/EncryptionExample"
Running this bit of script produces the results presented in Figure 28-5.
Looking over the script used in the encryption process, you can see that the –pe command specifies the section in the web.config file to encrypt, whereas the –app command specifies which application to actually work with. If you look back at the web.config file and examine the encryption that occurred, you see something similar to the code in Listing 28-39.
LISTING 28-39: The encrypted <connectionStrings> section of the web.config file
<?xml version="1.0"?>
<configuration>
<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>UAqurj7pvS7WLwt4CSXJN2Fe2yYcrXA1K
Go33DVzec0nOhUy6FYO751MARiaJvqSepvkuRPSU3ZRP
xESmc7p4t3N6i9OGT3q3xHIzSJV7rzSSpDZcd+DxQkwPp
oXeGx7x7H22DSWrxDEyn4EJQf3ZeTY2tcTcDcvH4UNLTD
wIW2ACM56s/OOZOCOVUq4nKdRC0q4W8enBoNIvhDdL2E5
ZpTUAsJl4MvrWOMqlVX8F0P6Osn+apX5Zh9QhvPLXz7G8
nbwNzSc8tLuCW1M419dmM6r97vID6qxNgBz3bJbM03BjT
WUBQEXSN5HBmuAVFl1QjzBDZbqFrBl+Mgu7TpQMw==</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>OEr7uMFQU7736eCharUocrRs442uD3Ivi2woGfon
SnpiReILGlkSfMdfdF7CLzfUIt8KIARZeKkAswJrOVDjinM75840
QLmwFnQhtSqKqphM92kbudTsmVrQkKJFN6Y2PElTpa9BG+nEf0HX
Y7cERhj2Yv1Hua7B+oYhM5TtfPUTEP3fpqROPNSlWvRmJN2XwDyj
mjTGON2Lk0pbAAf8rLwVB0IGT3lF6CKsK0YCPuiMAcFAHzcgzEXP
UFXOqJqXBFjoEL0jx/sWV4DHOFJq5p2+DPxHKoI4ZTWo1p1oxFWF
ro1eLimzygvjnsUmbHyP</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.5"/>
<httpRuntime targetFramework="4.5"/>
</system.web>
</configuration>
Now when you work with a connection string in your ASP.NET application, ASP.NET itself automatically decrypts this section to utilize the values stored. Looking at the web.config file, you can see a subsection within the <system.web> section that exposes a username and password as clear text. This is also something that you might want to encrypt in order to keep it away from prying eyes. Because it is a subsection, you use the script presented in Listing 28-40.
LISTING 28-40: Encrypting the <authentication> section
aspnet_regiis -pe "system.web/authentication" -app "/EncryptionExample"
This code gives you the partial results presented in Listing 28-41.
LISTING 28-41: The encrypted <authentication> section of the web.config file
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="ExampleApplication"
connectionString="server=ExampleApplicationServer;
database=ExampleApplicationDB;uid=WebUser;pwd=P@$$worD9"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.5"/>
<httpRuntime targetFramework="4.5"/>
<authentication
configProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>P4mqGAlV4Nmdsa+c3nARXx5OQeeOyo6JrKFJ/
DYYGFomQwq3tvErbaHQhffRr9S3UkeNcloFM/zg0Oxvdfq9X
tkR/dOb0o8LOKAnlBwoJ9+sKAdsd2U6tv+Of+k7h1Qwi3jM4
guTiBAudpZXr9TnjouwN9KI3xmebagYTkR2NSPFoFMcH5RQb
+iIRLeiGecVMpz17qz72acd3KWzabYHW2W+zbVA9lm2aATUb
kDrdk3BahxzdOID62+svSDqzSgyibaAjc4WSN1nTRsNvPKLo
aIUJiliTxn7APaunH8afdeqMmvOEGI3jVt733Pcz2rljMjUQ
yPkxfow1OMyhGKfAg==</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>kqNlrAcWy2ZHK+cRSpWQUOX4OPfjl2x3wqu
f9cXNFU+TuleXF3EUwCb/SQCYgdJz</CipherValue>
</CipherData>
</EncryptedData>
</authentication>
</system.web>
</configuration>
After you have sections of your web.config file encrypted, you need a process to decrypt these sections to their original unencrypted values. To accomplish this task, you use the aspnet_regiis tool illustrated in Listing 28-42.
LISTING 28-42: Decrypting the <connectionStrings> section in the web.config file
aspnet_regiis -pd "connectionStrings" -app "/EncryptionExample"
Running this script returns the encrypted values to original values.
So far in this chapter, you have learned about configuration files and what each configuration entry means. Even though the configuration entries are in an easy, human-readable XML format, editing these entries can be cumbersome. To help with editing, Microsoft ships three tools:
One of the nice capabilities of the Visual Studio 2012 IDE is that it supports IntelliSense-based editing for configuration files, as shown in Figure 28-6.
The Visual Studio 2012 IDE also supports XML element syntax checking, as shown in Figure 28-7.
The Visual Studio 2012 IDE includes two new useful features via the XML toolbar options that can help you with formatting the configuration settings:
The Web Site Administration Tool and IIS Manager allow you to edit the configuration entries without knowing the XML element names and their corresponding values. Appendix D covers these tools in more detail.
In addition to using the web.config file as discussed, you can also extend it and add your own custom sections to the file that you can make use of just as with the other sections.
One way of creating custom sections is to use some built-in handlers that enable you to read key-value pairs from the .config file. All three of the following handlers are from the System.Configuration namespace:
Next, this chapter looks at each of these handlers and some programmatic ways to customize the configuration file.
If you are looking to create a custom section that behaves like the <appSettings> section of the web.config file, then using this handler is the way to go. Above the <system.web> section of the web.config file, make a reference to the NameValueFileSectionHandler object, along with the other default references you will find in an ASP.NET 4.5 application. Listing 28-43 shows this additional reference.
LISTING 28-43: Creating your own custom section of key-value pairs in the web.config file
<configSections>
<section name="MyCompanyAppSettings"
type="System.Configuration.NameValueFileSectionHandler, System,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
restartOnExternalChanges="false" />
</configSections>
After you have made this reference to the System.Configuration.NameValueFileSectionHandler object and have given it a name (in this case, MyCompanyAppSettings), you can create a section in your web.config file that makes use of this reference, as illustrated in Listing 28-44.
LISTING 28-44: Creating your own custom key-value pair section in the web.config
<configuration>
<MyCompanyAppSettings>
<add key="Key1" value="This is value 1" />
<add key="Key2" value="This is value 2" />
</MyCompanyAppSettings>
<system.web>
<!-- Removed for clarity -->
</system.web>
</configuration>
After you have this code in place within your web.config file, you can programmatically get access to this section, as illustrated in Listing 28-45.
LISTING 28-45: Getting access to your custom section in the web.config file
VB
Dim nvc As NameValueCollection = New NameValueCollection()
nvc = System.Configuration.ConfigurationManager.GetSection("MyCompanyAppSettings")
Response.Write(nvc("Key1") + "<br />")
Response.Write(nvc("Key2"))
C#
NameValueCollection nvc = new NameValueCollection();
nvc = ConfigurationManager.GetSection("MyCompanyAppSettings") as
NameValueCollection;
Response.Write(nvc["Key1"] + "<br />");
Response.Write(nvc["Key2"]);
For this to work, you must import the System.Collections.Specialized namespace into the file, because this is where you will find the NameValueCollection object.
The DictionarySectionHandler works nearly the same as the NameValueFileSectionHandler. The difference, however, is that the DictionarySectionHandler returns a HashTable object instead of returning an Object. Listing 28-46 presents this handler.
LISTING 28-46: Making a reference to the DictionarySectionHandler object
<configSections>
<section name="MyCompanyAppSettings"
type="System.Configuration.DictionarySectionHandler, System,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
restartOnExternalChanges="false" />
</configSections>
With this configuration setting in place, you can then make the same MyCompanyAppSettings section in the web.config file, as shown in Listing 28-47.
LISTING 28-47: Creating a custom key-value pair section in the web.config file
<configuration>
<MyCompanyAppSettings>
<add key="Key1" value="This is value 1" />
<add key="Key2" value="This is value 2" />
</MyCompanyAppSettings>
<system.web>
<!-- Removed for clarity -->
</system.web>
</configuration>
Now that the web.config file is ready, you can call the items from code using the Configuration API, as illustrated in Listing 28-48.
LISTING 28-48: Getting access to your custom section in the web.config file
VB
Dim nv As NameValueCollection = New NameValueCollection()
nv = System.Configuration.ConfigurationManager.GetSection("MyCompanyAppSettings")
Response.Write(nv("Key1") + "<br />")
Response.Write(nv("Key2"))
C#
NameValueCollection nv = new NameValueCollection();
nv = ConfigurationManager.GetSection("MyCompanyAppSettings") as NameValueCollection;
Response.Write(nv["Key1"] + "<br />");
Response.Write(nv["Key2"]);
The SingleTagSectionHandler works almost the same as the previous NameValueFileSectionHandler and DictionarySectionHandler. However, this object looks to work with a single element that contains the key-value pairs as attributes.
Listing 28-49 presents this handler.
LISTING 28-49: Making a reference to the SingleTagSectionHandler object
<configSections>
<section name="MyCompanyAppSettings"
type="System.Configuration.SingleTagSectionHandler, System,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
restartOnExternalChanges="false" />
</configSections>
With this configuration setting in place, you can make a different MyCompanyAppSettings section in the web.config file, as presented in Listing 28-50.
LISTING 28-50: Creating a custom key-value pair section in the web.config file
<configuration>
<MyCompanyAppSettings Key1="This is value 1" Key2="This is value 2" />
<system.web>
<!-- Removed for clarity -->
</system.web>
</configuration>
Now that the web.config file is complete, you can call the items from code using the Configuration API, as illustrated in Listing 28-51.
LISTING 28-51: Getting access to your custom section in the web.config file
VB
Dim ht As Hashtable = New Hashtable()
ht = System.Configuration.ConfigurationManager.GetSection("MyCompanyAppSettings")
Response.Write(ht("Key1") + "<br />")
Response.Write(ht("Key2"))
Dim ht As Hashtable = New Hashtable()
ht = System.Configuration.ConfigurationManager.GetSection("MyCompanyAppSettings")
Response.Write(ht("Key1") + "<br />")
Response.Write(ht("Key2"))
C#
Hashtable ht = new Hashtable();
ht = ConfigurationManager.GetSection("MyCompanyAppSettings") as Hashtable;
Response.Write(ht["Key1"] + "<br />");
Response.Write(ht["Key2"]);
You can also create your own custom configuration handler. To do this, you first must create a class that represents your section in the web.config file. In your App_Code folder, create a class called MyCompanySettings. Listing 28-52 shows this class.
LISTING 28-52: The MyCompanySettings class
VB
Public Class MyCompanySettings
Inherits ConfigurationSection
<ConfigurationProperty("Key1", DefaultValue:="This is the value of Key 1", _
IsRequired:=False)> _
Public ReadOnly Property Key1() As String
Get
Return MyBase.Item("Key1").ToString()
End Get
End Property
<ConfigurationProperty("Key2", IsRequired:=True)> _
Public ReadOnly Property Key2() As String
Get
Return MyBase.Item("Key2").ToString()
End Get
End Property
End Class
C#
using System.Configuration;
public class MyCompanySettings : ConfigurationSection
{
[ConfigurationProperty("Key1", DefaultValue = "This is the value of Key 1",
IsRequired = false)]
public string Key1
{
get
{
return this["Key1"] as string;
}
}
[ConfigurationProperty("Key2", IsRequired = true)]
public string Key2
{
get
{
return this["Key2"] as string;
}
}
}
You can see that this class inherits from the ConfigurationSection and the two properties that are created using the ConfigurationProperty attribute. You can use a couple of attributes here, such as the DefaultValue, IsRequired, IsKey, and IsDefaultCollection.
After you have this class in place, you can configure your application to use this handler, as illustrated in Listing 28-53.
LISTING 28-53: Making a reference to the MyCompanySettings object
<configSections>
<section name="MyCompanySettings" type="MyCompanySettings" />
</configSections>
You can now use this section in your web.config file, as illustrated in Listing 28-54.
LISTING 28-54: Creating your own custom key-value pair section in the web.config file
<configuration>
<configSections>
<!-- Removed for clarity -->
</configSections>
<MyCompanySettings Key2="Here is a value for Key2" />
<system.web>
<!-- Removed for clarity -->
</system.web>
</configuration>
Using this configuration you can programmatically access this key-value pair from code, as illustrated in Listing 28-55.
LISTING 28-55: Accessing a custom section in the web.config file
VB
Dim cs As MyCompanySettings = New MyCompanySettings()
cs = ConfigurationManager.GetSection("MyCompanySettings")
Response.Write(cs.Key1 + "<br />")
Response.Write(cs.Key2)
C#
MyCompanySettings cs = ConfigurationManager.GetSection("MyCompanySettings") as
MyCompanySettings;
Response.Write(cs.Key1 + "<br />");
Response.Write(cs.Key2);
By now, you should see how powerful the XML configuration system is. However, you may also be thinking of how many settings you’ll need to change or about the manual process of changing the config files upon deployment. Although there have been several solutions in the past besides these two manual processes, the ASP.NET team realized that something needed to change.
With ASP.NET 4 and Visual Studio 2010, web.config transforms were introduced. ASP.NET 4.5 continues to use this behavior. The transforms can be used with any configuration element or attribute, including the custom config sections covered in this chapter.
Web.config transforms are used with ASP.NET web applications. After creating a new ASP.NET web application, you’ll notice that the web.config file can be expanded and will include a web.Debug.config and web.Release.config file. Both of these files are the config transform files for the related configuration mode. Since each ASP.NET web application starts with the Debug and Release configuration mode, these files are created for you, as shown in Figure 28-8.
In Figure 28-8, you can see that there are two configuration modes. You can also see in the Solution Explorer that there are two additional web.config files, as mentioned a moment ago.
However, assume for a moment that you have another custom configuration process that corresponds with the new Publish features in Visual Studio 2012. This configuration process includes deploying to a staging server. You can add a new configuration mode called Staging by choosing the New option in the Configuration Manager window, as shown in Figure 28-8. Next, you can enter the new configuration mode name and copy any settings from another build configuration, as shown in Figure 28-9.
After you add the new configuration mode, the mode will be available in the Configuration Manager window, as shown in Figure 28-10.
At this point, you’ve added the new configuration mode. However, the transform has not yet been added. Visual Studio 2012 has a built-in method to assist you with adding the transform. If you right-click on the web.config file in Solution Explorer, you’ll see an option called Add Config Transform (shown in Figure 28-11).
After you add the config transform, you can see the corresponding file in Solution Explorer, as shown in Figure 28-12.
After opening one of the config transform files, such as the web.Staging.config file, you’ll notice that the file is essentially empty. Contained in this file are sample transforms and a single, active transform to remove the debug attribute, as illustrated in Listing 28-56.
LISTING 28-56: Browsing the web.Staging.config file
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit
http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated
Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>
The compilation configuration element contains a transform. This transform is possible because of the XML Document Transform namespace that has been added to the configuration root element. You’ll notice that the value for the transform attribute is RemoveAttributes(debug). The RemoveAttributes name is the transform method that will be used. In this case, this method will remove attributes from the configuration element. The value passed into the method is debug. This means that the attribute named debug will be removed from the configuration.
There are two attributes for the XML Document Transform namespace. The first, transform, is demonstrated in the transformation of the compilation configuration element. The possible methods for the transform attribute are:
The second attribute, location, is used to select a particular element. The possible methods for the location attribute are:
Using both the location and transform attributes can be necessary. For instance, you created a custom configuration section earlier in this chapter called MyCompanyAppSettings as a NameValueFileSectionHandler. When you are transforming the configuration file, you may need to replace a value for only one of the items in the collection. As an example, add the following to the MyCompanyAppSettings configuration section:
<add key="Key2" value="The is value 2 in staging"
xdt:Transform="SetAttributes(value)" xdt:Locator="Match(key)" />
After publishing to your staging server using the staging configuration mode, you should notice that your web.config file will look similar to Listing 28-57.
LISTING 28-57: The result of the config transform
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="MyCompanyAppSettings"
type="System.Configuration.NameValueFileSectionHandler, System,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
restartOnExternalChanges="false" />
</configSections>
<MyCompanyAppSettings>
<add key="Key1" value="This is value 1" />
<add key="Key2" value="The is value 2 in staging" />
</MyCompanyAppSettings>
<system.web>
<compilation targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
</configuration>
This practice is used frequently to replace the connectionString value for specific connections during a release deployment.
ASP.NET 4.5 includes a new feature to minify and bundle CSS and JavaScript within your web application. If you use any of the default project templates in Visual Studio 2012, this new feature is turned on.
Most static content files, such as CSS and JavaScript, contain a lot of whitespace and comments. Although the whitespace and comments do not necessarily take up significant space, depending on the client’s Internet speed, the additional space can noticeably slow the page load of the site. Figure 28-13 shows the network traffic before minification.
An example of a JavaScript snippet before minification can be found is as follows:
function helloWorld(firstName) {
// Declare variables
var message = "Hello, " + firstName;
// Display an alert message
alert(message);
}
In the past, one of the best techniques for compressing static content was to enable HTTP compression on the web server, such as IIS. The most common compression method was to gzip the static files. The issue with this process is that it is very difficult to customize which files to minify. In larger organizations, you may need to get an administrator involved to update the web server for you. In ASP.NET, you now have the ability to have complete control over the minification process without the need for an administrator.
To take the minification process a step further, you can also bundle a group of files, such as all CSS files, into a single minified path.
There are numerous ways to bundle and minify scripts and styles. One way to handle the bundling is to add a bundle.config file to the project and add the appropriate file listing in the file. However, you can also add the files to the bundle manually during the application start method.
To enable this feature in your projects, download the Microsoft ASP.NET Web Optimization Framework package from the package manager. After it’s downloaded and enabled, head right to the Global.asax file. Then, in the Global.asax file, include the namespace System.Web.Optimization. Finally, to bundle and minify CSS files located in the styles folder of your web, for example, add the following to the Application_Start method:
var cssBundle = new Bundle("~/styles/css");
cssBundle.IncludeDirectory("~/styles", "*.css");
BundleTable.Bundles.Add(cssBundle);
Like everything else in ASP.NET, developers can have complete control over the way CSS and JavaScript are bundled. This includes creating custom transforms, including and excluding specific files, and creating multiple bundles. It is important to note that the files are minified and added in the order they are listed. If a script file is dependent upon another file, it’s best to move the dependent file to a bundle above.
Figure 28-14 shows the network traffic after minification.
The JavaScript snippet from earlier, when compressed, will render like so:
function helloWorld(n){var t="Hello, "+n;alert(t)}
In this chapter, you have seen the ASP.NET configuration system and learned how it does not rely on the IIS metabase. Instead, ASP.NET uses an XML configuration system that is human-readable.
You also looked at the two different ASP.NET XML configuration files:
The machine.config file applies default settings to all web applications on the server. However, if the server has multiple versions of the framework installed, the machine.config file applies to a particular framework version. On the other hand, a particular web application can customize or override its own configuration information using web.config files. Using a web.config file, you can also configure the applications on an application-by-application or folder-by-folder basis.
Next, you looked at some typical configuration settings that you can apply to an ASP.NET application, such as configuring connection strings, session state, browser capabilities, and so on. Then you looked at protecting the configuration section using cryptographic algorithms. You also reviewed replacing configuration values upon deployment. Finally, you looked at minifying your scripts and CSS files and placing those files into a bundle.