Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 35:

Управление состоянием страниц на клиенте

Упражнение 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 для случая, когда настройки допускают двойственное размещение.

  • Дополните файл конфигурации web.config секцией <pages> с отключенной проверкой достоверности
<?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 />, система пропускает все управляющие символы и отображает их в полях в кодированном виде

Результат после восстановления ранее сохраненной в состоянии вида информации будет примерно таким

  • Удалите из секции <system.web> файла Web.config параметр отключения проверки достоверности <pages validateRequest="false" /> и включите его как атрибут ValidateRequest="false" в состав директивы @ Page страницы ViewStateObjects.aspx, чтобы он действовал только на одну эту страницу
Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000