| Украина, Киев |
Управление состоянием страниц на клиенте
Упражнение 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 />,
система пропускает все управляющие символы и отображает их
в полях в кодированном виде
Результат после восстановления ранее сохраненной в состоянии вида информации будет примерно таким
