Украина, Киев |
Управление состоянием страниц на клиенте
Упражнение 2. Программирование ViewState страницы с применением объекта Hashtable
Это упражнение подобно предыдущему, но в нем значения текстовых полей прежде поэлементно сохраняются в промежуточном объекте Hashtable, а затем этот объект целиком сохраняется в свойстве объекта страницы ViewState. Интерфейсная часть страницы остается в точности такой-же, как в предыдущем упражнении, а кодовая часть изменится. При этом, чтобы не менять интерфейсную часть, мы динамически (в кодовой части) добавим в конце коллекции формы form1 текстовую метку для отображения на клиенте содержимого промежуточного объекта-словаря.
- Через панель Solution Explorer создайте копию страницы ViewStateTest.aspx и присвойте ей имя ViewStateObjects.aspx
- Измените имя класса ViewStateTest на ViewStateObjects в кодовой и интерфейсной частях скопированной страницы
- Заполните кодовую часть страницы следующим образом
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class ViewStateObjects : System.Web.UI.Page { // Создаем экземпляр промежуточного словаря Hashtable dictionary = new Hashtable(); protected void cmdSave_Click(object sender, EventArgs e) { // Сохраняем присланные значения текстовых полей в словаре SaveAllText(Table1.Controls); // Сохраняем помеченный словарь целиком в состоянии вида this.ViewState["contentsState"] = dictionary; } private void SaveAllText(ControlCollection controls) { // Перебираем все дочерние элементы в переданной коллекции таблицы foreach (Control control in controls) { // Выбираем элементы только текстовых полей и сохраняем // их содержимое в промежуточном объекте, используя // в качестве ключа идентификатор элемента if (control is TextBox) { // Значения полей перед сохранением на всякий случай // URL-кодируем как поступившие от ненадежного источника // (предохраняемся от возможного ввода зловредных скриптов). // Чтобы система не реагировала на угловые скобки в полях ввода, // нужно в директиву @ Page интерфейсной части вставить атрибут // ValidateRequest="false" или в секцию // <pages validateRequest="false"> файла web.config // Внимание!!! XML-файл web.config чувствителен к регистру символов string text = ((TextBox)control).Text; text = Page.Server.HtmlEncode(text); // То же самое... // text = HttpUtility.HtmlEncode(text); // Добавляем в словарь обезвреженный текст dictionary.Add(control.ID, text); } // На верхних уровнях объекта Table1 дочерними элементами // являются строки и ячейки таблицы, поэтому продолжаем // рекурсивно спускаться ниже, пока не встретим объекты TextBox if (control.Controls != null) { SaveAllText(control.Controls); } } } protected void cmdRestore_Click(object sender, EventArgs e) { // Извлекаем из состояния вида пришедший с // обратной отсылкой промежуточный объект и заполняем // из него текстовые поля сохраненными значениями RestoreAllText(); } // Если доступ к функции явно не объявлен, то по умолчанию считается private void RestoreAllText() { // Извлекаем помеченные ранее данные из состояния // вида в промежуточный объект-словарь dictionary = (Hashtable)this.ViewState["contentsState"]; if (dictionary == null) return; // Перебираем все элементы заполненного словаря и ищем на // странице соответствующее текстовое поле. Заодно заполняем // парами словаря текстовую метку для отсылки клиенту. Текстовую // метку создадим динамически и добавим в конец коллекции формы Label label = new Label(); form1.Controls.Add(label); label.Text = "<hr />" + "<h2>Значения словаря</h2>"; foreach (DictionaryEntry pair in dictionary) { // Выделяем ключ и значение для очередной извлеченной пары string key = pair.Key.ToString(); string value = pair.Value.ToString(); // Ищем на странице текстовое поле с соответствующим // идентификатором, уникальным в пределах страницы TextBox item = (TextBox)Page.FindControl(key); if (item != null) { // Если нашли, заполняем поле и текстовую метку item.Text = value; label.Text += string.Format("Key={0}, Value={1}", key, value); label.Text += "<br />"; } } } }Листинг 35.5. Содержимое файла ViewStateObjects.aspx.cs
Поскольку мы взяли на себя обеспечение безопасности данных, введенных пользователем, выполнением URL-кодирования управляющих символов.
- Запустите страницу и убедитесь, что она работает точно также, как и предыдущая без промежуточного объекта Hashtable
- Попробуйте вводить в поля какой-нибудь дескриптор, например, <br />
Мы видим, что встроенная защита ASP.NET прерывает выполнение приложения, считая введенный код потециально опасным. И мы фактически никогда не сможем испытать нашу систему URL-кодирования, если эту защиту не отключить. Отключить такую проверку самой системой ASP.NET можно, поместив соответствующую инструкцию в директиву @ Page страницы или в файл конфигурации в секцию <pages>.
Нужно строго учитывать регистр символов и в директиву @ Page атрибут помещать как ValidateRequest="false", а в конфигурационный файл как validateRequest="false". Это общее правило ASP.NET для случая, когда настройки допускают двойственное размещение.
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.web> <compilation debug="true" /> <pages validateRequest="false" /> </system.web> </configuration>Листинг 35.6. Файл web.config с отключенной проверкой достоверности
- Запустите приложение и убедитесь, что при введении опасного кода в текстовые поля, например, <br />, система пропускает все управляющие символы и отображает их в полях в кодированном виде
Результат после восстановления ранее сохраненной в состоянии вида информации будет примерно таким