Главная особенность удаленных приложений состоит в том, что они могут одновременно обрабатывать множество пользователей. К тому же сервер может выполнять одновременно несколько разных приложений. Поэтому среда исполнения устроена так, что после обработки запроса очередного пользователя память сервера немедленно очищается от кода запрошенной страницы, освобождая ресурсы для новых возможных
Такой факт вынуждает разработчиков Web-приложений изыскивать другие способы хранения информации о состоянии между запросами, с последующим ее извлечением и использованием. В ASP.NET существует два способа хранения информации между запросами: на клиенте и на сервере. Разработчик сайта сам решает, какую группу методов
Хранение информации на клиенте имеет следующие преимущества:
Хранение информации на сервере имеет следующие преимущества:
Различают следующие методы
view state ) - используется для сохранения значений элементов управления, добавленных конечным пользователем. Применение состояния вида основано на свойстве ViewState типа System.Web.UI.StateBag, наследуемом от класса Control любым элементом управления и самой страницей.hidden fields ) - POST (по щелчку кнопки Submit ), а не методом GET (по щелчку на query strings ) - cookie -файлы) - созданные на сервере данные записываются на компьютер клиента в небольших файлах (в количестве до 20 файлов и до 4 Кб каждый) и автоматически пересылаются Рассмотрим последовательно перечисленные методы хранения состояния на клиенте.
Это простой способ хранения информации на клиенте между обратными отсылками для одной и той же страницы. Вспомним, как обрабатывается страница на сервере. Для этого рассмотрим свежий отклик сервера, только что полученный
При поступлении обратной отсылки на сервер в память загружается адресуемая страница в своем первозданном виде, инициницализируются ее интерфейсные элементы начальными настройками, затем извлекается поступившая в
Все серверные Web-элементы управления используют состояние вида по умолчанию (не обязательно включать EnableViewState="true" ). В состав каждого из них входит свойство-коллекция , наследуемое от класса System.Web.UI.Control. Это же свойство напрямую наследует и класс Page, который мы расширяем при проектировании активной страницы. Оно создается и заполняется поступившими данными вместе с объектом страницы, а значит мы программно можем использовать это страничное свойство для сохранения и извлечения информации между запросами одной и той же страницы.
После обработки страницы ее текущее состояние, а также состояние элементов управления, автоматически хэшируется (maxPageStateFieldLength класса Page (по умолчанию равен -1 ). Для изменения начальных настроек объекта страницы значение этого свойства нужно поместить в
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation debug="true" />
<pages maxPageStateFieldLength="1024" />
</system.web>
</configuration>
Коллекция построена по типу словаря (Object, поэтому она способна хранить объекты любого типа. При извлечении из коллекции состояния вида данные нужно приводить к соответствующему типу. Если в коде попытаться извлечь значение, которого нет в коллекции, будет выдано исключение NullReferenceException, поэтому необходима предварительная проверка возвращаемой ссылки на null -значение.
Не все [, то порожденный этим из пространства имен System. также представляет собой словарь с полями Key и Value. Он поддается .
В качестве иллюстрации сказанного приведем 2 примера, где используется программирование словаря самой страницы.
В этом примере мы создадим форму с интерфейсными элементами, в которых отключим автоматическое сохранение состояния вида, а будем его сохранять и восстанавливать вручную. Интерфейсные элементы мы разместим в серверной таблице и будем использовать рекурсивную логику для опроса всех ее дочерних элементов, размещенных в коллекции Controls. В качестве ключа для доступа к элементам словаря уровня страницы будем использовать идентификаторы элементов.
Создайте пустой проект с именем
Добавьте к проекту новую Web-страницу с именем
Запустите командой Website/ASP.NET Configuration страницу WAT (Web Enable debugging Таким действием мы создадим файл конфигурации Web.config с настройкой
В панели Solution Explorer щелкните на кнопке Refresh, чтобы увидеть файл конфигурации в составе проекта
Откройте интерфейсную часть страницы ViewStateTest.aspx в режиме Source и добавьте в открывающую часть <div> внутренний стиль<div style="text-align: center">и заголовок
<h1 style="color: Red">Заполните анкету</h1>
Откройте интерфейсную часть страницы ViewStateTest.aspx в режиме Design и поместите на нее элемент Table из вкладки HTML панели Toolbox
Щелкните на элементе Table правой кнопкой мыши и выполните опцию Run As Server Control, чтобы превратить его в серверный
Откорректируйте интерфейсный (рис ) <table id="Table1" width="100%" runat="server" style="text-align: left; width: 500px;">
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
</table>
Поместите в первую строку и первый столбец таблицы приведенный ниже текст, а в остальные ячейки второго столбца - элемент TextBox из вкладки Standard панели Toolbox
Поместите после таблицы три кнопки Button из вкладки Standard панели Toolbox и присвойте им имена cmdSubmit, cmdSave и cmdRestore соответственноПосле этих действий интерфейс страницы в режиме проектирования будет таким

Ему соответствует такой дескрипторный код
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="ViewStateTest.aspx.cs" Inherits="ViewStateTest" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<h1 style="color: Red">Заполните анкету</h1>
<table id="Table1" runat="server" style="text-align: left; width: 500px;">
<tr>
<td style="width: 200px">
<b>Наименование</b></td>
<td>
<b>Значение</b></td>
</tr>
<tr>
<td style="width: 200px">
Имя:</td>
<td>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
Учетный номер:</td>
<td>
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
Возраст:</td>
<td>
<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
E-mail:</td>
<td>
<asp:TextBox ID="TextBox4" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
Пароль:</td>
<td>
<asp:TextBox ID="TextBox5" runat="server"></asp:TextBox></td>
</tr>
</table>
<br />
<asp:Button ID="cmdSubmit" runat="server" Text="Отправить" />
nbsp;nbsp;nbsp; nbsp;
<asp:Button ID="cmdSave" runat="server" Text="Сохранить" />
nbsp;nbsp;nbsp; nbsp;
<asp:Button ID="cmdRestore" runat="server" Text="Восстановить" />
</div>
</form>
</body>
</html>

Откройте файл отделенного кода и заполните его следующим кодом(рис ) Содержимое файла ViewStateTest.aspx.csusing 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 ViewStateTest : System.Web.UI.Page
{
private bool executeFlag = true;
protected void cmdSave_Click(object sender, EventArgs e)
{
// Сохраняем присланные значения текстовых полей
SaveAllText(Table1.Controls, executeFlag);
}
private void SaveAllText(ControlCollection controls, bool saveNested)
{
if (!saveNested)
return;
// Перебираем все дочерние элементы в переданной коллекции таблицы
foreach (Control control in controls)
{
// Выбираем элементы текстовых полей и сохраняем
// их содержимое в свойстве ViewState объекта страницы,
// используя в качестве ключа идентификатор элемента
if (control is TextBox)
{
this.ViewState[control.ID] = ((TextBox)control).Text;
}
// На верхних уровнях объекта Table1 дочерними элементами
// являются строки и ячейки таблицы, поэтому продолжаем
// рекурсивно спускаться ниже, пока не встретим объекты TextBox.
// Флаг saveNested разрешает проверять вложенные уровни коллекций
if (control.Controls != null)
{
SaveAllText(control.Controls, saveNested);
}
}
}
protected void cmdRestore_Click(object sender, EventArgs e)
{
// Извлекаем пришедший с обратной отсылкой сохраненный текст предыдущей
// обратной отсылки и вставляем его в текстовые поля, готовя новый отклик
RestoreAllText(Table1.Controls, executeFlag);
}
void RestoreAllText(ControlCollection controls, bool restoreNested)
{
if (!restoreNested)
return;
// Перебираем все элементы таблицы, включая вложенные, чтобы
// использовать их идентификаторы как ключи для извлечения
// возможно сохраненных значений из словаря ViewState
foreach (Control control in controls)
{
// Нашли текстовое поле
if (control is TextBox)
{
// Убеждаемся, что есть в словаре
if (this.ViewState[control.ID] != null)
{
// Приводим типы и восстанавливаем
((TextBox)control).Text = (string)this.ViewState[control.ID];
}
}
// Рекурсивно спускаемся ниже к дочерним элементам
if (control.Controls != null)
{
RestoreAllText(control.Controls, restoreNested);
}
}
}
}
Испытайте работу страницы и убедитесь, что в любое время можно восстановить состояние элементов управления, сохраненное ранее в словаре 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
{
private bool executeFlag = true;
// Создаем экземпляр промежуточного словаря
Hashtable dictionary = new Hashtable();
protected void cmdSave_Click(object sender, EventArgs e)
{
// Сохраняем присланные значения текстовых полей в словаре
SaveAllText(Table1.Controls, executeFlag);
// Сохраняем помеченный словарь целиком в состоянии вида
this.ViewState["contentsState"] = dictionary;
}
private void SaveAllText(ControlCollection controls, bool saveNested)
{
if (!saveNested)
return;
// Перебираем все дочерние элементы в переданной коллекции таблицы
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.
// Флаг saveNested разрешает проверять вложенные уровни коллекций
if (control.Controls != null)
{
SaveAllText(control.Controls, saveNested);
}
}
}
protected void cmdRestore_Click(object sender, EventArgs e)
{
// Извлекаем из состояния вида пришедший с
// обратной отсылкой промежуточный объект и заполняем
// из него текстовые поля сохраненными значениями
RestoreAllText(executeFlag);
}
// Если доступ к функции явно не объявлен, то по умолчанию считается private
void RestoreAllText(bool restoreNested)
{
if (!restoreNested)
return;
// Извлекаем помеченные ранее данные из состояния
// вида в промежуточный объект-словарь
dictionary = (Hashtable)this.ViewState["contentsState"];
// Перебираем все элементы заполненного словаря и ищем на
// странице соответствующее текстовое поле. Заодно заполняем
// парами словаря текстовую метку для отсылки клиенту. Текстовую
// метку создадим динамически и добавим в конец коллекции формы
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 />";
}
}
}
}
Поскольку мы взяли на себя проверку достоверности данных, введенных пользователем, то нужно отключить такую проверку системой ASP.NET при считывании запроса, поместив соответствующую инструкцию в @ Page страницы или в файл конфигурации в секцию <pages>. При этом нужно строго учитывать регистр символов и в @ Page атрибут помещать таким ValidateRequest="false", а в validateRequest="false". Это общее правило ASP.NET для случая, когда настройки допускают двойственное размещение.
Дополните файл конфигурации web.config секцией <pages> с отключенной проверкой достоверности(рис ) Файл web.config с отключенной проверкой достоверности<?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>
Запустите приложение и убедитесь в его работоспособностиРезультат после восстановления ранее сохраненной в состоянии вида информации будет примерно таким

Удалите из секции <system.web> файла Web.config параметр отключения проверки достоверности <pages validateRequest="false" /> и включите его как атрибут ValidateRequest="false" в состав @ Page страницы ViewStateObjects.aspx, чтобы он действовал только на одну эту страницуИногда состояние вида в элементе полезно отключить, чтобы уменьшить нагрузку на каналы связи. Но для некоторых элементов это нарушает их нормальную работу. Возьмем, например, списки ListBox и DropDownList. Когда они заполнены большим количеством элементов, объем пересылаемой информации состояния вида очень большой и это свойство лучше отключить. Для восстановления состояния выделенного страницы. Проверим это на примере.
Добавьте к проекту страницу с именем ViewStateList.aspx без файла отделенного кода и назначьте ее стартовой
Оформите страницу так, чтобы код ее интерфейсной части был следующим(рис ) Код интерфейсной части страницы ViewStateList.aspx<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<h2>Отключение состояния вида в списках</h2>
<asp:ListBox ID="ListBox1" runat="server" Height="200px" Width="150px" />
nbsp; nbsp; nbsp; nbsp;
<asp:DropDownList ID="DropDownList1"
runat="server" Width="150px">
</asp:DropDownList><br />
<br />
<asp:Button ID="Button1" runat="server" Text="Отправить" /></div>
</form>
</body>
</html>
Этому коду в режиме проектирования соответствует следующее представление

Page_Load()
Поместите в обработчик Page_Load() код заполнения списков видимым содержимым, который будет срабатывать при каждом запросе страницы(рис ) Обобработчик Page_Load() для заполнения списков<script runat="server">
// Поле класса страницы
private const int maxCount = 1000;
protected void Page_Load(object sender, EventArgs e)
{
// Заполняем списки программной генерацией значений
for (int i = 0; i < maxCount; i++)
{
ListBox1.Items.Add("Опция " + (i + 1).ToString());
DropDownList1.Items.Add("Опция " + (i + 1).ToString());
}
}
</script>
Выполните страницу и убедитесь, что при каждом новом запросе по щелчку на кнопке Submit отмеченные пользователем опции списков возвращаются сервером сохраненнымиПока что это работает механизм автоматического сохранения состояния вида ASP.NET. Но давайте посмотрим, какой при этом получается объем пересылаемой информации, именно касающийся работы этого механизма. Для этого включим работу ASP.NET в режиме выдачи трассировки на индивидуальных страницах. Это можно сделать, если добавить атрибут в @ Page страницы, либо через утилиту , запустив ее командой Website/ASP.NET Configuration (или щелкнуть на пиктограмме в панели Solution Explorer ).
Запустите утилиту WAT , пойдите последовательно по ссылкам Application Configuration/Configure debugging and tracing и включите флажки "Capture tracing information" и "Display tracing information on ondividual pages". Закройте утилитуВ результате этих действий файл конфигурации примет вид
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<trace enabled="true" pageOutput="true" />
<compilation debug="true" />
</system.web>
</configuration>
Вновь запустите страницу ViewStateList.aspx, поманипулируйте с интерфейсными элементами, выполните обратную отсылку и проанализируйте секцию Control Tree в результатах трассировки, которая будет, примерно, такой| Control UniqueID | Type | ControlState | ||
|---|---|---|---|---|
| __Page | 910866 | 0 | 0 | |
| ctl02 | System.Web.UI.LiteralControl | 50 | 0 | 0 |
| ctl00 | System.Web.UI.HtmlControls.HtmlHead | 46 | 0 | 0 |
| ctl01 | System.Web.UI.HtmlControls.HtmlTitle | 33 | 0 | 0 |
| ctl03 | System.Web.UI.LiteralControl | 14 | 0 | 0 |
| form1 | System.Web.UI.HtmlControls.HtmlForm | 910736 | 0 | 0 |
| ctl04 | System.Web.UI.LiteralControl | 129 | 0 | 0 |
| ListBox1 | System.Web.UI. |
227258 | 196036 | 0 |
| ctl05 | System.Web.UI.LiteralControl | 47 | 0 | 0 |
| DropDownList1 | System.Web.UI. |
227246 | 196036 | 0 |
| ctl06 | System.Web.UI.LiteralControl | 32 | 0 | 0 |
| Button1 | System.Web.UI. |
78 | 0 | 0 |
| ctl07 | System.Web.UI.LiteralControl | 12 | 0 | 0 |
| ctl08 | System.Web.UI.LiteralControl | 20 | 0 | 0 |
Объем пересылаемой по каналам связи в оба конца информации получается огромным до неприличия. Причем с каждой новой отсылкой он катастрофически нарастает, добавляя к прежним сохраненным значениям сгенерированные в коде новые значения списков. Отключим сохранение состояния вида в списках, а для восстановления позиций выделенных пользователем опций воспользуемся тем фактом, что с каждым запросом эти значения передаются на сервер в словарь объекта страницы.
Перейдите в режим Design редактора страницы ViewStateList.aspx, выделите одновременно оба списка и через панель Properties сбросьте свойства EnableViewState
Запустите страницу ViewStateList.aspx и убедитесь, что объем сохраняемого состояния вида для списков стал нулевымЗамечание. Если количество запросов превысит заданное параметром трассировки requestLimit значение (по умолчанию 10), то ASP.NET при исполнении не будет включать результаты трассировки в страницу. В этом случае нужно при очередном запуске тестового браузера вызвать через строку адреса страницу trace.axd и щелкнуть на ссылке " этой страницы. Включить безлимитную трассировку можно более просто - добавить в @ Page страницы параметр
Мы видим, что при отключении сохранения состояния вида резко уменьшился и общий объем страницы. Но сервер в своих откликах не сохраняет выбранные клиентом опции списков. Исправим этот недостаток программно.
Добавьте к странице код восстановления выбора пользователя в списках<script runat="server">
// Поле класса страницы
private const int maxCount = 1000;
protected void Page_Load(object sender, EventArgs e)
{
// Заполняем списки программной генерацией значений
for (int i = 0; i < maxCount; i++)
{
ListBox1.Items.Add("Опция " + (i + 1).ToString());
DropDownList1.Items.Add("Опция " + (i + 1).ToString());
}
// Отслеживание выделенных пользователем опций списков
if (this.IsPostBack)
{
string text = this.Request.Form["ListBox1"];
ListBox1.Items.FindByText(text).Selected = true;
text = this.Request.Form["DropDownList1"];
DropDownList1.Items.FindByText(text).Selected = true;
}
else
ListBox1.SelectedIndex = DropDownList1.SelectedIndex = 0;
}
</script>
< из
<select size="4" name="ListBox1" id="ListBox1" style="height:200px;width:150px;"> <option selected="selected" value="Опция 1">Опция 1</option> <option value="Опция 2">Опция 2</option> <option value="Опция 3">Опция 3</option> .................................................. </select>
Фактически ListBox1=Опция 1, которая попадает в словарь , доступный для извлечения и использования в коде страницы на стороне сервера. Мы находим это значение в сгенерированной коллекции значений списка и настраиваем этот элемент коллекции как выделенный. После таких настроек список сгенерирует в текущем отклике сервера нужный
Запустите страницу ViewStateList.aspx с включенной трассировкой и убедитесь, что выбранные опции списков сохраняют свои позиции и ASP.NET генерирует небольшой объем клиентского кодаТаким образом мы выяснили, что состояние вида элементов управления, которое включено по умолчанию, не всегда является полезным либо просто не используется, и тогда его лучше отключать. Отключить состояние вида можно следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation debug="true" />
<pages enableViewState="false" />
</system.web>
</configuration>
@ Page параметр EnableViewState="false" (в коде свойство Page.EnableViewState = false )EnableViewState="false" (в коде свойство Элемент.EnableViewState = false )Если более общая настройка сохранения состояния вида выключена, а для каких-то страниц или элементов управления ее нужно включить, то в соответствующее место следует добавить атрибут EnableViewState="true".
Состояние вида передается клиенту системой ASP.NET в
Добавьте к проекту новую страницу (без файла отделенного кода) с именем DisplayViewState.aspx и установите ее стартовой
Удалите из интерфейсной части страницы <div></div>
Включите трассировку, добавив в @ Page страницы параметр Trace =true
Добавьте в блок скриптов кодовую часть, чтобы окончательно страница стала такой(рис ) Окончательный код страницы DisplayViewState.aspx<%@ Page Language="C#" Trace="true" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Объекты создаем каждый раз заново
Label label = new Label();
this.form1.Controls.Add(label);
this.form1.Controls.Add(new HtmlGenericControl("br"));
Button button = new Button();
this.form1.Controls.Add(button);
if (!this.IsPostBack)
{
// Один раз настроили, а дальше работает состояние вида
label.Text = "Привет всем!!!";
button.Text = "Отправить";
}
}
protected void Page_PreRender(object sender, EventArgs e)
{
// Вывод на страницу строки состояния
if (this.IsPostBack)
{
// Извлекаем строку состояния
string strViewState = this.Request.Form["__VIEWSTATE"];
// Добавляем вывод к родительской форме
Label label = new Label();
this.form1.Controls.Add(label);
label.Text = "<p><b>Строка состояния как есть:</b><br />"
+ strViewState + "</p>";
label.EnableViewState = false;// Чтобы не влияла на результат
// Преобразуем строку из Base64 в массив ASCII-символов
byte[] ascii = Convert.FromBase64String(strViewState);
// Вставляем в отклик
Response.Write("<b>Строка состояния в формате ASCII:</b><br />");
Response.BinaryWrite(ascii);
// То же самое, только по турецки...
// Десериализация и отображение строки
//string decodedViewState = System.Text.Encoding.ASCII.GetString(ascii);
//Response.Write(decodedViewState);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Запустите страницу DisplayViewState.aspx и повыполните обратную отсылкуHTML-отклик на стороне клиента, сгенерированный страницей DisplayViewState.aspx при обратной отсылке, будет выглядеть примерно так

Мы видим, что некоторая информация, сохраняемая на клиенте в строке состояния вида, может быть частично расшифрована.
Поскольку состояние вида формирует только серверная сторона, то перед включением строки в @ Page параметром EnableViewStateMac="true".
Для большей защиты строки состояния вида можно использовать встроенное шифрование, которое включается на странице параметром ViewStateEncryptionMode="Always" @ Page или установкой в
<pages viewStateEncryptionMode="Always">
Включите шифрование и выполните страницу DisplayViewState.aspx. Сгенерированный отклик на обратную отсылку станет примерно таким
Теперь строка состояния стала зашифрованной и ее трудно расшифровать. Однако не рекомендуется без надобности
Во вкладке Standard панели Toolbox имеется элемент управления HiddenField, который генерирует Value. Этот элемент не поддерживает шифрование, хэширование и хранение по частям. Поэтому клиенты в исходном коде HTML-вывода могут просматривать эти данные. Данные в скрытом поле хранятся до тех пор, пока пользователь не перейдет к другой странице.
Добавьте к проекту новую страницу с именем HiddenFieldTest.aspx без файла отделенного кода и назначьте ее стартовой
Переведите страницу в режим Design и Page_Load()
Поместите на Web-форму из вкладки Standard панели Toolbox два элемента управления HiddenField и присвойте им имена beginTime и countRequest
Отключите для всех элементов страницы механизм сохранения состояния вида, добавив в @ Page параметр EnableViewState="false"
Заполните страницу следующим кодом(рис ) Код страницы HiddenFieldTest.aspx<%@ Page Language="C#" EnableViewState="false" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Создаем текстовую метку
lblTime = new Label();
div1.Controls.Add(lblTime);
div1.Controls.Add(new HtmlGenericControl("br"));
// Создаем кнопку CurrentTime
Button button = new Button();
div1.Controls.Add(button);
// Подписываемся на событие кнопки
button.Click += CurrentTimeClick;
button.Text = "Текущее время";
// Добавляем промежуток между кнопками
Literal literal = new Literal();
literal.Text = "nbsp;nbsp;nbsp;nbsp;nbsp;";
div1.Controls.Add(literal);
// Создаем кнопку OldTime
button = new Button();
div1.Controls.Add(button);
// Подписываемся на событие кнопки
button.Click += OldTimeClick;
button.Text = "Начальное время";
// Инициализируем скрытые поля и метку при первом запросе
if (!this.IsPostBack)
{
beginTime.Value = System.DateTime.Now.ToLongTimeString();
OldTimeClick(null, EventArgs.Empty);
}
else
{
// Ведение счетчика запросов страницы
int.TryParse(countRequest.Value, out count);
count++;
// Или то же самое
// count = int.Parse(countRequest.Value) + 1;
}
countRequest.Value = count.ToString();
}
// Вынесены в поля класса для видимости в обработчиках
int count = 0;
Label lblTime;
void CurrentTimeClick(object sender, EventArgs e)
{
lblTime.Text = "Текущее время: " + System.DateTime.Now.ToLongTimeString();
lblTime.Text += "<br />Ответ № " + count.ToString();
}
void OldTimeClick(object sender, EventArgs e)
{
lblTime.Text = "Начальное время: " + beginTime.Value;
lblTime.Text += "<br />Ответ № " + count.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center" id="div1" runat="server">
<h1 style="color: Red">
Испытание элемента HiddenField
</h1>
<asp:HiddenField ID="beginTime" runat="server" />
<asp:HiddenField ID="countRequest" runat="server" />
</div>
</form>
</body>
</html>
Исполните страницу и убедитесь, что HiddenField, в пределах одной страницы работают исправноОдно из самых серьезных ограничений состояния вида состоит в том, что оно работает только на одной странице. При переходе пользователя на другую страницу состояние вида прежней страницы утрачивается. Одним из выходов является передача информации в
GET вместо POST Отправляющая запрос страница инициирует передачу либо с помощью ), либо с помощью кнопки Submit, в обработчик которой вставлен код . Для извлечения информации из .
Добавьте к проекту страницу без файла отделенного кода с именем SourceQueryString.aspx и назначьте ее стартовой
Добавьте внутрь контейнера <div></div> <h1 style="color: Red">Введите учетную запись</h1>
Дополните открывающий <div> стилем центрирования<div style="text-align: center">
Создайте на Web-форме позиционирующую таблицу размером 3x2 и поместите в ее ячейки из вкладки Standard панели Toolbox серверные элементы с настройками согласно таблице| Элемент | Свойство |
|---|---|
|
ID="Label1" |
Text="Имя:" |
|
TextBox |
ID="Name" |
|
ID="Label2" |
Text="Пароль:" |
|
TextBox |
ID="Password" |
TextMode="Password" |
|
|
ID="Button1" |
Text="Отправить" |
Интерфейс страницы в режиме проектирования будет таким

Переведите страницу в режим Design и Button1 создайте для него обработчик. Заполните обработчик следующим кодомprotected void Button1_Click(object sender, EventArgs e)
{
string Url = "TargetQueryString.aspx?Name="
+ Server.UrlEncode(Name.Text)
+ "Password="
+ Server.UrlEncode(Password.Text);
this.Response.Redirect(Url);
// То же самое, только с прямой переадресацией
// this.Server.Transfer(Url);
}
Кнопка будет инициировать обратную отсылку, а ее обработчик сформирует адрес целевой страницы со значениями параметров, снятых с элементов текстовых полей. Перед включением в строку адреса введенных пользователем значений мы применяем
Переведите страницу в режим Design и Page_Load(). Заполните обработчик кодом, восстанавливающий состояние текстового поля Name сохраненным в protected void Page_Load(object sender, EventArgs e)
{
if (this.Request.QueryString["Name"] != null !this.IsPostBack)
Name.Text = this.Request.QueryString["Name"];
}
Страница SourceQueryString.aspx, отправляющая
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
string Url = "TargetQueryString.aspx?Name="
+ Server.UrlEncode(Name.Text)
+ "Password="
+ Server.UrlEncode(Password.Text);
this.Response.Redirect(Url);
// То же самое с прямой переадресацией
// this.Server.Transfer(Url);
}
protected void Page_Load(object sender, EventArgs e)
{
if (this.Request.QueryString["Name"] != null !this.IsPostBack)
Name.Text = this.Request.QueryString["Name"];
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<h1 style="color: Red">
Введите учетную запись</h1>
<table border="0" width="100%">
<tr>
<td style="text-align: right; width: 412px;">
nbsp;<asp:Label ID="Label1" runat="server" Text="Имя:" />
</td>
<td>
nbsp;<asp:TextBox ID="Name" runat="server" />
</td>
</tr>
<tr>
<td style="width: 412px; text-align: right">
nbsp;<asp:Label ID="Label2" runat="server" Text="Пароль:" />
</td>
<td>
nbsp;<asp:TextBox ID="Password" runat="server" TextMode="Password" />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center">
nbsp;<asp:Button ID="Button1" runat="server" Text="Отправить"
OnClick="Button1_Click" />
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Теперь нужно создать принимающую запрос (целевую) страницу. На целевой странице значения поступивших параметров, извлекаемые из
Создайте страницу с совмещенным кодом и именем TargetQueryString.aspx
Заполните страницу следующим кодом<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Критерии оценки зарегистрированного пользователя
const string NAME = "USER", PASSWORD = "ROOT";
// Извлекаем и раскодируем поступившие данные из строки запроса
string name = this.Request.QueryString["Name"];
name = this.Server.UrlDecode(name);
string password = this.Request.QueryString["Password"];
password = this.Server.UrlDecode(password);
// Создаем текстовую метку для отображения решения
Label label = new Label();
form1.Controls.Add(label);
// Резервируем место для размещения гиперссылки
PlaceHolder placeHiperLink = new PlaceHolder();
form1.Controls.Add(placeHiperLink);
// Создаем муляж кнопки
Button button = new Button();
form1.Controls.Add(button);
// Анализируем данные и принимаем решение
StringBuilder message = new StringBuilder();
if (name.ToUpper() == NAME password.ToUpper() == PASSWORD)
{
// Просто выведем хвалебное приветствие
message.Append("<h1 style=\"color: Red\">");
message.Append("Приветствуем Вас, дорогой " + name + "!");
message.Append("</h1>");
// Отключаем у кнопки способность инициировать отсылку
// Лень посылать дальше зарегистрированного пользователя
button.PostBackUrl = "javascript: void(0)";
button.Text = "Дальше >";
// Добавляем в зарезервированное место гиперссылку
HyperLink link = new HyperLink();
placeHiperLink.Controls.Add(link);
link.Text = "< Назад";
link.NavigateUrl = "~/SourceQueryString.aspx?"
+ "Name=" + name;
// Добавляем в зарезервированное место пробелы
Label lblSpace = new Label();
placeHiperLink.Controls.Add(lblSpace);
lblSpace.Text = "nbsp;nbsp;nbsp;nbsp;";
}
else
{
// Поругаем и отправим на повторную регистрацию
message.Append("<h1>");
message.Append("Вы неопознаны!<br />Повторите регистрацию...");
message.Append("</h1>");
button.PostBackUrl = "~/SourceQueryString.aspx?"
+ "Name=" + name;
button.Text = "< Назад";
}
// Выводим сформированное сообщение
label.Text = message.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Обратите внимание, что интерфейсная часть целевой страницы совершенно пустая - все создается динамически.
Запустите страницу SourceQueryString.aspx на выполнение и убедитесь, что механизм межстраничного обмена информацией через Страница TargetQueryString.aspx на клиенте при наведении курсора мыши на
В классах кнопочных элементов управления , и LinkButton имеется свойство PostBackUrl, позволяющее сразу адресоваться к целевой странице с доступом в ней ко всем данным текущей страницы. Свойству PostBackUrl кнопки обратной отсылки присваивается строка с URL целевой страницы, которая и будет загружена на выполнение по щелчку пользователя на этой кнопке. При этом на целевой странице, благодаря ее свойству PreviousPage, будет полностью доступен объект страницы, инициировавшей обратную отсылку.
Идея реализации механизма межстраничной отсылки достаточно проста. Любая страница на этапе описания является классом (инструкцией) с именем, равном имени страницы, которая при загрузке в память сервера превращается в объект (экземпляр класса), адресуемый средой исполнения. Исполняемый объект проходит все необходимые этапы жизни, начиная с инициализации элементов управления, корректировкой состояния, выполнением обработчиков, и заканчивая генерацией
То же самое происходит и со страницей, загруженной по кнопке с ненулевым свойством PostBackUrl. Она также проходит все начальные стадии жизни, включая выполнение обработчиков. Непосредственно перед генерацией PreviousPage среда исполнения записывает адрес объекта первой страницы.
Теперь код целевой страницы может адресоваться к общедоступным данным исходной страницы, но рендеринг для клиента будет генерироваться только второй страницей. Когда целевая страница заканчивает свой цикл жизни, вместе с ней из памяти выгружается и исходная страница. Для того, чтобы закрытые или защищенные члены класса исходной страницы стали доступными в классе целевой страницы, их нужно обернуть в общедоступные свойства или функции доступа.
Рассмотренная межстраничная связь может быть реализована не только как "один к одному", но и в других сочетаниях: "один ко многим", "многие к одному" или "многие ко многим". Множественность на исходной странице может быть организована программным формированием значения свойства PostBackUrl в зависимости от выбранных на этой странице предпочтений. Множественность на целевой странице реализуется проверкой того, к какому типу (к какой странице) относится записанный в свойство PreviousPage объект.
Межстраничная обратная отсылка целевой страницы может быть инициирована не только по щелчку на кнопке с ненулевым значением свойства PostBackUrl, но и в любом месте кода вызовом соответствующей
public void Page.Server.Transfer(string path, bool preserveForm)
где
path - вызываемая целевая страницаpreserveForm - флаг передачи в целевую страницу данных QueryString и Form Оптимальным будет вызов
Server.Transfer(Url, true);
Проиллюстрируем межстраничную отсылку на примере двух страниц-источников и двух страниц-приемников.
Добавьте к проекту страницу с совмещенным кодом и именем SourcePostBack1.aspx
Назначьте страницу стартовой и оформите пользовательский интерфейс на 
Здесь использованы серверные элементы , TextBox, ListBox, и .
Для элемента управления ListBox включите свойство AutoPostBack
Заполните страницу следующим кодом(рис ) Полный код страницы SourcePostBack1.aspx<%@ Page Language="C#" %>
<script runat="server">
// Функция доступа к закрытому полю класса из внешнего кода
public string GetName()
{
return txtName.Text;
}
// Свойство доступа к закрытым данным класса из внешнего кода
public string GetTitle
{
get { return lblTitle.Text; }
}
string text;
protected void Page_Load(object sender, EventArgs e)
{
// Просто отображаем страницу
if (lstPost.SelectedItem == null)
return;
// Выбираем целевую страницу при AutoPostBack списка
text = lstPost.SelectedItem.Text;
if (String.Compare(text, "Студент", true) == 0)
btnPostBackUrl.PostBackUrl = "~/CrossPageStudent.aspx";
else
btnPostBackUrl.PostBackUrl = "~/CrossPageLecturer.aspx";
}
protected void btn_Click(object sender, EventArgs e)
{
// Контролируем наличие выбора при щелчках на кнопках
if (lstPost.SelectedItem == null)
{
String mess = "<script>alert('Выберите сотрудника')</" + "script>";
this.Response.Write(mess);
return;
}
text = lstPost.SelectedItem.Text;
// Различаем кнопки
Button btn = (Button)sender;
if (String.Compare(btn.Text, "PostBackUrl", true) != 0)
{
if (String.Compare(text, "Студент", true) == 0)
this.Server.Transfer("~/CrossPageStudent.aspx", true);
else
this.Server.Transfer("~/CrossPageLecturer.aspx", true);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<asp:Label ID="lblTitle" runat="server" Text="Это первая страница-источник"></asp:Label>
<br />
<br />
Фамилия:nbsp;
<asp:TextBox ID="txtName" runat="server">Иванов</asp:TextBox>nbsp;
<asp:ListBox ID="lstPost" runat="server" Height="45px" AutoPostBack="True">
<asp:ListItem>Студент</asp:ListItem>
<asp:ListItem>Доцент</asp:ListItem>
</asp:ListBox>
<br />
<br />
<asp:Button ID="btnPostBackUrl" runat="server" Text="PostBackUrl" OnClick="btn_Click" />nbsp;
<asp:Button ID="btnServerTransfer" runat="server" OnClick="btn_Click"
Text="Server.Transfer()" />
<br />
<br />
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="SourcePostBack2.aspx">
На вторую страницу-источник</asp:HyperLink>
</div>
</form>
</body>
</html>
Получите копию страницы SourcePostBack1.aspx под именем SourcePostBack2.aspx и скорректируйте в ней элементы lblTitle и HyperLink1
Создайте новую страницу с именем CrossPageStudent.aspx без файла отделенного кода, в которой блок скриптов заполните так(рис ) Полный код страницы-приемника данных CrossPageStudent.aspx<%@ Page Language="C#" %>
<script runat="server">
// Поля, заполняемые данными вызвавшей страницы
String name, title, className;
// Наша функция, использующая доступ с помощью механизма отражения типов
void GetInfoPreviousPage()
{
// Получаем тип запускающей страницы для
// динамического вызова членов представляющего класса
System.Type typePreviousPage = this.PreviousPage.GetType();
// Находим общедоступный метод класса
System.Reflection.MethodInfo methodGetName =
typePreviousPage.GetMethod("GetName");
// Выполняем метод предыдущей страницы
name = methodGetName.Invoke(this.PreviousPage, null).ToString();
// Находим общедоступное свойство класса
System.Reflection.PropertyInfo propertyGetTitle =
typePreviousPage.GetProperty("GetTitle");
// Выполняем свойство предыдущей страницы
title = propertyGetTitle.GetGetMethod().
Invoke(this.PreviousPage, null).ToString();
// Извлекаем имя класса предыдущей страницы
className = this.PreviousPage.GetType().Name;
}
protected void Page_Load(object sender, EventArgs e)
{
// Получаем информацию с предыдущей страницы
GetInfoPreviousPage();
Label label = new Label();
form1.Controls.Add(label);
string text = "<h1 style='text-align: center; color: Red'>";
if (this.PreviousPage == null)
{
text += "Страница запущена напрямую</h1>";
label.Text = text;
return;
}
text += "Методом межстраничной обратной отсылки получены данные</h1>";
text += "<ul>";
text += "<li>Заголовок: " + title + "</li>";
if (String.Compare(className, "SourcePostBack1_aspx", true) == 0)
text += "<li>Данные прислала первая страница</li>";
else if (String.Compare(className, "SourcePostBack2_aspx", true) == 0)
text += "<li>Данные прислала вторая страница</li>";
text += "<li>Фамилия студента: " + name + "</li>";
if (this.PreviousPage.IsCrossPagePostBack)
text += "<li>Обратная отсылка инициирована свойством PostBackUrl</li>";
else
text += "<li>Обратная отсылка инициирована методом Server.Transfer()</li>";
text += "</ul>";
label.Text = text;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Получите копию страницы CrossPageStudent.aspx под именем CrossPageLecturer.aspxЭти обе страницы-приемники имеют совершенно одинаковый код и просто демонстрируют работу схемы межстраничной GetInfoPreviousPage(), в которую мы упаковали код, извлекающий данные из класса страницы-источника на этапе выполнения. Этот прием применен потому, что при прямой
Разберитесь с кодом отсылающей и принимающей сторон и испытайте их работуВариант результата на принимающей странице будет примерно таким
Куки-наборы являются еще одним распространенным способом хранения информации на клиенте. Они, подобно состоянию вида, также включают в себя текстовую информацию, но хранят ее не в
Однако клиент имеет возможность удалить куки, отключить в и , каждый из которых имеет коллекцию (набор) типа HttpCookieCollection. Поступившие от клиента куки нужно извлекать из коллекции , а отправляемые на клиента новые данные для их сохранения в куках необходимо добавлять в коллекцию .
Коллекции состоят из отдельных объектов-экземпляров класса HttpCookie, каждый из которых является словарем. При создании объекта-словаря в конструкторе HttpCookie(string) задается имя кука, под которым он будет адресоваться в коллекции. Перегруженная версия HttpCookie(string, string) позволяет при создании имени кука определять и его единственное значение.
Важным свойством объекта является , которое определяет срок хранения куки-файла на клиенте. Если будет храниться в памяти, пока пользователь не закроет на клиенте с длительным сроком хранения нужно перезаписать в него просроченную дату.
В качестве примера разработаем страницу, на которой значения некоторых интерфейсных элементов управления будем хранить в .
Добавьте к проекту страницу без отделенного кода с именем CookiesPage1.aspx и назначьте ее стартовойНикакие элементы в режиме проектирования мы на странице размещать не будем, а все создадим и настроим динамически, чтобы еще раз подчеркнуть возможности программирования. В итоге код нашей страницы будет выглядеть так
<%@ Page Language="C#" ValidateRequest="false" %>
<script runat="server">
// Вынесли как поле для видимости в обработчиках
// Инициализировать необязательно, компилятор сам справится
TextBox txtName;
String result = "";
int count = 0;
protected void Page_Load(object sender, EventArgs e)
{
this.Response.Write("<h1 style='color: Red;'>Испытание Cookies</h1>");
Label label = new Label();
label.Text = "Введите ФИО:nbsp;nbsp;";
form1.Controls.Add(label);
txtName = new TextBox();
txtName.Text = "Снетков В.М.";
form1.Controls.Add(txtName);
form1.Controls.Add(new HtmlGenericControl("br"));
Button button = new Button();
button.Text = "Отправить";
form1.Controls.Add(button);
button = new Button();
button.Text = "Сохранить";
form1.Controls.Add(button);
button.Click += btnSaveClick;
button = new Button();
button.Text = "Восстановить";
form1.Controls.Add(button);
button.Click += btnRestoreClick;
form1.Controls.Add(new HtmlGenericControl("br"));
button = new Button();
button.Text = "Удалить";
form1.Controls.Add(button);
button.Click += btnDeleteClick;
form1.Controls.Add(new HtmlGenericControl("br"));
HyperLink link = new HyperLink();
form1.Controls.Add(link);
// Определяем имя класса текущей страницы и формируем гиперссылку
String nameCurrentPage = this.GetType().Name;
// Разбиваем строку на части
String[] parts = nameCurrentPage.Split('_');
// Сравниваем первую часть без учета регистра
if (String.Compare(parts[0], "CookiesPage1", true) == 0)
link.NavigateUrl = "CookiesPage2" + ".aspx";
else
link.NavigateUrl = "CookiesPage1" + ".aspx";
link.Text = "Перейти к " + link.NavigateUrl;
// Отключаем сохранение состояния вида всех элементов страницы
DisabledViewState(form1.Controls);
// Извлекаем общее число посещений
HttpCookie cookie = this.Request.Cookies["count"];
if (cookie != null)
{
int.TryParse(cookie.Value, out count);
}
count++;
// Адресуемся к отклику и сохраняем общее число посещений
cookie = this.Response.Cookies["count"];
cookie.Value = count.ToString();
// Устанавливаем срок хранения на клиенте: от текущего 1 день
cookie.Expires = DateTime.Now.AddDays(1);
// Отображаем сообщение на странице пользователя
result = "Текущий запрос: " + count.ToString();
}
// Отключаем механизм сохранения состояния вида для всех элементов
void DisabledViewState(ControlCollection controls)
{
foreach (Control ctrl in controls)
{
// Отключаем сохранение состояния вида элемента
if (ctrl.EnableViewState)
ctrl.EnableViewState = false;
// Продолжаем опрашивать рекурсивно, если есть дочерние элементы
if (ctrl.Controls != null)
DisabledViewState(ctrl.Controls);
}
}
// Переопределяем обработчик генерации HTML-вывода страницы
protected override void Render(HtmlTextWriter output)
{
// Центрируем динамически созданные элементы управления
output.RenderBeginTag(HtmlTextWriterTag.Center);
base.Render(output);
output.Write("<hr />");
output.Write(result);
output.RenderEndTag();
}
// Сохраняем данные в куке "oldInfo"
void btnSaveClick(object sender, EventArgs e)
{
// Создаем словарь и заполняем текущей информацией
HttpCookie cookie = new HttpCookie("oldInfo");
// Кодировать необязательно, направляется в файл
cookie["name"] = txtName.Text;
cookie["clicks"] = count.ToString();
// Присоединяем к ответу
// Срок хранения - текущий сеанс
// cookie.Expires = DateTime.Now.AddYears(1);
this.Response.Cookies.Add(cookie);
}
// Извлекаем данные из кука "oldInfo"
void btnRestoreClick(object sender, EventArgs e)
{
// Подключаемся к кукам поступившего запроса
HttpCookieCollection cookies = this.Request.Cookies;
// Извлекаем нужный словарь
HttpCookie cookie = cookies["oldInfo"];
if (cookie != null)
{
// Извлекаем значения словаря
string name, clicks;
name = cookie["name"];
clicks = cookie["clicks"];
// Альтернативный синтаксис
// name = cookies["oldInfo"]["name"];
// clicks = cookies["oldInfo"]["clicks"];
// Если готовим к отправке, то можно...
// cookies["oldInfo"].Expires = DateTime.Now.AddYears(1);
// Безопасно отображаем сохраненные значения,
// кодируя интерпретируемые броузером символы
name = Server.HtmlEncode(name);
clicks = Server.HtmlEncode(clicks);
result = "Сохраненное ФИО: " + name;
result += "; cохраненный запрос: " + clicks;
}
else
{
result = "Нечего восстанавливать! ";
result += "Текущий запрос: " + count.ToString();
}
}
// Удаляем кук "count"
void btnDeleteClick(object sender, EventArgs e)
{
// Удаляем кук количества запросов, посылая просроченную дату
this.Response.Cookies["count"].Expires =
DateTime.Now.AddDays(-1);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
Для того, чтобы продемонстрировать, что куки сохраняются и при переходе к другим страницам, создадим еще одну страницу, совершенно идентичную первой.
Создайте в проекте копию страницы CookiesPage1.aspx и присвойте ей имя CookiesPage2.aspx
Запустите первую страницу CookiesPage1.aspx и испытайте ее работу совместно со второй страницейВид страницы на клиенте будет таким
Механизм является очень удобным, но неустойчивым, поскольку может либо не поддерживаться
Информация о функциональности System.Web.HttpBrowserCapabilities, ссылка на который находится в свойстве Page. объекта страницы. Эта информация включает в себя тип
К сожалению эта информация свидетельствует только о потенциальных возможностях
Что касается , а затем в поступившем запросе попытаться прочитать его. Если такая попытка не удастся, значит поддержка
В качестве иллюстрации к сказанному создайте страницу с именем IsCookiesSupported.aspx и заполните ее следующим кодом<%@ Page Language="C#" ClassName="IsCookiesSupported" EnableViewState="false" %>
<script runat="server">
bool CookiesSupportedFlag;
int clicks = 1;
protected void Page_Load(object sender, EventArgs e)
{
// Создаем объекты пользовательского интерфейса
// Текстовая метка заголовка
Label label = new Label();
form1.Controls.Add(label);
label.Text = "<h2>Поддерживаются ли Cookies?</h2>";
// Кнопка Submit
Button submit = new Button();
form1.Controls.Add(submit);
submit.Text = "Отправить";
// Горизонтальная линия
form1.Controls.Add(new HtmlGenericControl("hr"));
// Текстовая метка результатов
label = new Label();
form1.Controls.Add(label);
if (!this.IsPostBack)
{
// Проверяем функциональность броузера на поддержку Cookies
CookiesSupportedFlag = this.Request.Browser.Cookies;
if (CookiesSupportedFlag)
{
// Для дополнительной проверки пробуем отправить cookie
this.Response.Cookies["tmp"].Value = String.Empty;
}
// this.Response.Redirect(Request.Url.ToString());// Зацикливает
// Дублируем сохранение в состоянии вида
this.ViewState["clicks"] = clicks.ToString(); ;
label.Text = "Пробный запрос страницы";
return;
}
else if (this.Request.Cookies["tmp"] != null)
{
// Поднимаем флаг поддержки cookies
CookiesSupportedFlag = true;
}
else
CookiesSupportedFlag = false;
// Принимаем решение, откуда извлекать и где сохранять данные
if (CookiesSupportedFlag)
{
if (this.Request.Cookies["clicks"] != null)
clicks = int.Parse(this.Request.Cookies["clicks"].Value) + 1;
this.Response.Cookies["clicks"].Value = clicks.ToString();
// Устанавливаем срок хранения
this.Response.Cookies["clicks"].Expires =
DateTime.Now.AddMinutes(1.0);
}
else
{
clicks = Convert.ToInt32(this.ViewState["clicks"]) + 1;
this.ViewState["clicks"] = clicks.ToString();
}
// Показываем пользователю количество щелчков и механизм хранения
label.Text = "Запрос страницы № " + clicks.ToString();
if (CookiesSupportedFlag)
label.Text += "<br />Хранение в cookies";
else
label.Text += "<br />Хранение в ViewState";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
" при записи пометить
Response.Cookies["clicks"].Path = "~/MyFolder";
то кук будет доступен только страницам папки MyFolder. По умолчанию кук доступен любым страницам домена, как и, например, при установке
Response.Cookies["clicks"].Domain = "mysite.ru";
Перейдите ко второй части ...
Любой пользователь при первом обращении к приложению получает свой сеанс, который выражается в выделении некоторого объема памяти на стороне сервера для . За счет этого можно сохранять информацию на одной странице, а получать к ней доступ с другой страницы.
При поступлении первого клиентского запроса на объект страницы ASP.NET генерирует идентификатор сеанса размером 42 байта. Этот идентификатор сеанса включается в отклик сервера и является уникальным в пределах приложения. Если ни одна из страниц приложения, к которым обращается пользователь, не записывает в объект , идентификатор сеанса сразу запоминается .
Когда клиент присылает идентификатор сеанса, сервер по нему автоматически извлекает из места созданной страницы. После чего данные сеанса становятся доступными в коде этой страницы.
Идентификатор сеанса может пересылаться серверу с запросом двумя способами:
ASP .NET_SessionIdИдентификатор сеанса можно получить как значение - при формировании первого отклика, или - всегда. Но конкретное его значение нас, как правило, не интересует, поскольку процесс распознавания выполняется автоматически.
Данные сеанса могут сохраняться ASP.NET в одном из трех мест на сервере, определяемых свойством :
Session .Abandon ()Сеанс является экземпляром класса System.Web.SessionState.HttpSessionState и может частично управляться из кода. В таблице приведены некоторые свойства и HttpSessionState
| Член | Описание |
|---|---|
|
Количество элементов в коллекции текущего сеанса. Только для чтения |
IsCookieless |
Булев флаг, показывающий способ хранения идентификатора сеанса: в куках или в модифицированном URL. Только для чтения |
IsNewSession |
Булево значение, которое указывает, был ли сеанс создан с текущим запросом. Если коллекция пуста или сеанс создан ранее, то возвращается false. Только для чтения |
|
Свойство (только для чтения) содержит одно из значений перечисления SessionStateMode, указывающее способ Off = 0 - сеанс не используетсяInProc = 1 - данные сеанса хранятся в памяти доменаStateServer = 2 - сеанс хранится в специальной службе Windows под названием ASP.NET State ServiceSQLServer = 3 - сеанс хранится в базе данных SQL Server - сеанс хранится на основе нестандартного поставщика |
SessionID |
Строка, содержащая уникальный идентификатор сеанса. Только для чтения |
|
Максимальный срок простоя сеанса в минутах. Для чтения и записи |
|
Отменяет текущий сеанс |
Add(string name, object value) |
Добавляет новый элемент в коллекцию сеанса. Допускается |
|
Очищает коллекцию сеанса |
RemoveAll() |
Очищает коллекцию сеанса |
|
Удаляет |
RemoveAt(int index) |
Удаляет |
Начальное <sessionState>. Настройки секции <sessionState> поддерживаются классом System.Web. через его свойства, но имена свойств в кофигурационном файле начинаются с символа нижнего регистра. Вот некоторые наиболее важные из них
| Свойство | Описание |
|---|---|
Cookieless |
Задает режим использования System.Web.HttpCookieMode:UseUri = 0 - никогда не использовать cookieless="UseUri" или cookieless="true" )UseCookies = 1 - всегда использовать - наиболее надежный режим, в котором сервер сам определяет возможности UseDeviceProfile = 3 - способ пересылки идентификатора сеанса основывается только на потенциальной функциональности |
|
Задает одно из значений перечисления SessionStateMode, указывающее способ Off = 0 - указывает, что сеанс не задействуется для InProc = 1 - данные сеанса хранятся в памяти доменаStateServer = 2 - сеанс хранится в специальной службе Windows под названием ASP.NET State ServiceSQLServer = 3 - сеанс хранится в базе данных SQL Server - сеанс хранится на основе нестандартного поставщика |
|
Устанавливают время простоя сеанса в минутах |
RegenerateExpiredSessionId |
cookieless="true", когда идентификатор сеанса пересылается |
CookieName |
Устанавливает имя идентификатора сеанса в " |
При включенном режиме cookieless="true" с полным циклом. Для того, чтобы указать системе, что идентификатор сеанса должен передаваться секретным протоколом, нужно установить
Приведем пример использования сеанса.
Отредактируйте файл web.config приложения как показано ниже(рис ) Файл web.config с включенным режимом Cookieless<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation debug="true" />
<sessionState cookieless="true" regenerateExpiredSessionId="true" />
</system.web>
</configuration>
Добавьте страницу с именем SessionCookieless.aspx с совмещенным кодом, которую заполните так(рис ) Код страницы SessionCookieless.aspx<%@ Page Language="C#" EnableViewState="false" %>
<script runat="server">
int clicks = 1;
const int maxCol=2;
HtmlTable table;
protected void Page_Load(object sender, EventArgs e)
{
string[] headers = {
// Имена свойств секции sessionState
"Cookieless",
"Mode",
"Timeout",
"RegenerateExpiredSessionId",
"CookieName",
// Дополнительная информация
"SessionID",
"numRequest",
"currentTime"
};
// Создаем объекты пользовательского интерфейса
// Текстовая метка заголовка
Label label = new Label();
form1.Controls.Add(label);
label.Text = "<h2>Использование сеансов Session</h2>";
if (!IsPostBack)
{
// Получаем всю информацию о параметрах конфигурации
System.Configuration.Configuration config;
config =
System.Web.Configuration.WebConfigurationManager.
OpenWebConfiguration(this.Request.ApplicationPath);
// Объявляем ссылку на объект SessionStateSection
System.Web.Configuration.SessionStateSection sessionState;
// Поиск элемента <sessionState> в элементе <system.web>
sessionState = (System.Web.Configuration.SessionStateSection)
config.GetSection(@"system.web/sessionState");
//sessionState.Timeout = TimeSpan.FromMinutes(1);
// Формируем таблицу и сохраняем ее в сеансе
table = new HtmlTable();
table.Border = 1;
for (int i = 0; i < headers.Length; i++)
{
// Создаем строку
HtmlTableRow row = new HtmlTableRow();
table.Rows.Add(row);
// Создаем ячейки в строке
for (int j = 0; j < maxCol; j++)
{
HtmlTableCell col = new HtmlTableCell();
row.Cells.Add(col);
if (j == 0)// Левый столбец
{
col.InnerHtml = headers[i] + ":nbsp;";
col.Align = "right";
col.BgColor = "Yellow";// Цвет фона
}
else // Правый столбец
{
// Применяем методику отражения типов
System.Type type = typeof(System.Web.Configuration.
SessionStateSection);
// Извлекаем публичное свойство по его имени
System.Reflection.PropertyInfo property =
type.GetProperty(headers[i]);
if (property != null)
{
col.InnerHtml = "nbsp;";
// Извлекаем значение свойства из экземпляра класса
col.InnerHtml += property.GetGetMethod().
Invoke(sessionState, null).ToString();
}
else
{
// Маркируем ячейку, чтобы потом найти
col.ID = headers[i];
}
col.BgColor = "#CCFFFF";// Цвет фона
}
}
}
// Сохраняем информацию в сеансе при первом запросе
this.Session.Add("table", table);
this.Session["clicks"] = clicks;
}
else
{
// Извлекаем информацию из сеанса при каждой обратной отсылке
table = (HtmlTable)this.Session["table"];
clicks = (int)this.Session["clicks"];
// Сохраняем новое значение
this.Session["clicks"] = ++clicks;
}
// Добавляем таблицу на форму
form1.Controls.Add(table);
// Корректируем таблицу
((HtmlTableCell)Page.FindControl("SessionID")).InnerHtml =
"nbsp;" + this.Session.SessionID;
((HtmlTableCell)Page.FindControl("numRequest")).InnerHtml =
"nbsp;" + clicks.ToString();
((HtmlTableCell)Page.FindControl("currentTime")).InnerHtml =
"nbsp;" + DateTime.Now.ToLocalTime();
// Новая строка HTML
HtmlGenericControl br = new HtmlGenericControl();
br.InnerHtml = "<br />";
form1.Controls.Add(br);
// Кнопка Submit
Button submit = new Button();
form1.Controls.Add(submit);
submit.Text = "Отправить";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server" style="text-align: center;">
</form>
</body>
</html>
Клиентский результат будет таким

Обратите внимание, что идентификатор сеанса встроен в URL при значении параметра Cookieless="UseUri". Отметьте также, что интерфейсная часть страницы пустая - все создается динамически. Часть выводимой в таблицу информации мы добыли из объекта класса SessionStateSection, который создается по настройкам
Состояние приложения - это область памяти сервера, выделяемая при запуске приложения для хранения глобальных данных, доступных с любой страницы приложения. На эти данные в каждой странице ссылается объект-словарь Application типа System.Web.HttpApplicationState. Как и при
Данные состояния приложения существуют в течение всего периода работы приложения и теряются при перезапуске сервера или перехода приложения в новый домен. Для обеспечения
В качестве примера построим две страницы, в которых подсчитывается общее число посещений сайта с момента запуска приложения.
Добавьте к приложению файл Global.asax и заполните его так(рис ) Файл Global.asax<%@ Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
this.Application["clicks"] = (int)0;
}
</script>
Создайте страницу без отделенного кода с именем ApplicationState1.aspx, назначьте ее стартовой и заполните так(рис ) Файл ApplicationState1.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Читаем без проверки - инициализирована в global.asax
int visit = (int)this.Application["clicks"] + 1;
// Обновляем состояние приложения с блокировкой
this.Application.Lock();
this.Application["clicks"] = visit;
this.Application.UnLock();
// Создаем текстовую метку
Label label = new Label();
form1.Controls.Add(label);
label.Text = "Сегодня вы посетили нас в "
+ visit.ToString() + " раз!<br />";
// Создаем гиперссылку
HyperLink link = new HyperLink();
form1.Controls.Add(link);
link.NavigateUrl = "~/ApplicationState2.aspx";
link.Text = "На другую страницу...";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Создайте копию для страницы ApplicationState1.aspx с именем ApplicationState2.aspx и поменяйте в ней ApplicationState1.aspx, чтобы страницы ссылались друг на друга
Запустите само приложение несколько раз и пощелкайте по ссылкам чтобы убедиться, что счетчик количества визитов увеличивается
В панели задач нижней части экрана щелкните правой кнопкой мыши на пиктограмме тестового сервера и
Вновь запустите страницу ApplicationState1.aspx, чтобы убедиться, что счетчик сбросился в начальное значениеДля увеличения @ Application файла Global.asax параметр ClassName, в котором указывается имя
При изменении в страницах глобальных данных , чтобы на время позволить воздействовать на переменную только со стороны одного потока. Такой подход удобен тем, что мы можем обернуть сохраняемые данные в статические свойства и назначить для них контролирующий код, прежде чем сохранять их в памяти сервера. Мы можем объявлять статические переменные для хранения объектов любого типа и для доступа к ним
Приведем пример, в котором количество визитов всех пользователей, а также список файлов текущего физического
Отредактируйте код файла Global.asax следующим образом(рис ) Код файла Global.asax со статическими членами<%@ Application Language="C#" ClassName="MyAppClass" %>
<script runat="server">
// Список файлов
private static string[] fileList;// Базовое поле
// Общедоступное статическое свойство для обертки базового поля
public static string[] FileList
{
get
{
// Заполнить список только при первом запросе
if (fileList == null)
{
fileList = System.IO.Directory.GetFiles(
HttpContext.Current.Request.PhysicalApplicationPath);
}
return fileList;
}
}
// Число посещений
private static int visit = 1;// Базовое поле
// Общедоступное статическое свойство для обертки базового поля
public static int Visit
{
get { return visit; }
set
{
lock (typeof(int))// Контейнер обеспечения синхронизации потоков
{
visit = value;
}
}
}
</script>
Добавьте к приложению страницу без отделенного кода с именем AppClassStaticMembers.aspx и назначьте ее стартовой
Заполните страницу AppClassStaticMembers.aspx следующим кодом(рис ) Код страницы AppClassStaticMembers.aspx <%@ Page Language="C#" EnableViewState="false" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Заголовок
Label title = new Label();
form1.Controls.Add(title);
title.Text = "<h2 style='text-align: center; color: Red'>"
+ "Использование статических"
+ " полей класса приложения</h2>";
// Число посещений
Label visit = new Label();
form1.Controls.Add(visit);
int clicks = MyAppClass.Visit;
visit.Text = "<b>Количество посещений: "
+ clicks.ToString() + "</b>";
MyAppClass.Visit = ++clicks;
// Горизонтальная черта
form1.Controls.Add(new HtmlGenericControl("hr"));
// Извлекаем и готовим список файлов
// текущего каталога приложения
StringBuilder builder = new StringBuilder();
builder.Append("<ol>");
// Счетчик для сокращения листинга
int count = 0;
const int maxCount = 5;
foreach (string file in MyAppClass.FileList)
{
if (++count > maxCount)
break;
string str;
//int pos = file.LastIndexOf(@"\");
//str = file.Substring(pos + 1);
// То же самое
str = System.IO.Path.GetFileName(file);
builder.Append("<li>" + str + "</li>");
}
builder.Append("</ol>");
// Готовим текстовую метку для отображения списка
Label fileInfo = new Label();
form1.Controls.Add(fileInfo);
fileInfo.Text = builder.ToString();
// Горизонтальная черта
form1.Controls.Add(new HtmlGenericControl("hr"));
// Создаем дескриптор центрирования кнопки
HtmlGenericControl center = new HtmlGenericControl("center");
form1.Controls.Add(center);
// Кнопка Submit добавляется в контейнер центрирования
Button button = new Button();
button.Text = "Отправить";
center.Controls.Add(button);
}
// Переопределяем обработчик рендеринга
protected override void Render(HtmlTextWriter writer)
{
// Сгенерировать основное
base.Render(writer);
// Добавить в конец еще HTML-вывод
// Настроить атрибуты будущего дескриптора
writer.AddAttribute(HtmlTextWriterAttribute.Align, "Center");
writer.AddStyleAttribute(HtmlTextWriterStyle.Color, "Blue");
// Создать дескриптор
writer.RenderBeginTag(HtmlTextWriterTag.H2);
// Писать внутрь дескриптора
writer.Write("Щелкайте на кнопке и наблюдайте счетчик посещений");
// Закрыть дескриптор
writer.RenderEndTag();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Запустите страницу и убедитесь, что все работает и объявленные в
В панели задач нижней части экрана щелкните правой кнопкой мыши на пиктограмме тестового сервера и
Вновь запустите страницу AppClassStaticMembers.aspx, чтобы убедиться, что память сервера очистилась и счетчик сбросился в начальное значениеА сейчас мы убедимся, что при модификации любого файла приложения, включая web.config, оно автоматически перекомпилируется при первом запросе и загружается в новый домен.
Не нарушая кода, модифицируйте любой исходный файл приложения, например, введите пробел после точки с запятой в конце любой строки (или добавьте пустую строку) и сохраните изменения
Запустите приложение и убедитесь, что счетчик сбросился за счет загрузки приложения в новый доменИнтерфейс клиента для данного примера будет таким

Рассмотренные в данной теме способы
Главная особенность удаленных приложений состоит в том, что они могут одновременно обрабатывать множество пользователей. К тому же сервер может выполнять одновременно несколько разных приложений. Поэтому среда исполнения устроена так, что после обработки запроса очередного пользователя память сервера немедленно очищается от кода запрошенной страницы, освобождая ресурсы для новых возможных
Такой факт вынуждает разработчиков Web-приложений изыскивать другие способы хранения информации о состоянии между запросами, с последующим ее извлечением и использованием. В ASP.NET существует два способа хранения информации между запросами: на клиенте и на сервере. Разработчик сайта сам решает, какую группу методов
Хранение информации на клиенте имеет следующие преимущества:
Хранение информации на сервере имеет следующие преимущества:
Различают следующие методы
view state ) - используется для сохранения значений элементов управления, добавленных конечным пользователем. Применение состояния вида основано на свойстве ViewState типа System.Web.UI.StateBag, наследуемом от класса Control любым элементом управления и самой страницей.hidden fields ) - POST (по щелчку кнопки Submit ), а не методом GET (по щелчку на query strings ) - cookie -файлы) - созданные на сервере данные записываются на компьютер клиента в небольших файлах (в количестве до 20 файлов и до 4 Кб каждый) и автоматически пересылаются Рассмотрим последовательно перечисленные методы хранения состояния на клиенте.
Это простой способ хранения информации на клиенте между обратными отсылками для одной и той же страницы. Вспомним, как обрабатывается страница на сервере. Для этого рассмотрим свежий отклик сервера, только что полученный
При поступлении обратной отсылки на сервер в память загружается адресуемая страница в своем первозданном виде, инициницализируются ее интерфейсные элементы начальными настройками, затем извлекается поступившая в
Все серверные Web-элементы управления используют состояние вида по умолчанию (не обязательно включать EnableViewState="true" ). В состав каждого из них входит свойство-коллекция , наследуемое от класса System.Web.UI.Control. Это же свойство напрямую наследует и класс Page, который мы расширяем при проектировании активной страницы. Оно создается и заполняется поступившими данными вместе с объектом страницы, а значит мы программно можем использовать это страничное свойство для сохранения и извлечения информации между запросами одной и той же страницы.
После обработки страницы ее текущее состояние, а также состояние элементов управления, автоматически хэшируется (maxPageStateFieldLength класса Page (по умолчанию равен -1 ). Для изменения начальных настроек объекта страницы значение этого свойства нужно поместить в
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation debug="true" />
<pages maxPageStateFieldLength="1024" />
</system.web>
</configuration>
Коллекция построена по типу словаря (Object, поэтому она способна хранить объекты любого типа. При извлечении из коллекции состояния вида данные нужно приводить к соответствующему типу. Если в коде попытаться извлечь значение, которого нет в коллекции, будет выдано исключение NullReferenceException, поэтому необходима предварительная проверка возвращаемой ссылки на null -значение.
Не все [, то порожденный этим из пространства имен System. также представляет собой словарь с полями Key и Value. Он поддается .
В качестве иллюстрации сказанного приведем 2 примера, где используется программирование словаря самой страницы.
В этом примере мы создадим форму с интерфейсными элементами, в которых отключим автоматическое сохранение состояния вида, а будем его сохранять и восстанавливать вручную. Интерфейсные элементы мы разместим в серверной таблице и будем использовать рекурсивную логику для опроса всех ее дочерних элементов, размещенных в коллекции Controls. В качестве ключа для доступа к элементам словаря уровня страницы будем использовать идентификаторы элементов.
Создайте пустой проект с именем
Добавьте к проекту новую Web-страницу с именем
Запустите командой Website/ASP.NET Configuration страницу WAT (Web Enable debugging Таким действием мы создадим файл конфигурации Web.config с настройкой
В панели Solution Explorer щелкните на кнопке Refresh, чтобы увидеть файл конфигурации в составе проекта
Откройте интерфейсную часть страницы ViewStateTest.aspx в режиме Source и добавьте в открывающую часть <div> внутренний стиль<div style="text-align: center">и заголовок
<h1 style="color: Red">Заполните анкету</h1>
Откройте интерфейсную часть страницы ViewStateTest.aspx в режиме Design и поместите на нее элемент Table из вкладки HTML панели Toolbox
Щелкните на элементе Table правой кнопкой мыши и выполните опцию Run As Server Control, чтобы превратить его в серверный
Откорректируйте интерфейсный (рис ) <table id="Table1" width="100%" runat="server" style="text-align: left; width: 500px;">
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
</table>
Поместите в первую строку и первый столбец таблицы приведенный ниже текст, а в остальные ячейки второго столбца - элемент TextBox из вкладки Standard панели Toolbox
Поместите после таблицы три кнопки Button из вкладки Standard панели Toolbox и присвойте им имена cmdSubmit, cmdSave и cmdRestore соответственноПосле этих действий интерфейс страницы в режиме проектирования будет таким

Ему соответствует такой дескрипторный код
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="ViewStateTest.aspx.cs" Inherits="ViewStateTest" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<h1 style="color: Red">Заполните анкету</h1>
<table id="Table1" runat="server" style="text-align: left; width: 500px;">
<tr>
<td style="width: 200px">
<b>Наименование</b></td>
<td>
<b>Значение</b></td>
</tr>
<tr>
<td style="width: 200px">
Имя:</td>
<td>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
Учетный номер:</td>
<td>
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
Возраст:</td>
<td>
<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
E-mail:</td>
<td>
<asp:TextBox ID="TextBox4" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 200px">
Пароль:</td>
<td>
<asp:TextBox ID="TextBox5" runat="server"></asp:TextBox></td>
</tr>
</table>
<br />
<asp:Button ID="cmdSubmit" runat="server" Text="Отправить" />
nbsp;nbsp;nbsp; nbsp;
<asp:Button ID="cmdSave" runat="server" Text="Сохранить" />
nbsp;nbsp;nbsp; nbsp;
<asp:Button ID="cmdRestore" runat="server" Text="Восстановить" />
</div>
</form>
</body>
</html>

Откройте файл отделенного кода и заполните его следующим кодом(рис ) Содержимое файла ViewStateTest.aspx.csusing 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 ViewStateTest : System.Web.UI.Page
{
private bool executeFlag = true;
protected void cmdSave_Click(object sender, EventArgs e)
{
// Сохраняем присланные значения текстовых полей
SaveAllText(Table1.Controls, executeFlag);
}
private void SaveAllText(ControlCollection controls, bool saveNested)
{
if (!saveNested)
return;
// Перебираем все дочерние элементы в переданной коллекции таблицы
foreach (Control control in controls)
{
// Выбираем элементы текстовых полей и сохраняем
// их содержимое в свойстве ViewState объекта страницы,
// используя в качестве ключа идентификатор элемента
if (control is TextBox)
{
this.ViewState[control.ID] = ((TextBox)control).Text;
}
// На верхних уровнях объекта Table1 дочерними элементами
// являются строки и ячейки таблицы, поэтому продолжаем
// рекурсивно спускаться ниже, пока не встретим объекты TextBox.
// Флаг saveNested разрешает проверять вложенные уровни коллекций
if (control.Controls != null)
{
SaveAllText(control.Controls, saveNested);
}
}
}
protected void cmdRestore_Click(object sender, EventArgs e)
{
// Извлекаем пришедший с обратной отсылкой сохраненный текст предыдущей
// обратной отсылки и вставляем его в текстовые поля, готовя новый отклик
RestoreAllText(Table1.Controls, executeFlag);
}
void RestoreAllText(ControlCollection controls, bool restoreNested)
{
if (!restoreNested)
return;
// Перебираем все элементы таблицы, включая вложенные, чтобы
// использовать их идентификаторы как ключи для извлечения
// возможно сохраненных значений из словаря ViewState
foreach (Control control in controls)
{
// Нашли текстовое поле
if (control is TextBox)
{
// Убеждаемся, что есть в словаре
if (this.ViewState[control.ID] != null)
{
// Приводим типы и восстанавливаем
((TextBox)control).Text = (string)this.ViewState[control.ID];
}
}
// Рекурсивно спускаемся ниже к дочерним элементам
if (control.Controls != null)
{
RestoreAllText(control.Controls, restoreNested);
}
}
}
}
Испытайте работу страницы и убедитесь, что в любое время можно восстановить состояние элементов управления, сохраненное ранее в словаре 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
{
private bool executeFlag = true;
// Создаем экземпляр промежуточного словаря
Hashtable dictionary = new Hashtable();
protected void cmdSave_Click(object sender, EventArgs e)
{
// Сохраняем присланные значения текстовых полей в словаре
SaveAllText(Table1.Controls, executeFlag);
// Сохраняем помеченный словарь целиком в состоянии вида
this.ViewState["contentsState"] = dictionary;
}
private void SaveAllText(ControlCollection controls, bool saveNested)
{
if (!saveNested)
return;
// Перебираем все дочерние элементы в переданной коллекции таблицы
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.
// Флаг saveNested разрешает проверять вложенные уровни коллекций
if (control.Controls != null)
{
SaveAllText(control.Controls, saveNested);
}
}
}
protected void cmdRestore_Click(object sender, EventArgs e)
{
// Извлекаем из состояния вида пришедший с
// обратной отсылкой промежуточный объект и заполняем
// из него текстовые поля сохраненными значениями
RestoreAllText(executeFlag);
}
// Если доступ к функции явно не объявлен, то по умолчанию считается private
void RestoreAllText(bool restoreNested)
{
if (!restoreNested)
return;
// Извлекаем помеченные ранее данные из состояния
// вида в промежуточный объект-словарь
dictionary = (Hashtable)this.ViewState["contentsState"];
// Перебираем все элементы заполненного словаря и ищем на
// странице соответствующее текстовое поле. Заодно заполняем
// парами словаря текстовую метку для отсылки клиенту. Текстовую
// метку создадим динамически и добавим в конец коллекции формы
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 />";
}
}
}
}
Поскольку мы взяли на себя проверку достоверности данных, введенных пользователем, то нужно отключить такую проверку системой ASP.NET при считывании запроса, поместив соответствующую инструкцию в @ Page страницы или в файл конфигурации в секцию <pages>. При этом нужно строго учитывать регистр символов и в @ Page атрибут помещать таким ValidateRequest="false", а в validateRequest="false". Это общее правило ASP.NET для случая, когда настройки допускают двойственное размещение.
Дополните файл конфигурации web.config секцией <pages> с отключенной проверкой достоверности(рис ) Файл web.config с отключенной проверкой достоверности<?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>
Запустите приложение и убедитесь в его работоспособностиРезультат после восстановления ранее сохраненной в состоянии вида информации будет примерно таким

Удалите из секции <system.web> файла Web.config параметр отключения проверки достоверности <pages validateRequest="false" /> и включите его как атрибут ValidateRequest="false" в состав @ Page страницы ViewStateObjects.aspx, чтобы он действовал только на одну эту страницуИногда состояние вида в элементе полезно отключить, чтобы уменьшить нагрузку на каналы связи. Но для некоторых элементов это нарушает их нормальную работу. Возьмем, например, списки ListBox и DropDownList. Когда они заполнены большим количеством элементов, объем пересылаемой информации состояния вида очень большой и это свойство лучше отключить. Для восстановления состояния выделенного страницы. Проверим это на примере.
Добавьте к проекту страницу с именем ViewStateList.aspx без файла отделенного кода и назначьте ее стартовой
Оформите страницу так, чтобы код ее интерфейсной части был следующим(рис ) Код интерфейсной части страницы ViewStateList.aspx<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<h2>Отключение состояния вида в списках</h2>
<asp:ListBox ID="ListBox1" runat="server" Height="200px" Width="150px" />
nbsp; nbsp; nbsp; nbsp;
<asp:DropDownList ID="DropDownList1"
runat="server" Width="150px">
</asp:DropDownList><br />
<br />
<asp:Button ID="Button1" runat="server" Text="Отправить" /></div>
</form>
</body>
</html>
Этому коду в режиме проектирования соответствует следующее представление

Page_Load()
Поместите в обработчик Page_Load() код заполнения списков видимым содержимым, который будет срабатывать при каждом запросе страницы(рис ) Обобработчик Page_Load() для заполнения списков<script runat="server">
// Поле класса страницы
private const int maxCount = 1000;
protected void Page_Load(object sender, EventArgs e)
{
// Заполняем списки программной генерацией значений
for (int i = 0; i < maxCount; i++)
{
ListBox1.Items.Add("Опция " + (i + 1).ToString());
DropDownList1.Items.Add("Опция " + (i + 1).ToString());
}
}
</script>
Выполните страницу и убедитесь, что при каждом новом запросе по щелчку на кнопке Submit отмеченные пользователем опции списков возвращаются сервером сохраненнымиПока что это работает механизм автоматического сохранения состояния вида ASP.NET. Но давайте посмотрим, какой при этом получается объем пересылаемой информации, именно касающийся работы этого механизма. Для этого включим работу ASP.NET в режиме выдачи трассировки на индивидуальных страницах. Это можно сделать, если добавить атрибут в @ Page страницы, либо через утилиту , запустив ее командой Website/ASP.NET Configuration (или щелкнуть на пиктограмме в панели Solution Explorer ).
Запустите утилиту WAT , пойдите последовательно по ссылкам Application Configuration/Configure debugging and tracing и включите флажки "Capture tracing information" и "Display tracing information on ondividual pages". Закройте утилитуВ результате этих действий файл конфигурации примет вид
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<trace enabled="true" pageOutput="true" />
<compilation debug="true" />
</system.web>
</configuration>
Вновь запустите страницу ViewStateList.aspx, поманипулируйте с интерфейсными элементами, выполните обратную отсылку и проанализируйте секцию Control Tree в результатах трассировки, которая будет, примерно, такой| Control UniqueID | Type | ControlState | ||
|---|---|---|---|---|
| __Page | 910866 | 0 | 0 | |
| ctl02 | System.Web.UI.LiteralControl | 50 | 0 | 0 |
| ctl00 | System.Web.UI.HtmlControls.HtmlHead | 46 | 0 | 0 |
| ctl01 | System.Web.UI.HtmlControls.HtmlTitle | 33 | 0 | 0 |
| ctl03 | System.Web.UI.LiteralControl | 14 | 0 | 0 |
| form1 | System.Web.UI.HtmlControls.HtmlForm | 910736 | 0 | 0 |
| ctl04 | System.Web.UI.LiteralControl | 129 | 0 | 0 |
| ListBox1 | System.Web.UI. |
227258 | 196036 | 0 |
| ctl05 | System.Web.UI.LiteralControl | 47 | 0 | 0 |
| DropDownList1 | System.Web.UI. |
227246 | 196036 | 0 |
| ctl06 | System.Web.UI.LiteralControl | 32 | 0 | 0 |
| Button1 | System.Web.UI. |
78 | 0 | 0 |
| ctl07 | System.Web.UI.LiteralControl | 12 | 0 | 0 |
| ctl08 | System.Web.UI.LiteralControl | 20 | 0 | 0 |
Объем пересылаемой по каналам связи в оба конца информации получается огромным до неприличия. Причем с каждой новой отсылкой он катастрофически нарастает, добавляя к прежним сохраненным значениям сгенерированные в коде новые значения списков. Отключим сохранение состояния вида в списках, а для восстановления позиций выделенных пользователем опций воспользуемся тем фактом, что с каждым запросом эти значения передаются на сервер в словарь объекта страницы.
Перейдите в режим Design редактора страницы ViewStateList.aspx, выделите одновременно оба списка и через панель Properties сбросьте свойства EnableViewState
Запустите страницу ViewStateList.aspx и убедитесь, что объем сохраняемого состояния вида для списков стал нулевымЗамечание. Если количество запросов превысит заданное параметром трассировки requestLimit значение (по умолчанию 10), то ASP.NET при исполнении не будет включать результаты трассировки в страницу. В этом случае нужно при очередном запуске тестового браузера вызвать через строку адреса страницу trace.axd и щелкнуть на ссылке " этой страницы. Включить безлимитную трассировку можно более просто - добавить в @ Page страницы параметр
Мы видим, что при отключении сохранения состояния вида резко уменьшился и общий объем страницы. Но сервер в своих откликах не сохраняет выбранные клиентом опции списков. Исправим этот недостаток программно.
Добавьте к странице код восстановления выбора пользователя в списках<script runat="server">
// Поле класса страницы
private const int maxCount = 1000;
protected void Page_Load(object sender, EventArgs e)
{
// Заполняем списки программной генерацией значений
for (int i = 0; i < maxCount; i++)
{
ListBox1.Items.Add("Опция " + (i + 1).ToString());
DropDownList1.Items.Add("Опция " + (i + 1).ToString());
}
// Отслеживание выделенных пользователем опций списков
if (this.IsPostBack)
{
string text = this.Request.Form["ListBox1"];
ListBox1.Items.FindByText(text).Selected = true;
text = this.Request.Form["DropDownList1"];
DropDownList1.Items.FindByText(text).Selected = true;
}
else
ListBox1.SelectedIndex = DropDownList1.SelectedIndex = 0;
}
</script>
< из
<select size="4" name="ListBox1" id="ListBox1" style="height:200px;width:150px;"> <option selected="selected" value="Опция 1">Опция 1</option> <option value="Опция 2">Опция 2</option> <option value="Опция 3">Опция 3</option> .................................................. </select>
Фактически ListBox1=Опция 1, которая попадает в словарь , доступный для извлечения и использования в коде страницы на стороне сервера. Мы находим это значение в сгенерированной коллекции значений списка и настраиваем этот элемент коллекции как выделенный. После таких настроек список сгенерирует в текущем отклике сервера нужный
Запустите страницу ViewStateList.aspx с включенной трассировкой и убедитесь, что выбранные опции списков сохраняют свои позиции и ASP.NET генерирует небольшой объем клиентского кодаТаким образом мы выяснили, что состояние вида элементов управления, которое включено по умолчанию, не всегда является полезным либо просто не используется, и тогда его лучше отключать. Отключить состояние вида можно следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation debug="true" />
<pages enableViewState="false" />
</system.web>
</configuration>
@ Page параметр EnableViewState="false" (в коде свойство Page.EnableViewState = false )EnableViewState="false" (в коде свойство Элемент.EnableViewState = false )Если более общая настройка сохранения состояния вида выключена, а для каких-то страниц или элементов управления ее нужно включить, то в соответствующее место следует добавить атрибут EnableViewState="true".
Состояние вида передается клиенту системой ASP.NET в
Добавьте к проекту новую страницу (без файла отделенного кода) с именем DisplayViewState.aspx и установите ее стартовой
Удалите из интерфейсной части страницы <div></div>
Включите трассировку, добавив в @ Page страницы параметр Trace =true
Добавьте в блок скриптов кодовую часть, чтобы окончательно страница стала такой(рис ) Окончательный код страницы DisplayViewState.aspx<%@ Page Language="C#" Trace="true" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Объекты создаем каждый раз заново
Label label = new Label();
this.form1.Controls.Add(label);
this.form1.Controls.Add(new HtmlGenericControl("br"));
Button button = new Button();
this.form1.Controls.Add(button);
if (!this.IsPostBack)
{
// Один раз настроили, а дальше работает состояние вида
label.Text = "Привет всем!!!";
button.Text = "Отправить";
}
}
protected void Page_PreRender(object sender, EventArgs e)
{
// Вывод на страницу строки состояния
if (this.IsPostBack)
{
// Извлекаем строку состояния
string strViewState = this.Request.Form["__VIEWSTATE"];
// Добавляем вывод к родительской форме
Label label = new Label();
this.form1.Controls.Add(label);
label.Text = "<p><b>Строка состояния как есть:</b><br />"
+ strViewState + "</p>";
label.EnableViewState = false;// Чтобы не влияла на результат
// Преобразуем строку из Base64 в массив ASCII-символов
byte[] ascii = Convert.FromBase64String(strViewState);
// Вставляем в отклик
Response.Write("<b>Строка состояния в формате ASCII:</b><br />");
Response.BinaryWrite(ascii);
// То же самое, только по турецки...
// Десериализация и отображение строки
//string decodedViewState = System.Text.Encoding.ASCII.GetString(ascii);
//Response.Write(decodedViewState);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Запустите страницу DisplayViewState.aspx и повыполните обратную отсылкуHTML-отклик на стороне клиента, сгенерированный страницей DisplayViewState.aspx при обратной отсылке, будет выглядеть примерно так

Мы видим, что некоторая информация, сохраняемая на клиенте в строке состояния вида, может быть частично расшифрована.
Поскольку состояние вида формирует только серверная сторона, то перед включением строки в @ Page параметром EnableViewStateMac="true".
Для большей защиты строки состояния вида можно использовать встроенное шифрование, которое включается на странице параметром ViewStateEncryptionMode="Always" @ Page или установкой в
<pages viewStateEncryptionMode="Always">
Включите шифрование и выполните страницу DisplayViewState.aspx. Сгенерированный отклик на обратную отсылку станет примерно таким
Теперь строка состояния стала зашифрованной и ее трудно расшифровать. Однако не рекомендуется без надобности
Во вкладке Standard панели Toolbox имеется элемент управления HiddenField, который генерирует Value. Этот элемент не поддерживает шифрование, хэширование и хранение по частям. Поэтому клиенты в исходном коде HTML-вывода могут просматривать эти данные. Данные в скрытом поле хранятся до тех пор, пока пользователь не перейдет к другой странице.
Добавьте к проекту новую страницу с именем HiddenFieldTest.aspx без файла отделенного кода и назначьте ее стартовой
Переведите страницу в режим Design и Page_Load()
Поместите на Web-форму из вкладки Standard панели Toolbox два элемента управления HiddenField и присвойте им имена beginTime и countRequest
Отключите для всех элементов страницы механизм сохранения состояния вида, добавив в @ Page параметр EnableViewState="false"
Заполните страницу следующим кодом(рис ) Код страницы HiddenFieldTest.aspx<%@ Page Language="C#" EnableViewState="false" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Создаем текстовую метку
lblTime = new Label();
div1.Controls.Add(lblTime);
div1.Controls.Add(new HtmlGenericControl("br"));
// Создаем кнопку CurrentTime
Button button = new Button();
div1.Controls.Add(button);
// Подписываемся на событие кнопки
button.Click += CurrentTimeClick;
button.Text = "Текущее время";
// Добавляем промежуток между кнопками
Literal literal = new Literal();
literal.Text = "nbsp;nbsp;nbsp;nbsp;nbsp;";
div1.Controls.Add(literal);
// Создаем кнопку OldTime
button = new Button();
div1.Controls.Add(button);
// Подписываемся на событие кнопки
button.Click += OldTimeClick;
button.Text = "Начальное время";
// Инициализируем скрытые поля и метку при первом запросе
if (!this.IsPostBack)
{
beginTime.Value = System.DateTime.Now.ToLongTimeString();
OldTimeClick(null, EventArgs.Empty);
}
else
{
// Ведение счетчика запросов страницы
int.TryParse(countRequest.Value, out count);
count++;
// Или то же самое
// count = int.Parse(countRequest.Value) + 1;
}
countRequest.Value = count.ToString();
}
// Вынесены в поля класса для видимости в обработчиках
int count = 0;
Label lblTime;
void CurrentTimeClick(object sender, EventArgs e)
{
lblTime.Text = "Текущее время: " + System.DateTime.Now.ToLongTimeString();
lblTime.Text += "<br />Ответ № " + count.ToString();
}
void OldTimeClick(object sender, EventArgs e)
{
lblTime.Text = "Начальное время: " + beginTime.Value;
lblTime.Text += "<br />Ответ № " + count.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center" id="div1" runat="server">
<h1 style="color: Red">
Испытание элемента HiddenField
</h1>
<asp:HiddenField ID="beginTime" runat="server" />
<asp:HiddenField ID="countRequest" runat="server" />
</div>
</form>
</body>
</html>
Исполните страницу и убедитесь, что HiddenField, в пределах одной страницы работают исправноОдно из самых серьезных ограничений состояния вида состоит в том, что оно работает только на одной странице. При переходе пользователя на другую страницу состояние вида прежней страницы утрачивается. Одним из выходов является передача информации в
GET вместо POST Отправляющая запрос страница инициирует передачу либо с помощью ), либо с помощью кнопки Submit, в обработчик которой вставлен код . Для извлечения информации из .
Добавьте к проекту страницу без файла отделенного кода с именем SourceQueryString.aspx и назначьте ее стартовой
Добавьте внутрь контейнера <div></div> <h1 style="color: Red">Введите учетную запись</h1>
Дополните открывающий <div> стилем центрирования<div style="text-align: center">
Создайте на Web-форме позиционирующую таблицу размером 3x2 и поместите в ее ячейки из вкладки Standard панели Toolbox серверные элементы с настройками согласно таблице| Элемент | Свойство |
|---|---|
|
ID="Label1" |
Text="Имя:" |
|
TextBox |
ID="Name" |
|
ID="Label2" |
Text="Пароль:" |
|
TextBox |
ID="Password" |
TextMode="Password" |
|
|
ID="Button1" |
Text="Отправить" |
Интерфейс страницы в режиме проектирования будет таким

Переведите страницу в режим Design и Button1 создайте для него обработчик. Заполните обработчик следующим кодомprotected void Button1_Click(object sender, EventArgs e)
{
string Url = "TargetQueryString.aspx?Name="
+ Server.UrlEncode(Name.Text)
+ "Password="
+ Server.UrlEncode(Password.Text);
this.Response.Redirect(Url);
// То же самое, только с прямой переадресацией
// this.Server.Transfer(Url);
}
Кнопка будет инициировать обратную отсылку, а ее обработчик сформирует адрес целевой страницы со значениями параметров, снятых с элементов текстовых полей. Перед включением в строку адреса введенных пользователем значений мы применяем
Переведите страницу в режим Design и Page_Load(). Заполните обработчик кодом, восстанавливающий состояние текстового поля Name сохраненным в protected void Page_Load(object sender, EventArgs e)
{
if (this.Request.QueryString["Name"] != null !this.IsPostBack)
Name.Text = this.Request.QueryString["Name"];
}
Страница SourceQueryString.aspx, отправляющая
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
string Url = "TargetQueryString.aspx?Name="
+ Server.UrlEncode(Name.Text)
+ "Password="
+ Server.UrlEncode(Password.Text);
this.Response.Redirect(Url);
// То же самое с прямой переадресацией
// this.Server.Transfer(Url);
}
protected void Page_Load(object sender, EventArgs e)
{
if (this.Request.QueryString["Name"] != null !this.IsPostBack)
Name.Text = this.Request.QueryString["Name"];
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<h1 style="color: Red">
Введите учетную запись</h1>
<table border="0" width="100%">
<tr>
<td style="text-align: right; width: 412px;">
nbsp;<asp:Label ID="Label1" runat="server" Text="Имя:" />
</td>
<td>
nbsp;<asp:TextBox ID="Name" runat="server" />
</td>
</tr>
<tr>
<td style="width: 412px; text-align: right">
nbsp;<asp:Label ID="Label2" runat="server" Text="Пароль:" />
</td>
<td>
nbsp;<asp:TextBox ID="Password" runat="server" TextMode="Password" />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center">
nbsp;<asp:Button ID="Button1" runat="server" Text="Отправить"
OnClick="Button1_Click" />
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Теперь нужно создать принимающую запрос (целевую) страницу. На целевой странице значения поступивших параметров, извлекаемые из
Создайте страницу с совмещенным кодом и именем TargetQueryString.aspx
Заполните страницу следующим кодом<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Критерии оценки зарегистрированного пользователя
const string NAME = "USER", PASSWORD = "ROOT";
// Извлекаем и раскодируем поступившие данные из строки запроса
string name = this.Request.QueryString["Name"];
name = this.Server.UrlDecode(name);
string password = this.Request.QueryString["Password"];
password = this.Server.UrlDecode(password);
// Создаем текстовую метку для отображения решения
Label label = new Label();
form1.Controls.Add(label);
// Резервируем место для размещения гиперссылки
PlaceHolder placeHiperLink = new PlaceHolder();
form1.Controls.Add(placeHiperLink);
// Создаем муляж кнопки
Button button = new Button();
form1.Controls.Add(button);
// Анализируем данные и принимаем решение
StringBuilder message = new StringBuilder();
if (name.ToUpper() == NAME password.ToUpper() == PASSWORD)
{
// Просто выведем хвалебное приветствие
message.Append("<h1 style=\"color: Red\">");
message.Append("Приветствуем Вас, дорогой " + name + "!");
message.Append("</h1>");
// Отключаем у кнопки способность инициировать отсылку
// Лень посылать дальше зарегистрированного пользователя
button.PostBackUrl = "javascript: void(0)";
button.Text = "Дальше >";
// Добавляем в зарезервированное место гиперссылку
HyperLink link = new HyperLink();
placeHiperLink.Controls.Add(link);
link.Text = "< Назад";
link.NavigateUrl = "~/SourceQueryString.aspx?"
+ "Name=" + name;
// Добавляем в зарезервированное место пробелы
Label lblSpace = new Label();
placeHiperLink.Controls.Add(lblSpace);
lblSpace.Text = "nbsp;nbsp;nbsp;nbsp;";
}
else
{
// Поругаем и отправим на повторную регистрацию
message.Append("<h1>");
message.Append("Вы неопознаны!<br />Повторите регистрацию...");
message.Append("</h1>");
button.PostBackUrl = "~/SourceQueryString.aspx?"
+ "Name=" + name;
button.Text = "< Назад";
}
// Выводим сформированное сообщение
label.Text = message.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Обратите внимание, что интерфейсная часть целевой страницы совершенно пустая - все создается динамически.
Запустите страницу SourceQueryString.aspx на выполнение и убедитесь, что механизм межстраничного обмена информацией через Страница TargetQueryString.aspx на клиенте при наведении курсора мыши на
В классах кнопочных элементов управления , и LinkButton имеется свойство PostBackUrl, позволяющее сразу адресоваться к целевой странице с доступом в ней ко всем данным текущей страницы. Свойству PostBackUrl кнопки обратной отсылки присваивается строка с URL целевой страницы, которая и будет загружена на выполнение по щелчку пользователя на этой кнопке. При этом на целевой странице, благодаря ее свойству PreviousPage, будет полностью доступен объект страницы, инициировавшей обратную отсылку.
Идея реализации механизма межстраничной отсылки достаточно проста. Любая страница на этапе описания является классом (инструкцией) с именем, равном имени страницы, которая при загрузке в память сервера превращается в объект (экземпляр класса), адресуемый средой исполнения. Исполняемый объект проходит все необходимые этапы жизни, начиная с инициализации элементов управления, корректировкой состояния, выполнением обработчиков, и заканчивая генерацией
То же самое происходит и со страницей, загруженной по кнопке с ненулевым свойством PostBackUrl. Она также проходит все начальные стадии жизни, включая выполнение обработчиков. Непосредственно перед генерацией PreviousPage среда исполнения записывает адрес объекта первой страницы.
Теперь код целевой страницы может адресоваться к общедоступным данным исходной страницы, но рендеринг для клиента будет генерироваться только второй страницей. Когда целевая страница заканчивает свой цикл жизни, вместе с ней из памяти выгружается и исходная страница. Для того, чтобы закрытые или защищенные члены класса исходной страницы стали доступными в классе целевой страницы, их нужно обернуть в общедоступные свойства или функции доступа.
Рассмотренная межстраничная связь может быть реализована не только как "один к одному", но и в других сочетаниях: "один ко многим", "многие к одному" или "многие ко многим". Множественность на исходной странице может быть организована программным формированием значения свойства PostBackUrl в зависимости от выбранных на этой странице предпочтений. Множественность на целевой странице реализуется проверкой того, к какому типу (к какой странице) относится записанный в свойство PreviousPage объект.
Межстраничная обратная отсылка целевой страницы может быть инициирована не только по щелчку на кнопке с ненулевым значением свойства PostBackUrl, но и в любом месте кода вызовом соответствующей
public void Page.Server.Transfer(string path, bool preserveForm)
где
path - вызываемая целевая страницаpreserveForm - флаг передачи в целевую страницу данных QueryString и Form Оптимальным будет вызов
Server.Transfer(Url, true);
Проиллюстрируем межстраничную отсылку на примере двух страниц-источников и двух страниц-приемников.
Добавьте к проекту страницу с совмещенным кодом и именем SourcePostBack1.aspx
Назначьте страницу стартовой и оформите пользовательский интерфейс на 
Здесь использованы серверные элементы , TextBox, ListBox, и .
Для элемента управления ListBox включите свойство AutoPostBack
Заполните страницу следующим кодом(рис ) Полный код страницы SourcePostBack1.aspx<%@ Page Language="C#" %>
<script runat="server">
// Функция доступа к закрытому полю класса из внешнего кода
public string GetName()
{
return txtName.Text;
}
// Свойство доступа к закрытым данным класса из внешнего кода
public string GetTitle
{
get { return lblTitle.Text; }
}
string text;
protected void Page_Load(object sender, EventArgs e)
{
// Просто отображаем страницу
if (lstPost.SelectedItem == null)
return;
// Выбираем целевую страницу при AutoPostBack списка
text = lstPost.SelectedItem.Text;
if (String.Compare(text, "Студент", true) == 0)
btnPostBackUrl.PostBackUrl = "~/CrossPageStudent.aspx";
else
btnPostBackUrl.PostBackUrl = "~/CrossPageLecturer.aspx";
}
protected void btn_Click(object sender, EventArgs e)
{
// Контролируем наличие выбора при щелчках на кнопках
if (lstPost.SelectedItem == null)
{
String mess = "<script>alert('Выберите сотрудника')</" + "script>";
this.Response.Write(mess);
return;
}
text = lstPost.SelectedItem.Text;
// Различаем кнопки
Button btn = (Button)sender;
if (String.Compare(btn.Text, "PostBackUrl", true) != 0)
{
if (String.Compare(text, "Студент", true) == 0)
this.Server.Transfer("~/CrossPageStudent.aspx", true);
else
this.Server.Transfer("~/CrossPageLecturer.aspx", true);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<asp:Label ID="lblTitle" runat="server" Text="Это первая страница-источник"></asp:Label>
<br />
<br />
Фамилия:nbsp;
<asp:TextBox ID="txtName" runat="server">Иванов</asp:TextBox>nbsp;
<asp:ListBox ID="lstPost" runat="server" Height="45px" AutoPostBack="True">
<asp:ListItem>Студент</asp:ListItem>
<asp:ListItem>Доцент</asp:ListItem>
</asp:ListBox>
<br />
<br />
<asp:Button ID="btnPostBackUrl" runat="server" Text="PostBackUrl" OnClick="btn_Click" />nbsp;
<asp:Button ID="btnServerTransfer" runat="server" OnClick="btn_Click"
Text="Server.Transfer()" />
<br />
<br />
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="SourcePostBack2.aspx">
На вторую страницу-источник</asp:HyperLink>
</div>
</form>
</body>
</html>
Получите копию страницы SourcePostBack1.aspx под именем SourcePostBack2.aspx и скорректируйте в ней элементы lblTitle и HyperLink1
Создайте новую страницу с именем CrossPageStudent.aspx без файла отделенного кода, в которой блок скриптов заполните так(рис ) Полный код страницы-приемника данных CrossPageStudent.aspx<%@ Page Language="C#" %>
<script runat="server">
// Поля, заполняемые данными вызвавшей страницы
String name, title, className;
// Наша функция, использующая доступ с помощью механизма отражения типов
void GetInfoPreviousPage()
{
// Получаем тип запускающей страницы для
// динамического вызова членов представляющего класса
System.Type typePreviousPage = this.PreviousPage.GetType();
// Находим общедоступный метод класса
System.Reflection.MethodInfo methodGetName =
typePreviousPage.GetMethod("GetName");
// Выполняем метод предыдущей страницы
name = methodGetName.Invoke(this.PreviousPage, null).ToString();
// Находим общедоступное свойство класса
System.Reflection.PropertyInfo propertyGetTitle =
typePreviousPage.GetProperty("GetTitle");
// Выполняем свойство предыдущей страницы
title = propertyGetTitle.GetGetMethod().
Invoke(this.PreviousPage, null).ToString();
// Извлекаем имя класса предыдущей страницы
className = this.PreviousPage.GetType().Name;
}
protected void Page_Load(object sender, EventArgs e)
{
// Получаем информацию с предыдущей страницы
GetInfoPreviousPage();
Label label = new Label();
form1.Controls.Add(label);
string text = "<h1 style='text-align: center; color: Red'>";
if (this.PreviousPage == null)
{
text += "Страница запущена напрямую</h1>";
label.Text = text;
return;
}
text += "Методом межстраничной обратной отсылки получены данные</h1>";
text += "<ul>";
text += "<li>Заголовок: " + title + "</li>";
if (String.Compare(className, "SourcePostBack1_aspx", true) == 0)
text += "<li>Данные прислала первая страница</li>";
else if (String.Compare(className, "SourcePostBack2_aspx", true) == 0)
text += "<li>Данные прислала вторая страница</li>";
text += "<li>Фамилия студента: " + name + "</li>";
if (this.PreviousPage.IsCrossPagePostBack)
text += "<li>Обратная отсылка инициирована свойством PostBackUrl</li>";
else
text += "<li>Обратная отсылка инициирована методом Server.Transfer()</li>";
text += "</ul>";
label.Text = text;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Получите копию страницы CrossPageStudent.aspx под именем CrossPageLecturer.aspxЭти обе страницы-приемники имеют совершенно одинаковый код и просто демонстрируют работу схемы межстраничной GetInfoPreviousPage(), в которую мы упаковали код, извлекающий данные из класса страницы-источника на этапе выполнения. Этот прием применен потому, что при прямой
Разберитесь с кодом отсылающей и принимающей сторон и испытайте их работуВариант результата на принимающей странице будет примерно таким
Куки-наборы являются еще одним распространенным способом хранения информации на клиенте. Они, подобно состоянию вида, также включают в себя текстовую информацию, но хранят ее не в
Однако клиент имеет возможность удалить куки, отключить в и , каждый из которых имеет коллекцию (набор) типа HttpCookieCollection. Поступившие от клиента куки нужно извлекать из коллекции , а отправляемые на клиента новые данные для их сохранения в куках необходимо добавлять в коллекцию .
Коллекции состоят из отдельных объектов-экземпляров класса HttpCookie, каждый из которых является словарем. При создании объекта-словаря в конструкторе HttpCookie(string) задается имя кука, под которым он будет адресоваться в коллекции. Перегруженная версия HttpCookie(string, string) позволяет при создании имени кука определять и его единственное значение.
Важным свойством объекта является , которое определяет срок хранения куки-файла на клиенте. Если будет храниться в памяти, пока пользователь не закроет на клиенте с длительным сроком хранения нужно перезаписать в него просроченную дату.
В качестве примера разработаем страницу, на которой значения некоторых интерфейсных элементов управления будем хранить в .
Добавьте к проекту страницу без отделенного кода с именем CookiesPage1.aspx и назначьте ее стартовойНикакие элементы в режиме проектирования мы на странице размещать не будем, а все создадим и настроим динамически, чтобы еще раз подчеркнуть возможности программирования. В итоге код нашей страницы будет выглядеть так
<%@ Page Language="C#" ValidateRequest="false" %>
<script runat="server">
// Вынесли как поле для видимости в обработчиках
// Инициализировать необязательно, компилятор сам справится
TextBox txtName;
String result = "";
int count = 0;
protected void Page_Load(object sender, EventArgs e)
{
this.Response.Write("<h1 style='color: Red;'>Испытание Cookies</h1>");
Label label = new Label();
label.Text = "Введите ФИО:nbsp;nbsp;";
form1.Controls.Add(label);
txtName = new TextBox();
txtName.Text = "Снетков В.М.";
form1.Controls.Add(txtName);
form1.Controls.Add(new HtmlGenericControl("br"));
Button button = new Button();
button.Text = "Отправить";
form1.Controls.Add(button);
button = new Button();
button.Text = "Сохранить";
form1.Controls.Add(button);
button.Click += btnSaveClick;
button = new Button();
button.Text = "Восстановить";
form1.Controls.Add(button);
button.Click += btnRestoreClick;
form1.Controls.Add(new HtmlGenericControl("br"));
button = new Button();
button.Text = "Удалить";
form1.Controls.Add(button);
button.Click += btnDeleteClick;
form1.Controls.Add(new HtmlGenericControl("br"));
HyperLink link = new HyperLink();
form1.Controls.Add(link);
// Определяем имя класса текущей страницы и формируем гиперссылку
String nameCurrentPage = this.GetType().Name;
// Разбиваем строку на части
String[] parts = nameCurrentPage.Split('_');
// Сравниваем первую часть без учета регистра
if (String.Compare(parts[0], "CookiesPage1", true) == 0)
link.NavigateUrl = "CookiesPage2" + ".aspx";
else
link.NavigateUrl = "CookiesPage1" + ".aspx";
link.Text = "Перейти к " + link.NavigateUrl;
// Отключаем сохранение состояния вида всех элементов страницы
DisabledViewState(form1.Controls);
// Извлекаем общее число посещений
HttpCookie cookie = this.Request.Cookies["count"];
if (cookie != null)
{
int.TryParse(cookie.Value, out count);
}
count++;
// Адресуемся к отклику и сохраняем общее число посещений
cookie = this.Response.Cookies["count"];
cookie.Value = count.ToString();
// Устанавливаем срок хранения на клиенте: от текущего 1 день
cookie.Expires = DateTime.Now.AddDays(1);
// Отображаем сообщение на странице пользователя
result = "Текущий запрос: " + count.ToString();
}
// Отключаем механизм сохранения состояния вида для всех элементов
void DisabledViewState(ControlCollection controls)
{
foreach (Control ctrl in controls)
{
// Отключаем сохранение состояния вида элемента
if (ctrl.EnableViewState)
ctrl.EnableViewState = false;
// Продолжаем опрашивать рекурсивно, если есть дочерние элементы
if (ctrl.Controls != null)
DisabledViewState(ctrl.Controls);
}
}
// Переопределяем обработчик генерации HTML-вывода страницы
protected override void Render(HtmlTextWriter output)
{
// Центрируем динамически созданные элементы управления
output.RenderBeginTag(HtmlTextWriterTag.Center);
base.Render(output);
output.Write("<hr />");
output.Write(result);
output.RenderEndTag();
}
// Сохраняем данные в куке "oldInfo"
void btnSaveClick(object sender, EventArgs e)
{
// Создаем словарь и заполняем текущей информацией
HttpCookie cookie = new HttpCookie("oldInfo");
// Кодировать необязательно, направляется в файл
cookie["name"] = txtName.Text;
cookie["clicks"] = count.ToString();
// Присоединяем к ответу
// Срок хранения - текущий сеанс
// cookie.Expires = DateTime.Now.AddYears(1);
this.Response.Cookies.Add(cookie);
}
// Извлекаем данные из кука "oldInfo"
void btnRestoreClick(object sender, EventArgs e)
{
// Подключаемся к кукам поступившего запроса
HttpCookieCollection cookies = this.Request.Cookies;
// Извлекаем нужный словарь
HttpCookie cookie = cookies["oldInfo"];
if (cookie != null)
{
// Извлекаем значения словаря
string name, clicks;
name = cookie["name"];
clicks = cookie["clicks"];
// Альтернативный синтаксис
// name = cookies["oldInfo"]["name"];
// clicks = cookies["oldInfo"]["clicks"];
// Если готовим к отправке, то можно...
// cookies["oldInfo"].Expires = DateTime.Now.AddYears(1);
// Безопасно отображаем сохраненные значения,
// кодируя интерпретируемые броузером символы
name = Server.HtmlEncode(name);
clicks = Server.HtmlEncode(clicks);
result = "Сохраненное ФИО: " + name;
result += "; cохраненный запрос: " + clicks;
}
else
{
result = "Нечего восстанавливать! ";
result += "Текущий запрос: " + count.ToString();
}
}
// Удаляем кук "count"
void btnDeleteClick(object sender, EventArgs e)
{
// Удаляем кук количества запросов, посылая просроченную дату
this.Response.Cookies["count"].Expires =
DateTime.Now.AddDays(-1);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
Для того, чтобы продемонстрировать, что куки сохраняются и при переходе к другим страницам, создадим еще одну страницу, совершенно идентичную первой.
Создайте в проекте копию страницы CookiesPage1.aspx и присвойте ей имя CookiesPage2.aspx
Запустите первую страницу CookiesPage1.aspx и испытайте ее работу совместно со второй страницейВид страницы на клиенте будет таким
Механизм является очень удобным, но неустойчивым, поскольку может либо не поддерживаться
Информация о функциональности System.Web.HttpBrowserCapabilities, ссылка на который находится в свойстве Page. объекта страницы. Эта информация включает в себя тип
К сожалению эта информация свидетельствует только о потенциальных возможностях
Что касается , а затем в поступившем запросе попытаться прочитать его. Если такая попытка не удастся, значит поддержка
В качестве иллюстрации к сказанному создайте страницу с именем IsCookiesSupported.aspx и заполните ее следующим кодом<%@ Page Language="C#" ClassName="IsCookiesSupported" EnableViewState="false" %>
<script runat="server">
bool CookiesSupportedFlag;
int clicks = 1;
protected void Page_Load(object sender, EventArgs e)
{
// Создаем объекты пользовательского интерфейса
// Текстовая метка заголовка
Label label = new Label();
form1.Controls.Add(label);
label.Text = "<h2>Поддерживаются ли Cookies?</h2>";
// Кнопка Submit
Button submit = new Button();
form1.Controls.Add(submit);
submit.Text = "Отправить";
// Горизонтальная линия
form1.Controls.Add(new HtmlGenericControl("hr"));
// Текстовая метка результатов
label = new Label();
form1.Controls.Add(label);
if (!this.IsPostBack)
{
// Проверяем функциональность броузера на поддержку Cookies
CookiesSupportedFlag = this.Request.Browser.Cookies;
if (CookiesSupportedFlag)
{
// Для дополнительной проверки пробуем отправить cookie
this.Response.Cookies["tmp"].Value = String.Empty;
}
// this.Response.Redirect(Request.Url.ToString());// Зацикливает
// Дублируем сохранение в состоянии вида
this.ViewState["clicks"] = clicks.ToString(); ;
label.Text = "Пробный запрос страницы";
return;
}
else if (this.Request.Cookies["tmp"] != null)
{
// Поднимаем флаг поддержки cookies
CookiesSupportedFlag = true;
}
else
CookiesSupportedFlag = false;
// Принимаем решение, откуда извлекать и где сохранять данные
if (CookiesSupportedFlag)
{
if (this.Request.Cookies["clicks"] != null)
clicks = int.Parse(this.Request.Cookies["clicks"].Value) + 1;
this.Response.Cookies["clicks"].Value = clicks.ToString();
// Устанавливаем срок хранения
this.Response.Cookies["clicks"].Expires =
DateTime.Now.AddMinutes(1.0);
}
else
{
clicks = Convert.ToInt32(this.ViewState["clicks"]) + 1;
this.ViewState["clicks"] = clicks.ToString();
}
// Показываем пользователю количество щелчков и механизм хранения
label.Text = "Запрос страницы № " + clicks.ToString();
if (CookiesSupportedFlag)
label.Text += "<br />Хранение в cookies";
else
label.Text += "<br />Хранение в ViewState";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
" при записи пометить
Response.Cookies["clicks"].Path = "~/MyFolder";
то кук будет доступен только страницам папки MyFolder. По умолчанию кук доступен любым страницам домена, как и, например, при установке
Response.Cookies["clicks"].Domain = "mysite.ru";
Перейдите ко второй части ...
Любой пользователь при первом обращении к приложению получает свой сеанс, который выражается в выделении некоторого объема памяти на стороне сервера для . За счет этого можно сохранять информацию на одной странице, а получать к ней доступ с другой страницы.
При поступлении первого клиентского запроса на объект страницы ASP.NET генерирует идентификатор сеанса размером 42 байта. Этот идентификатор сеанса включается в отклик сервера и является уникальным в пределах приложения. Если ни одна из страниц приложения, к которым обращается пользователь, не записывает в объект , идентификатор сеанса сразу запоминается .
Когда клиент присылает идентификатор сеанса, сервер по нему автоматически извлекает из места созданной страницы. После чего данные сеанса становятся доступными в коде этой страницы.
Идентификатор сеанса может пересылаться серверу с запросом двумя способами:
ASP .NET_SessionIdИдентификатор сеанса можно получить как значение - при формировании первого отклика, или - всегда. Но конкретное его значение нас, как правило, не интересует, поскольку процесс распознавания выполняется автоматически.
Данные сеанса могут сохраняться ASP.NET в одном из трех мест на сервере, определяемых свойством :
Session .Abandon ()Сеанс является экземпляром класса System.Web.SessionState.HttpSessionState и может частично управляться из кода. В таблице приведены некоторые свойства и HttpSessionState
| Член | Описание |
|---|---|
|
Количество элементов в коллекции текущего сеанса. Только для чтения |
IsCookieless |
Булев флаг, показывающий способ хранения идентификатора сеанса: в куках или в модифицированном URL. Только для чтения |
IsNewSession |
Булево значение, которое указывает, был ли сеанс создан с текущим запросом. Если коллекция пуста или сеанс создан ранее, то возвращается false. Только для чтения |
|
Свойство (только для чтения) содержит одно из значений перечисления SessionStateMode, указывающее способ Off = 0 - сеанс не используетсяInProc = 1 - данные сеанса хранятся в памяти доменаStateServer = 2 - сеанс хранится в специальной службе Windows под названием ASP.NET State ServiceSQLServer = 3 - сеанс хранится в базе данных SQL Server - сеанс хранится на основе нестандартного поставщика |
SessionID |
Строка, содержащая уникальный идентификатор сеанса. Только для чтения |
|
Максимальный срок простоя сеанса в минутах. Для чтения и записи |
|
Отменяет текущий сеанс |
Add(string name, object value) |
Добавляет новый элемент в коллекцию сеанса. Допускается |
|
Очищает коллекцию сеанса |
RemoveAll() |
Очищает коллекцию сеанса |
|
Удаляет |
RemoveAt(int index) |
Удаляет |
Начальное <sessionState>. Настройки секции <sessionState> поддерживаются классом System.Web. через его свойства, но имена свойств в кофигурационном файле начинаются с символа нижнего регистра. Вот некоторые наиболее важные из них
| Свойство | Описание |
|---|---|
Cookieless |
Задает режим использования System.Web.HttpCookieMode:UseUri = 0 - никогда не использовать cookieless="UseUri" или cookieless="true" )UseCookies = 1 - всегда использовать - наиболее надежный режим, в котором сервер сам определяет возможности UseDeviceProfile = 3 - способ пересылки идентификатора сеанса основывается только на потенциальной функциональности |
|
Задает одно из значений перечисления SessionStateMode, указывающее способ Off = 0 - указывает, что сеанс не задействуется для InProc = 1 - данные сеанса хранятся в памяти доменаStateServer = 2 - сеанс хранится в специальной службе Windows под названием ASP.NET State ServiceSQLServer = 3 - сеанс хранится в базе данных SQL Server - сеанс хранится на основе нестандартного поставщика |
|
Устанавливают время простоя сеанса в минутах |
RegenerateExpiredSessionId |
cookieless="true", когда идентификатор сеанса пересылается |
CookieName |
Устанавливает имя идентификатора сеанса в " |
При включенном режиме cookieless="true" с полным циклом. Для того, чтобы указать системе, что идентификатор сеанса должен передаваться секретным протоколом, нужно установить
Приведем пример использования сеанса.
Отредактируйте файл web.config приложения как показано ниже(рис ) Файл web.config с включенным режимом Cookieless<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation debug="true" />
<sessionState cookieless="true" regenerateExpiredSessionId="true" />
</system.web>
</configuration>
Добавьте страницу с именем SessionCookieless.aspx с совмещенным кодом, которую заполните так(рис ) Код страницы SessionCookieless.aspx<%@ Page Language="C#" EnableViewState="false" %>
<script runat="server">
int clicks = 1;
const int maxCol=2;
HtmlTable table;
protected void Page_Load(object sender, EventArgs e)
{
string[] headers = {
// Имена свойств секции sessionState
"Cookieless",
"Mode",
"Timeout",
"RegenerateExpiredSessionId",
"CookieName",
// Дополнительная информация
"SessionID",
"numRequest",
"currentTime"
};
// Создаем объекты пользовательского интерфейса
// Текстовая метка заголовка
Label label = new Label();
form1.Controls.Add(label);
label.Text = "<h2>Использование сеансов Session</h2>";
if (!IsPostBack)
{
// Получаем всю информацию о параметрах конфигурации
System.Configuration.Configuration config;
config =
System.Web.Configuration.WebConfigurationManager.
OpenWebConfiguration(this.Request.ApplicationPath);
// Объявляем ссылку на объект SessionStateSection
System.Web.Configuration.SessionStateSection sessionState;
// Поиск элемента <sessionState> в элементе <system.web>
sessionState = (System.Web.Configuration.SessionStateSection)
config.GetSection(@"system.web/sessionState");
//sessionState.Timeout = TimeSpan.FromMinutes(1);
// Формируем таблицу и сохраняем ее в сеансе
table = new HtmlTable();
table.Border = 1;
for (int i = 0; i < headers.Length; i++)
{
// Создаем строку
HtmlTableRow row = new HtmlTableRow();
table.Rows.Add(row);
// Создаем ячейки в строке
for (int j = 0; j < maxCol; j++)
{
HtmlTableCell col = new HtmlTableCell();
row.Cells.Add(col);
if (j == 0)// Левый столбец
{
col.InnerHtml = headers[i] + ":nbsp;";
col.Align = "right";
col.BgColor = "Yellow";// Цвет фона
}
else // Правый столбец
{
// Применяем методику отражения типов
System.Type type = typeof(System.Web.Configuration.
SessionStateSection);
// Извлекаем публичное свойство по его имени
System.Reflection.PropertyInfo property =
type.GetProperty(headers[i]);
if (property != null)
{
col.InnerHtml = "nbsp;";
// Извлекаем значение свойства из экземпляра класса
col.InnerHtml += property.GetGetMethod().
Invoke(sessionState, null).ToString();
}
else
{
// Маркируем ячейку, чтобы потом найти
col.ID = headers[i];
}
col.BgColor = "#CCFFFF";// Цвет фона
}
}
}
// Сохраняем информацию в сеансе при первом запросе
this.Session.Add("table", table);
this.Session["clicks"] = clicks;
}
else
{
// Извлекаем информацию из сеанса при каждой обратной отсылке
table = (HtmlTable)this.Session["table"];
clicks = (int)this.Session["clicks"];
// Сохраняем новое значение
this.Session["clicks"] = ++clicks;
}
// Добавляем таблицу на форму
form1.Controls.Add(table);
// Корректируем таблицу
((HtmlTableCell)Page.FindControl("SessionID")).InnerHtml =
"nbsp;" + this.Session.SessionID;
((HtmlTableCell)Page.FindControl("numRequest")).InnerHtml =
"nbsp;" + clicks.ToString();
((HtmlTableCell)Page.FindControl("currentTime")).InnerHtml =
"nbsp;" + DateTime.Now.ToLocalTime();
// Новая строка HTML
HtmlGenericControl br = new HtmlGenericControl();
br.InnerHtml = "<br />";
form1.Controls.Add(br);
// Кнопка Submit
Button submit = new Button();
form1.Controls.Add(submit);
submit.Text = "Отправить";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server" style="text-align: center;">
</form>
</body>
</html>
Клиентский результат будет таким

Обратите внимание, что идентификатор сеанса встроен в URL при значении параметра Cookieless="UseUri". Отметьте также, что интерфейсная часть страницы пустая - все создается динамически. Часть выводимой в таблицу информации мы добыли из объекта класса SessionStateSection, который создается по настройкам
Состояние приложения - это область памяти сервера, выделяемая при запуске приложения для хранения глобальных данных, доступных с любой страницы приложения. На эти данные в каждой странице ссылается объект-словарь Application типа System.Web.HttpApplicationState. Как и при
Данные состояния приложения существуют в течение всего периода работы приложения и теряются при перезапуске сервера или перехода приложения в новый домен. Для обеспечения
В качестве примера построим две страницы, в которых подсчитывается общее число посещений сайта с момента запуска приложения.
Добавьте к приложению файл Global.asax и заполните его так(рис ) Файл Global.asax<%@ Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
this.Application["clicks"] = (int)0;
}
</script>
Создайте страницу без отделенного кода с именем ApplicationState1.aspx, назначьте ее стартовой и заполните так(рис ) Файл ApplicationState1.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Читаем без проверки - инициализирована в global.asax
int visit = (int)this.Application["clicks"] + 1;
// Обновляем состояние приложения с блокировкой
this.Application.Lock();
this.Application["clicks"] = visit;
this.Application.UnLock();
// Создаем текстовую метку
Label label = new Label();
form1.Controls.Add(label);
label.Text = "Сегодня вы посетили нас в "
+ visit.ToString() + " раз!<br />";
// Создаем гиперссылку
HyperLink link = new HyperLink();
form1.Controls.Add(link);
link.NavigateUrl = "~/ApplicationState2.aspx";
link.Text = "На другую страницу...";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Создайте копию для страницы ApplicationState1.aspx с именем ApplicationState2.aspx и поменяйте в ней ApplicationState1.aspx, чтобы страницы ссылались друг на друга
Запустите само приложение несколько раз и пощелкайте по ссылкам чтобы убедиться, что счетчик количества визитов увеличивается
В панели задач нижней части экрана щелкните правой кнопкой мыши на пиктограмме тестового сервера и
Вновь запустите страницу ApplicationState1.aspx, чтобы убедиться, что счетчик сбросился в начальное значениеДля увеличения @ Application файла Global.asax параметр ClassName, в котором указывается имя
При изменении в страницах глобальных данных , чтобы на время позволить воздействовать на переменную только со стороны одного потока. Такой подход удобен тем, что мы можем обернуть сохраняемые данные в статические свойства и назначить для них контролирующий код, прежде чем сохранять их в памяти сервера. Мы можем объявлять статические переменные для хранения объектов любого типа и для доступа к ним
Приведем пример, в котором количество визитов всех пользователей, а также список файлов текущего физического
Отредактируйте код файла Global.asax следующим образом(рис ) Код файла Global.asax со статическими членами<%@ Application Language="C#" ClassName="MyAppClass" %>
<script runat="server">
// Список файлов
private static string[] fileList;// Базовое поле
// Общедоступное статическое свойство для обертки базового поля
public static string[] FileList
{
get
{
// Заполнить список только при первом запросе
if (fileList == null)
{
fileList = System.IO.Directory.GetFiles(
HttpContext.Current.Request.PhysicalApplicationPath);
}
return fileList;
}
}
// Число посещений
private static int visit = 1;// Базовое поле
// Общедоступное статическое свойство для обертки базового поля
public static int Visit
{
get { return visit; }
set
{
lock (typeof(int))// Контейнер обеспечения синхронизации потоков
{
visit = value;
}
}
}
</script>
Добавьте к приложению страницу без отделенного кода с именем AppClassStaticMembers.aspx и назначьте ее стартовой
Заполните страницу AppClassStaticMembers.aspx следующим кодом(рис ) Код страницы AppClassStaticMembers.aspx <%@ Page Language="C#" EnableViewState="false" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Заголовок
Label title = new Label();
form1.Controls.Add(title);
title.Text = "<h2 style='text-align: center; color: Red'>"
+ "Использование статических"
+ " полей класса приложения</h2>";
// Число посещений
Label visit = new Label();
form1.Controls.Add(visit);
int clicks = MyAppClass.Visit;
visit.Text = "<b>Количество посещений: "
+ clicks.ToString() + "</b>";
MyAppClass.Visit = ++clicks;
// Горизонтальная черта
form1.Controls.Add(new HtmlGenericControl("hr"));
// Извлекаем и готовим список файлов
// текущего каталога приложения
StringBuilder builder = new StringBuilder();
builder.Append("<ol>");
// Счетчик для сокращения листинга
int count = 0;
const int maxCount = 5;
foreach (string file in MyAppClass.FileList)
{
if (++count > maxCount)
break;
string str;
//int pos = file.LastIndexOf(@"\");
//str = file.Substring(pos + 1);
// То же самое
str = System.IO.Path.GetFileName(file);
builder.Append("<li>" + str + "</li>");
}
builder.Append("</ol>");
// Готовим текстовую метку для отображения списка
Label fileInfo = new Label();
form1.Controls.Add(fileInfo);
fileInfo.Text = builder.ToString();
// Горизонтальная черта
form1.Controls.Add(new HtmlGenericControl("hr"));
// Создаем дескриптор центрирования кнопки
HtmlGenericControl center = new HtmlGenericControl("center");
form1.Controls.Add(center);
// Кнопка Submit добавляется в контейнер центрирования
Button button = new Button();
button.Text = "Отправить";
center.Controls.Add(button);
}
// Переопределяем обработчик рендеринга
protected override void Render(HtmlTextWriter writer)
{
// Сгенерировать основное
base.Render(writer);
// Добавить в конец еще HTML-вывод
// Настроить атрибуты будущего дескриптора
writer.AddAttribute(HtmlTextWriterAttribute.Align, "Center");
writer.AddStyleAttribute(HtmlTextWriterStyle.Color, "Blue");
// Создать дескриптор
writer.RenderBeginTag(HtmlTextWriterTag.H2);
// Писать внутрь дескриптора
writer.Write("Щелкайте на кнопке и наблюдайте счетчик посещений");
// Закрыть дескриптор
writer.RenderEndTag();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Запустите страницу и убедитесь, что все работает и объявленные в
В панели задач нижней части экрана щелкните правой кнопкой мыши на пиктограмме тестового сервера и
Вновь запустите страницу AppClassStaticMembers.aspx, чтобы убедиться, что память сервера очистилась и счетчик сбросился в начальное значениеА сейчас мы убедимся, что при модификации любого файла приложения, включая web.config, оно автоматически перекомпилируется при первом запросе и загружается в новый домен.
Не нарушая кода, модифицируйте любой исходный файл приложения, например, введите пробел после точки с запятой в конце любой строки (или добавьте пустую строку) и сохраните изменения
Запустите приложение и убедитесь, что счетчик сбросился за счет загрузки приложения в новый доменИнтерфейс клиента для данного примера будет таким

Рассмотренные в данной теме способы
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.