Иерархическая модель настроек
Как говорилось в лекции 1, модель настроек ASP.NET наследует параметры от сервера к приложению при помощи иерархической модели. Благодаря иерархической модели можно указать параметры в файле machine.config или web.config родительского приложения, и эти параметры будут переданы во все дочерние приложения. Дочерние приложения наследуют настройки от родительских приложений, и все приложения наследуют настройки из файла machine.config. Вы можете указать параметры для всего сервера, одного или нескольких приложений, одной или нескольких директорий и даже одного файла. При наследовании настроечных параметров используются следующие правила.
Приложения сначала наследуют настройки из файла machine.config сервера, затем – из файла web.config родительского приложения и, наконец, из своего собственного файла web.config.Параметры в каждом файле web.config переопределяют параметры из файлов machine.config и web.config, стоящих перед ним.Наследование следует адресу URL запрошенного ресурса и не обязательно следует физической структуре файлов.Параметры из файла machine.config или файла web.config родительского приложения могут запретить переопределение параметров.С помощью установки расположения параметры могут относиться к конкретной директории, приложению или файлу.
Для наглядной демонстрации правил наследования давайте взглянем на сценарий, включающий несколько директорий и приложений. Для этого примера используются три приложения: App1, App2 и App3. На рисунке 2.1 показана виртуальная структура этих приложений.
Дополнительная информация. Виртуальная структура приложений – это структура, представленная при помощи инструмента администрирования IIS. Эта структура не связана с физическим расположением директорий и просто показывает, как вы настраиваете приложения в IIS.Рис. 2.1. Виртуальная структура приложений
На рисунке 2.1 App1 и App3 – это корневые приложения, а App2 – дочернее приложение App1.
Дополнительная информация. App3 в IIS представляет собой виртуальную директорию.Виртуальная директория – это указатель из IIS на реальную физическую директорию на диске. Остальные приложения не являются виртуальными директориями, так как в физической структуре они находятся в одном и том же месте wwwroot, как и в виртуальной структуре.
Наследование в иерархической модели сначала берет во внимание виртуальную структуру, а затем – физическую структуру приложений. На рисунке 2.2 показана физическая структура приложений. Мы видим, что App1 – это поддиректория в директории wwwroot, а App2 и App3 –поддиректории App1. SubDir также является поддиректорией App1, но не является приложением или виртуальной директорией в IIS.
Включите трассировку в файле machine.config на сервере, следуя шагам, описанным в разделе "Редактирование настроечных файлов" в лекции 1. Сделав это изменение, вы включите трассировку для всех приложений и файлов .aspx на этом сервере.Отключите трассировку в файле web.config для App1 – первого веб-приложения.
Рис. 2.2. Физическая структура приложений
При доступе к указанным URL вы увидите следующие результаты. В этом примере в качестве имени сайта взято http://www.northwindtraders.com/. При тестировании замените его именем вашего сайта.
http://www.northwindtraders.com/App1. Этот URL наследует включенную трассировку из файла machine.config, но параметр в файле web.config для App1, отключающий ее, переопределяет данный параметр. Следовательно, для указанного URL трассировка отключена.http://www.northwindtraders.com/App1/App2. App2 является в виртуальной структуре дочерним приложением App1, и трассировка здесь также отключена, поскольку App2 наследует файл web.config для App1, а в файле web.config для App2 переопределение этого параметра отсутствует.http://www.northwindtraders.com/App1/SubDir. Для всех файлов в директории SubDir трассировка отключена, так как они наследуют этот параметр от файла web.config для App1.http://www.northwindtraders.com/App3. Для App3 трассировка включена, так как оно наследует только от файла machine.config сервера.
Хотя в физической структуре это приложение дочернее по отношению к App1, оно не является дочерним в виртуальной структуре.http://www.northwindtraders.com/App1/App3. App3 – это самостоятельное приложение, но оно является поддиректорией App1 и доступ к нему может осуществляться по этому пути. В результате трассировка для App3 отключена, так как оно наследует данный параметр из файла web.config для App1.Замечание. Заметили интересную особенность? Одна директория или приложение могут иметь несколько вариантов настроечных параметров в зависимости от того, по какому URL производится доступ к этому ресурсу.
URL ресурса определяет, какие настроечные параметры будет наследовать ресурс. Другими словами, одни и те же ресурсы наследуют различные настроечные параметры в зависимости от того, какой URL используется для доступа к ним. Поняв то, как настроечные параметры наследуются от сервера к приложениям, попробуем указать, к чему относятся настроечные параметры.
Изучение обработчиков разделов настроек
Итак, я уже описал, где следует вводить параметры, как эти параметры наследуются, как указать, к чему они относятся, и как их заблокировать. Теперь давайте рассмотрим, как используются настроечные параметры. Обработчики разделов настроек обрабатывают параметры, указанные в настроечных файлах, и делают их доступными для приложений. Обработчики разделов настроек – это классы, которые реализуют интерфейс IConfigurationSectionHandler. Данные классы интерпретируют и обрабатывают параметры из настроечного файла и возвращают объект конфигурации, основанный на этих параметрах.
Как уже было сказано ранее, обработчики разделов настроек сначала объявляются в configSections. Давайте снова посмотрим на объявление обработчика настроек.
<section name="appSettings" type="System.Configuration.NameValueFileSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<appSettings> <add key="MyPassword" value="Secret" /> <add key="ServerIP" value="192.168.31.1" /> </appSettings>
Это объявление раздела <appSettings> настроечного файла. Данный раздел используется для хранения пар имя/значение, к которым нужно получать доступ из любого файла приложения. Данное объявление указывает настроечному файлу на класс, который будет обрабатывать этот раздел. Вот пример раздела <appSettings>, который добавляет еще две пары имя/значение.
<appSettings> <add key="MyPassword" value="Secret"> <add key="ServerIP" value="192.168.31.1"> </appSettings>
Теперь доступ к этим параметрам можно получить из приложений, что значительно облегчает отслеживание и изменение параметров, используемых на различных страницах или в приложениях. Мы знаем, где располагается объявление обработчика, но еще не увидели реального кода .NET Framework для объявления обработчика. Взглянуть на пример обработчика можно в исходном коде Microsoft Shared Source CLI Implementation (кодовое имя: Rotor), который является замечательной вещью.
Дополнительная информация. Microsoft Shared Source CLI Implementation, известная как Rotor, – это реализация с открытыми исходными текстами значительной части .NET Framework, доступная для разработчиков. Это не совсем тот код, который используется в .NET Framework, но он очень похож на него и выполняет те же функции. Однако между ними имеются многочисленные отличия, так как код Rotor разработан для того, чтобы быть наиболее переносимым и читаемым. За дополнительной информацией обратитесь по адресу http://msdn.microsoft.com/downloads/default.asp?url=/downloads/topic.asp?URL=/MSDN-FILES/028/000/123/topic.xml.
Следующий код – это обработчик раздела настроек для раздела <appSettings>, который называется NameValueFileSectionHandler. Я удалил из него код, не имеющий отношения к этому обсуждению.
//——————————————————––––––––––––——————————————— // <copyright file="NameValueFileSectionHandler.cs" company="Microsoft"> // // Copyright (c) 2002 Microsoft Corporation All rights reserved. // The use and distribution terms for this software are contained in the // file named license.txt, which can be found in the root of this // distribution. By using this software in any fashion, you are agreeing // to be bound by the terms of this license // You must not remove this notice, or any other, from this software. // // </copyright> //——————————————————––––––––––––——————————————— #if !LIB namespace System.Configuration { using System.IO; using System.Xml; public class NameValueFileSectionHandler : IConfigurationSectionHandler { public object Create(object parent, object configContext, XmlNode section) { object result = parent; // parse XML XmlNode fileAttribute = section.Attributes.RemoveNamedItem("file"); result = NameValueSectionHandler.CreateStatic(result, section); if (fileAttribute != null && fileAttribute.Value.Length != 0) { /* Удалено для краткости. Этот раздел будет срабатывать, если имеется свойство file="", расположенное в разделе appSettings */ } return result; } } } #endif
Данный обработчик проверяет, имеется ли свойство file=", а затем, если это свойство пустое, возвращает значение, основанное на вызове метода NameValueSectionHandler.CreateStatic(). Это метод, создающий объект конфигурации. Ниже приведен код NameValueSectionHandler.
//———————————————————————————––––––––––––—————— // <copyright file="NameValueSectionHandler.cs" company="Microsoft"> // // Copyright (c) 2002 Microsoft Corporation All rights reserved. // The use and distribution terms for this software are contained in the // file named license.txt, which can be found in the root of this // distribution. By using this software in any fashion, you are agreeing // to be bound by the terms of this license // You must not remove this notice, or any other, from this software. // // </copyright> //——————————––––––––––––——————————————————————— #if !LIB namespace System.Configuration { using System.Collections; using System.Collections.Specialized; using System.Xml; using System.Globalization; public class NameValueSectionHandler : IConfigurationSectionHandler { const string defaultKeyAttribute = "key"; const string defaultValueAttribute = "value"; public object Create(object parent, object context, XmlNode section) { return CreateStatic(parent, section, KeyAttributeName, ValueAttributeName); } internal static object CreateStatic(object parent, XmlNode section) { return CreateStatic(parent, section, defaultKeyAttribute, defaultValueAttribute); } internal static object CreateStatic(object parent, XmlNode section, string keyAttriuteName, string valueAttributeName) { ReadOnlyNameValueCollection result; // start result off as a shallow clone of the parent if (parent == null) result = new ReadOnlyNameValueCollection(new CaseInsensitiveHashCodeProvider( CultureInfo.InvariantCulture), new CaseInsensitiveComparer( CultureInfo.InvariantCulture)); else { ReadOnlyNameValueCollection parentCollection = (ReadOnlyNameValueCollection)parent; result = new ReadOnlyNameValueCollection(parentCollection); } // process XML HandlerBase.CheckForUnrecognizedAttributes(section); foreach (XmlNode child in section.ChildNodes) { // skip whitespace and comments if (HandlerBase.IsIgnorableAlsoCheckForNonElement( child)) continue; // handle <set>, <remove>, <clear> tags if (child.Name == "add") { String key = HandlerBase.RemoveRequiredAttribute( child, keyAttriuteName); String value = HandlerBase.RemoveRequiredAttribute( child, valueAttributeName, true /*allowEmptyString*/); HandlerBase.CheckForUnrecognizedAttributes(child); result[key] = value; } else if (child.Name == "remove") { String key = HandlerBase.RemoveRequiredAttribute( child, keyAttriuteName); HandlerBase.CheckForUnrecognizedAttributes(child); result.Remove(key); } else if (child.Name.Equals("clear")) { HandlerBase.CheckForUnrecognizedAttributes(child); result.Clear(); } else { HandlerBase.ThrowUnrecognizedElement(child); } } result.SetReadOnly(); return result; } protected virtual string KeyAttributeName { get { return defaultKeyAttribute;} } protected virtual string ValueAttributeName { get { return defaultValueAttribute;} } } } #endif
Данный код создает объект коллекции настроек, содержащий пары различных ключей и значений из раздела настроек <appSettings>. Я не буду тратить много времени на объяснение кода обработчика, но давайте рассмотрим несколько наиболее важных пар. Метод CreateStatic() вызывается из первого обработчика раздела и создает объект конфигурации ReadOnlyNameValueCollection, который будет хранить всю информацию о парах различных ключей и значений. Давайте посмотрим на код, который используется для разбора XML.
if (child.Name == "add") { String key = HandlerBase.RemoveRequiredAttribute(child, keyAttriuteName); String value = HandlerBase.RemoveRequiredAttribute(child, valueAttributeName, true/*allowEmptyString*/); HandlerBase.CheckForUnrecognizedAttributes(child); result[key] = value; }
Если имя дочернего элемента XML равно "add", то этот код добавляет в объект типа ReadOnlyNameValueCollection с именем result пару ключ/значение. Когда вы в своем коде ссылаетесь на коллекцию <appSettings>, то осуществляете доступ к этой коллекции и обращаетесь к значениям, сохраненным в этом разделе.
Обработчики разделов настроек интерпретируют все параметры из конкретных разделов и делают их доступными для приложений. В лекции 9 описывется, как создавать собственные обработчики разделов настроек для интерпретации параметров из собственных разделов.
Ключевые моменты
Иерархическая модель настроек организует различные виды наследования от сервера к приложению и от одного приложения к другому. Это наследование основано на URL, который используется для доступа к требуемому ресурсу.Для указания того, что настроечные параметры относятся к конкретной директории или даже одному файлу, используется тег location.Для запрета переопределение параметров другими файлами web.config используется параметр allowOverride.Обработчики разделов настроек делают настройки конфигурации доступными для приложений, обрабатывая XML и возвращая объект конфигурации.
Позиционирование настроечных параметров
С помощью тега location можно связать параметры с конкретным путем, приложением или файлом. Это особенно полезно, если вы планируете использовать для физической директории различные параметры, но не хотите (или не должны) делать эту директорию приложением. Например, требуется, чтобы одна директория имела различные параметры трассировки. Используйте тег location и добавьте в файл web.config для App1 следующий текст.
<location path="SubDir"> <system.web> <trace enabled="false" /> </system.web> </location>
<location path="notracepage.aspx"> <system.web> <trace enabled="false" /> </system.web> </location>
Данный способ отключает трассировку для всех файлов в директории SubDir.
Замечание. Обратите внимание, что вам все равно требуется включить в тег location группу разделов system.web. Всегда при использовании тега location должно быть включено имя группы разделов.Трассировка будет также отключена для всех поддиректорий директории SubDir. Вы можете использовать данную процедуру для назначения настроечных параметров одному файлу. Это приведет к отключению трассировки для файла notracepage.aspx, расположенного в корневой директории App1.
Пример из практики. Использование тега location оказывается полезным, когда требуется задать права доступа для одного файла приложения. Помните, что это работает только для файлов, обрабатываемых ASP.NET, то есть вы не можете применить настроечные параметры к файлам html или графическим файлам, поскольку они обрабатываются IIS, а не ASP.NET.Запрет переопределения настроечных параметров
Оказывается, можно переопределить параметры в файле web.config приложения. Однако бывают ситуации, когда необходимо запретить переопределять параметры. К счастью, есть параметр, позволяющий это сделать.
Если в нашем примере установить параметр allowOverride в файле machine.config или web.config в значение false, то независимо от того, что вы укажете в наследующем файле web.config приложения, трассировка всегда будет отключена.
<system.web> <trace enabled="false" allowOverride="false" /> </system.web> <location path="SubDir" allowOverride="false > <system.web> <trace enabled="false" /> </system.web> </location>
Можно использовать этот параметр в сочетании с тегом location.
<location path="SubDir" allowOverride="false" > <system.web> <trace enabled="false"> </system.web> </location>
Пример из практики. Запрет на переопределение настроечных параметров особенно полезен, если вы являетесь администратором или работаете в компании, предоставляющей услуги хостинга. Он позволяет установить глобальные параметры для вашего сервера, которые не могут быть изменены вашими клиентами для отдельных приложений.Помните, что при использовании свойства allowOverride="false" любое приложение, которое пытается переопределить этот параметр, сгенерирует ошибку конфигурации. Перед тем как задавать параметр allowOverride="false", убедитесь, что ни одно из приложений не пытается установить это значение – иначе вы нарушите их работу.