Файлы к лабораторной работе Вы можете скачать здесь.
Главная особенность удаленных приложений состоит в том, что
они могут одновременно обрабатывать множество пользователей.
К тому же сервер может выполнять одновременно несколько разных
приложений. Поэтому среда исполнения устроена так, что после
обработки единичного запроса очередного пользователя память
сервера немедленно очищается от кода запрошенной страницы,
освобождая ресурсы для новых возможных
Такой факт вынуждает разработчиков Web-приложений изыскивать способы хранения информации о состоянии между запросами, с последующим ее извлечением и использованием. В ASP.NET существует два способа хранения информации между запросами: на клиенте и на сервере. Разработчик сайта сам решает, какую группу методов хранения данных состояния ему выбрать.
Хранение информации на клиенте имеет следующие преимущества:
Хранение информации на сервере имеет следующие преимущества:
Различают следующие методы хранения данных на клиенте:
ViewState типа System.Web.UI.StateBag, наследуемом от класса Control любым элементом управления и самой страницей.POST (по щелчку кнопки Submit ), а не
методом GET (по щелчку на гиперссылке).URL.
Видимы для пользователя в поле адреса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.
Он поддается .
В этом упражнении будем использовать программирование словаря самой
страницы. Мы создадим форму с интерфейсными элементами,
в которых отключим автоматическое сохранение состояния вида,
а будем его сохранять и восстанавливать вручную.
Интерфейсные
элементы TextBox мы позиционируем в серверной
таблице и они будут размещены в коллекции Page.Controls.
Мы будем
использовать рекурсивную логику для опроса всех дочерних элементов
таблицы, чтобы выявить объекты TextBox.
В качестве ключа для доступа к элементам словаря уровня
страницы будем использовать TextBox, а сохранять будем значение свойства Text.
Создайте
пустой проект с именем ViewState командой File/New/Web Site 
Добавьте
к проекту новую Web-страницу с отделенным кодом и именем ViewStateTest.aspx,
назначьте ее стартовой
Запустите
командой Website/ASP.NET Configuration страницу WAT (Web Site Application Configuration/Configure debugging and tracing
На
появившейся странице включите флажок Enable debugging и
закройте WAT Таким действием мы создали файл конфигурации Web. с
настройкой отладки.
В панели Solution Explorer щелкните на кнопке Refresh , чтобы увидеть
файл конфигурации в составе проекта
Откройте
интерфейсную часть страницы ViewStateTest.aspx в
режиме Source и вставьте внутрь дескрипторного
блока < div></div> следующий
заголовок<h1 style="color:
Red">Заполните анкету</h1>
Добавьте
в открывающую часть парного дескриптора <div> внутренний
стиль<div style="text-align:
center">
Откройте
интерфейсную часть страницы ViewStateTest.aspx в
режиме Design и поместите на нее элемент Table из вкладки HTML панели Toolbox
Щелкните
на элементе Table правой кнопкой мыши и
выполните опцию Run As Server Control,
чтобы превратить его в серверный
Откорректируйте
интерфейсный HTML-код заготовки таблицы следующим образом<table id="Table1" 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>
Поместите
после таблицы три кнопки Button из вкладки Standard панели Toolbox и
присвойте им имена cmdSubmit, cmdSave и cmdRestore соответственно
Поместите
в первую строку и первый столбец таблицы приведенный ниже
текст, а в остальные ячейки второго столбца - элемент TextBox из
вкладки Standard панели Toolbox После этих действий интерфейс страницы в режиме проектирования будет таким

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

Удалите
из секции <system.web>
файла Web.config параметр отключения проверки
достоверности <pages
validateRequest="false" /> и включите
его как атрибут ValidateRequest="false" в
состав директивы @ Page страницы ViewStateObjects.aspx,
чтобы он действовал только на одну эту страницуИногда состояние вида в элементе полезно отключить, чтобы
уменьшить нагрузку на каналы связи. Но для некоторых элементов
это нарушает их нормальную работу. Возьмем, например, списки ListBox и DropDownList. Когда
они заполнены большим количеством элементов, объем пересылаемой
информации состояния вида очень большой и это свойство лучше
отключить. Для восстановления состояния выделенного элемента
списка в этом случае можно воспользоваться информацией из коллекции страницы.
Добавьте
к проекту страницу с именем 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>
Этому
коду в режиме проектирования соответствует следующее представление
Двойным
щелчком на свободном месте страницы в режиме Design создайте
блок скриптов с заготовкой обработчика Page_Load()
Поместите
в обработчик Page_Load() код заполнения списков
видимым содержимым, который будет срабатывать при каждом запросе
страницы<script runat="server">
// Поле класса страницы
private const int maxCount = 100;
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 отмеченные пользователем
опции списков возвращаются сервером сохраненными
После
нескольких обратных отсылок щелкните правой кнопкой мыши
в клиентской области окна View
Source, чтобы просмотреть HTML-содержимое, присланное
серверомМы видим большое количество пересылаемой информации
в Add() в обработчике Page_Load().
Работает
механизм автоматического сохранения состояния вида ASP.NET.
Чтобы посмотреть, какой при этом получается объем пересылаемой
информации, именно касающийся работы этого механизма, нужно
включить работу ASP.NET в режиме выдачи трассировки на индивидуальных
страницах. Это можно сделать, если добавить атрибут в
директиву @ Page страницы, либо через
утилиту ,
запустив ее командой Website/ASP.NET Configuration (или
щелкнуть на пиктограмме в панели ).
Запустите
утилиту WAT , пойдите последовательно
по ссылкам Application
Configuration/Configure
debugging and tracing и включите флажки " Capture
tracing information "
и " Display tracing information on ondividual
pages "
Закройте
утилиту WAT В результате этих действий файл конфигурации примет вид
<?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 Tree | ||||
|---|---|---|---|---|
| Control UniqueID | Type | ControlState Size | ||
__Page |
ASP.viewstatelist_aspx |
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 в значение False
Запустите
страницу ViewStateList.aspx и убедитесь,
что объем сохраняемого состояния вида для списков стал нулевым
Вновь
включите сброшенные свойства EnableViewState для
списков
Добавьте
в директиву @Page общие для всех элементов
страницы атрибуты EnableViewState = "false" и EnableEventValidation = "false"Замечание. Если количество запросов
превысит заданное параметром трассировки requestLimit значение
(по умолчанию 10), то ASP.NET при исполнении не будет включать
результаты трассировки в страницу. В этом случае нужно при
очередном запуске тестового и
щелкнуть на ссылке " " этой страницы. Включить
безлимитную трассировку можно более просто - добавить в
директиву @ Page страницы
параметр
Удалите
из файла Web.config строку включения трассировки<
и добавьте
в директиву @Page страницы атрибут = "true",
чтобы включить индивидуальную трассировку страницы
Мы видим, что при отключении сохранения состояния вида
уменьшился и общий объем страницы. Но зато сервер в своих
откликах не стал сохранять выбранные клиентом опции списков.
Исправим этот недостаток программно, воспользовавшись тем фактом,
что с каждым запросом выбранные значения передаются на сервер
в словарь объекта страницы.
Добавьте
к странице в конец обработчика Page_Load() код восстановления
выбора пользователя в списках<script runat="server">
// Поле класса страницы
private const int maxCount = 100;
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,
которая попадает в словарь , доступный
для извлечения и использования в коде страницы на стороне сервера.
Мы находим это значение в сгенерированной коллекции значений
списка и настраиваем этот элемент коллекции как выделенный.
После таких настроек список сгенерирует в текущем отклике
сервера нужный HTML-код, соответствующий состоянию списка на
клиенте перед обратной отсылкой.
Запустите
страницу ViewStateList.aspx с включенной
трассировкой и убедитесь, что выбранные опции списков сохраняют
свои позиции и ASP.NET генерирует небольшой объем клиентского
кодаТаким образом мы выяснили, что состояние вида элементов управления, которое включено по умолчанию, не всегда является полезным либо просто не используется, и тогда его лучше отключать. Отключить состояние вида можно следующим образом:
web.config текущего
узла web-дерева сайта<?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 или this.EnableViewState = false )EnableViewState = "false" (в
коде свойство Элемент .EnableViewState = false )Если более общая настройка сохранения состояния вида выключена,
а для каких-то страниц или элементов управления ее нужно
включить, то в соответствующее место следует добавить атрибут EnableViewState = "true".
Состояние вида передается клиенту системой ASP.NET в скрытых
полях частично зашифрованным. Но это шифрование поверхностное,
чтобы не требовало много процессорного времени. При желании
злоумышленник может частично восстановить и прочитать эту информацию.
В этом упражнении рассмотрим способ
Добавьте
к проекту новую страницу (без файла отделенного кода) с именем DisplayViewState.aspx и
установите ее стартовой
Удалите
из интерфейсной части страницы дескриптор <div></div>
Включите
трассировку, добавив в директиву @ Page страницы
параметр Trace = true
Добавьте
в блок скриптов кодовую часть, чтобы окончательно страница
стала такой<%@ 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 = "Привет всем!!!";
label.ForeColor = System.Drawing.Color.Red;
label.Font.Bold = true;
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;// Чтобы не влияла на результат
// Вставляем в отклик напрямую
Response.Write("<b>Строка состояния в формате ASCII:</b><br />");
// Преобразуем строку из Base64 в массив ASCII-символов
byte[] ascii = Convert.FromBase64String(strViewState);
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 после обратной отсылки,
будет выглядеть примерно так
Мы видим, что некоторая информация, сохраняемая на клиенте в строке состояния вида, может быть частично расшифрована. Злоумышленник может извлечь из нее нужные для себя сведения.

Для большей защиты строки состояния вида можно использовать
встроенное шифрование, которое включается на странице параметром ViewStateEncryptionMode = "Always" директивы @ Page или
установкой в
<pages viewStateEncryptionMode="Always">
Включите
шифрование добавлением в директиву @Page атрибута ViewStateEncryptionMode = "Always" и
выполните страницу DisplayViewState.aspxСгенерированный отклик на обратную отсылку станет примерно таким

Теперь строка состояния стала зашифрованной и ее трудно расшифровать.
Однако не рекомендуется без надобности шифровать данные состояния
вида. Это негативно скажется на производительности, поскольку
сервер должен будет выполнять операцию шифрования и
Для предотвращения того, что злоумышленник может изменить
строку состояния нужным для себя образом и попытаться отправить
ее на сервер, предусмотрена встроенная защита в виде подсчета
@ Page параметром EnableViewStateMac = "true".
Во вкладке Standard панели имеется
элемент управления HiddenField, который генерирует Value.
Этот элемент не поддерживает шифрование, хэширование и хранение
по частям. Поэтому клиенты в исходном коде HTML-вывода могут
просматривать эти данные. Данные в скрытом поле хранятся до
тех пор, пока пользователь не перейдет к другой странице.
Добавьте
к проекту новую страницу с именем HiddenFieldTest.aspx без
файла отделенного кода и назначьте ее стартовой
Переведите
страницу в режим Design и двойным щелчком
на ее свободном месте создайте в скриптах обработчик Page_Load()
Поместите
на Web-форму из вкладки Standard панели Toolbox два
элемента управления HiddenField и присвойте
им имена beginTime и countRequest
Отключите
для всех элементов страницы механизм сохранения состояния
вида, добавив в директиву @ Page параметр EnableViewState = "false"
Заполните
страницу следующим кодом<%@ 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, в пределах одной страницы
сохраняют информациюОдно из самых серьезных ограничений состояния вида состояит
в том, что оно работает только на одной странице. При переходе
пользователя на другую страницу состояние вида прежней страницы
утрачивается. Одним из выходов является передача информации
в
Отправляющая запрос страница инициирует передачу либо с помощью
гиперссылки (элемент ), либо с помощью
кнопки Submit,
в обработчик которой вставлен код .
Для извлечения информации из строки
запроса на адресуемой (целевой) странице используется встроенный
в нее объект-словарь
QueryString.
Добавьте
к проекту страницу без файла отделенного кода с именем SourceQueryString.aspx и назначьте ее стартовой
Добавьте
внутрь контейнера <div></div> дескриптор строки-заголовка<h1 style="color: Red">Введите
Дополните
открывающий дескриптор <div> встроенным
стилем центрирования<div style="text-align: center">
Создайте
на Web-форме позиционирующую таблицу размером 3x2 и поместите
в ее ячейки из вкладки Standard панели Toolbox серверные
элементы с настройками согласно таблицы| Элемент | Свойство |
|---|---|
Label |
ID="Label1" |
|
Text="Имя:" |
TextBox |
ID="Name" |
Label |
ID="Label2" |
|
Text="Пароль:" |
TextBox |
ID="Password" |
|
TextMode="Password" |
Button |
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>
<tr>
<td style="text-align: right">
nbsp;<asp:Label ID="Label1" runat="server" Text="Имя:" />
</td>
<td>
nbsp;<asp:TextBox ID="Name" runat="server" />
</td>
</tr>
<tr>
<td style="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">
<asp:Button ID="Button1" runat="server" Text="Отправить"
OnClick="Button1_Click" />
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Теперь нужно создать принимающую запрос (целевую) страницу. На целевой странице значения поступивших параметров, извлекаемые из строки запроса, следует предварительно URL-раскодировать. Затем можно принять решение, что делать с поступившими данными.
Создайте
страницу с совмещенным кодом и именем 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 на клиенте
при наведении курсора мыши на гиперссылке будет отображать
переданное с другой страницы

В классах кнопочных элементов управления Button, и LinkButton имеется
свойство PostBackUrl, позволяющее сразу
адресоваться к целевой странице с доступом в ней ко всем данным
текущей страницы. Свойству PostBackUrl кнопки обратной отсылки присваивается
строка с URL целевой страницы, которая и будет загружена на
выполнение по щелчку пользователя на этой кнопке. При этом
на целевой странице, благодаря ее свойству PreviousPage,
будет полностью доступен объект страницы, инициировавшей обратную
отсылку.
Идея реализации механизма межстраничной отсылки достаточно проста. Любая страница на этапе описания является классом (инструкцией) с именем, равным имени страницы, которая при загрузке в память сервера превращается в объект (экземпляр класса), адресуемый средой исполнения. Исполняемый объект проходит все необходимые этапы жизни, начиная с инициализации элементов управления, корректировкой состояния, выполнением обработчиков, и заканчивая генерацией HTML-кода (рендерингом).
То же самое происходит и с исходной страницей, загруженной
по кнопке, у которой свойство PostBackUrl адресует
другую страницу.
Исходная страница также проходит все начальные стадии жизни,
включая выполнение обработчиков. Непосредственно перед генерацией
HTML-кода выполнение исходной страницы прерывается, но она
продолжает оставаться в памяти сервера. Одновременно запускается
объект целевой страницы, которому в свойство PreviousPage среда
исполнения записывает адрес объекта исходной страницы.
Теперь код целевой страницы может адресоваться к общедоступным данным исходной страницы, но рендеринг для клиента будет генерироваться только второй страницей. Когда целевая страница заканчивает свой цикл жизни, вместе с ней из памяти выгружается и исходная страница. Для того, чтобы закрытые или защищенные члены класса исходной страницы стали доступными в классе целевой страницы, их нужно обернуть в общедоступные свойства или функции доступа.
Подобная межстраничная связь может быть реализована не
только как "один к одному", но и в других сочетаниях: "один
ко многим", "многие к одному" или "многие
ко многим". Множественность
на исходной странице может быть организована программным формированием
значения свойства PostBackUrl в зависимости
от выбранных на этой странице предпочтений. Множественность
на целевой странице реализуется проверкой того, к какому типу
(к какой странице) относится записанный в свойство PreviousPage объект.
Межстраничная обратная отсылка целевой страницы может быть
инициирована не только по щелчку на кнопке с ненулевым
значением свойства PostBackUrl, но и в любом
месте кода вызовом соответствующей
public void Page.Server.
где path - вызываемая
целевая страница
preserveForm - флаг
передачи в целевую страницу данных QueryString и Form
Оптимальным будет вызов
Server.
Проиллюстрируем межстраничную отсылку на примере двух страниц-источников и двух страниц-приемников.
Добавьте
к проекту страницу с совмещенным кодом и именем SourcePostBack1.aspx
Назначьте
страницу SourcePostBack1.aspx стартовой
и оформите пользовательский интерфейс на 
Здесь использованы серверные элементы Label, TextBox, ListBox, Button и .
Для
элемента управления 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,
выделенные в листинге страницы SourcePostBack1.aspx (" вторая "
и " SourcePostBack1.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);// Просто кнопка Submit
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>
Атрибут ValidateRequest = "false" директивы @Page отключает контроль системы за вводом опасного кода в текстовые
поля.
Для того, чтобы продемонстрировать, что куки сохраняются и при переходе к другим страницам, создадим еще одну страницу, совершенно идентичную первой.
Создайте
в проекте копию страницы CookiesPage1.aspx и
присвойте ей имя CookiesPage2.aspx
Запустите
первую страницу CookiesPage1.aspx и испытайте
ее работу совместно со второй страницей, переходя друг на
друга по гиперссылке внизу страницВид страницы на клиенте будет таким

Механизм является очень удобным,
но неустойчивым, поскольку может либо не поддерживаться
Информация о функциональности 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(HtmlTextWriterTag.Hr.ToString()));
//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>
-набору можно назначить
то кук будет доступен только страницам папки MyFolder. По
умолчанию кук доступен любым страницам домена, как и, например,
при установке
Любой пользователь при первом обращении к приложению получает
свой сеанс, который выражается в выделении некоторого объема
памяти на стороне сервера для хранения данных любого типа.
Доступ к этим совместным данным осуществляется любой страницей
через присоединенный к ней объект-словарь Session.
За счет этого можно сохранять информацию на одной странице,
а получать к ней доступ с другой страницы.
При поступлении первого клиентского запроса на объект страницы
ASP.NET генерирует идентификатор
сеанса размером 42 байта. Этот идентификатор сеанса включается
в отклик сервера и является уникальным в пределах приложения.
Если ни одна из страниц приложения, к которым обращается пользователь,
не записывает в объект Session данные, то
Session,
идентификатор сеанса сразу фиксируется и запоминается Session.
Когда клиент (Session созданной
страницы. После чего данные сеанса становятся доступными в
коде этой страницы.
Идентификатор сеанса может пересылаться серверу с запросом двумя способами:
ASP.NET_SessionIdURL для клиентов, не поддерживающих
Идентификатор сеанса можно получить как значение -
при формировании первого отклика, или Session. SessionID -
всегда. Но конкретное его значение нас, как правило, не интересует,
поскольку процесс распознавания выполняется автоматически.
Данные сеанса могут сохраняться ASP.NET в одном из трех мест
на сервере, определяемых свойством Mode объекта Session:
InProc -
установлен по умолчанию)Windows под названием ASP.NET State Service (режим StateServer )SQLServer )Abandon ()Сеанс является экземпляром класса System.Web.SessionState. HttpSessionState и может частично управляться из кода. В
таблице приведены некоторые свойства и HttpSessionState
| Член | Описание |
|---|---|
Count |
Количество элементов в коллекции текущего сеанса. Только для чтения |
IsCookieless |
Булев флаг, показывающий способ хранения идентификатора сеанса: в куках или в модифицированном URL. Только для чтения |
IsNewSession |
Булево значение, которое указывает, был ли сеанс создан с текущим запросом. Если коллекция пуста или сеанс создан ранее, то возвращается false. Только для чтения |
Mode |
Свойство (только для чтения) содержит одно из значений перечисления SessionStateMode, указывающее способ хранения данных сеанса: Off (= 0) - сеанс не используетсяInProc (= 1) - данные сеанса хранятся
в памяти доменаStateServer (= 2) - сеанс хранится
в специальной службе Windows под названием ASP.NET
State ServiceSQLServer (= 3) - сеанс хранится
в базе данных SQL Server (= 4) - сеанс хранится на
основе нестандартного поставщика |
SessionID |
Строка, содержащая уникальный идентификатор сеанса. Только для чтения |
|
Максимальный срок простоя сеанса в минутах. Для чтения и записи |
|
Останавливает текущий сеанс |
Add(string name, object value) |
Добавляет новый элемент в коллекцию сеанса. Допускается
|
|
Очищает коллекцию сеанса |
RemoveAll() |
Очищает коллекцию сеанса |
Remove(string name) |
Удаляет |
RemoveAt(int index) |
Удаляет |
Начальное web. в секции <sessionState>.
Настройки секции <sessionState> поддерживаются
классом System.Web.Configuration.SessionStateSection через
его свойства, но имена свойств в кофигурационном файле начинаются
с символа нижнего регистра. Вот некоторые наиболее важные из
них
| Свойство | Описание |
|---|---|
Cookieless |
Задает режим использования UseUri (= 0) - никогда не использовать
cookieless="UseUri" или cookieless="true" )UseCookies (= 1) - всегда использовать
(= 2) - наиболее надежный
режим, в котором сервер сам определяет возможности
UseDeviceProfile (= 3) - способ
пересылки идентификатора сеанса основывается только
на потенциальной функциональности |
Mode |
Задает одно из значений
перечисления Off (= 0) - указывает, что сеанс
не задействуется для хранения данных. Такой режим
может существенно увеличить быстродействие, когда
необходимости в сеансе нетInProc (= 1) - данные сеанса хранятся
в памяти доменаStateServer = 2 - сеанс хранится
в специальной службе Windows под названием ASP.NET
State ServiceSQLServer (= 3) - сеанс хранится
в базе данных SQL Server (= 4) - сеанс хранится
на основе нестандартного поставщика |
|
Устанавливают время простоя сеанса в минутах |
RegenerateExpiredSessionId |
cookieless="true", когда идентификатор сеанса пересылается
|
CookieName |
Устанавливает имя идентификатора сеанса в ASP.NET_SessionID " |
При включенном режиме cookieless="true" URL всех относительных
ссылок. Это же справедливо и при автоматической переадресации
на новую страницу методом с полным циклом. Для того, чтобы указать системе, что идентификатор
сеанса должен передаваться секретным протоколом, нужно установить
Приведем пример использования сеанса.
Отредактируйте
файл web.config приложения как показано ниже<?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 с
совмещенным кодом, которую заполните так<%@ 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>
Назначьте
страницу SessionCookieless.aspx стартовой
и выполните ееКлиентский результат будет таким

Обратите внимание, что идентификатор сеанса встроен в URL
при значении параметра Cookieless = "UseUri". Отметьте также,
что интерфейсная часть страницы пустая - все создается динамически.
Часть выводимой в таблицу информации мы добыли из объекта класса SessionStateSection,
который создается по настройкам
Состояние приложения - это область памяти сервера, выделяемая
при запуске приложения для хранения глобальных данных, доступных
с любой страницы приложения. На эти данные в каждой странице
ссылается объект-словарь Application типа System.Web.HttpApplicationState.
Как и при
Данные состояния приложения существуют в течение всего периода
работы приложения и теряются при перезапуске сервера или перехода
приложения в новый домен. Для обеспечения
В качестве примера построим две страницы, в которых подсчитывается общее число посещений сайта с момента запуска приложения.
Удалите
из <?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>
Добавьте
к приложению файл Global.asax и заполните
его так<%@ Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
this.Application["clicks"] = (int)0;
}
</script>
Создайте
страницу без отделенного кода с именем 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,
чтобы страницы ссылались друг на друга
Запустите
само приложение несколько раз и пощелкайте по ссылкам чтобы
убедиться, что счетчик количества визитов увеличивается
В панели
задач нижней части экрана щелкните правой кнопкой мыши
на пиктограмме тестового сервера и остановите его командой Stop
Вновь
запустите страницу ApplicationState1.aspx,
чтобы убедиться, что счетчик сбросился в начальное значениеДля увеличения @ Application файла параметр ClassName, в котором указывается
При изменении в страницах глобальных данных ,
чтобы на время позволить воздействовать на переменную только
со стороны одного объекта приложения или одного потока, если
применяется многопоточная модель. Такой подход удобен тем,
что мы можем обернуть сохраняемые данные в статические свойства
и назначить для них контролирующий код, прежде чем сохранять
их в памяти сервера. Мы можем объявлять статические переменные
для хранения объектов любого типа и для доступа к ним явных
Приведем пример, в котором количество визитов всех пользователей,
а также список файлов текущего физического
Отредактируйте
код файла 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 следующим
кодом<%@ 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>
Запустите
страницу и убедитесь, что все работает и объявленные в классе
приложения статические члены сохраняют свои значения даже
при отключении клиента от сервера до тех пор, пока сервер
остается запущенным
В панели
задач нижней части экрана щелкните правой кнопкой мыши на
пиктограмме тестового сервера и остановите его командой Stop
Вновь
запустите страницу AppClassStaticMembers.aspx,
чтобы убедиться, что память сервера очистилась и счетчик
сбросился в начальное значениеА сейчас мы убедимся, что при модификации любого файла приложения, включая web.config, оно автоматически перекомпилируется при первом запросе и загружается в новый домен.
Не
нарушая кода, модифицируйте любой исходный файл приложения,
например, введите пробел после точки с запятой в конце
любой строки (или добавьте пустую строку) и сохраните изменения
Запустите
приложение и убедитесь, что счетчик сбросился за счет загрузки
приложения в новый доменИнтерфейс клиента для данного примера будет таким

Рассмотренные в данной теме способы хранения данных относятся, в основном, к внутрипроцессным, так как сохраняются в оперативной памяти сервера или передаются по каналам связи на компьютер клиента для временного хранения. Другие методы хранения информации в службах сервера, включая базы данных и файлы, являются внепроцессными, поскольку не зависят от среды исполнения ASP.NET. Но мы их здесь не рассматривали.

Файлы к лабораторной работе Вы можете скачать здесь.
Главная особенность удаленных приложений состоит в том, что
они могут одновременно обрабатывать множество пользователей.
К тому же сервер может выполнять одновременно несколько разных
приложений. Поэтому среда исполнения устроена так, что после
обработки единичного запроса очередного пользователя память
сервера немедленно очищается от кода запрошенной страницы,
освобождая ресурсы для новых возможных
Такой факт вынуждает разработчиков Web-приложений изыскивать способы хранения информации о состоянии между запросами, с последующим ее извлечением и использованием. В ASP.NET существует два способа хранения информации между запросами: на клиенте и на сервере. Разработчик сайта сам решает, какую группу методов хранения данных состояния ему выбрать.
Хранение информации на клиенте имеет следующие преимущества:
Хранение информации на сервере имеет следующие преимущества:
Различают следующие методы хранения данных на клиенте:
ViewState типа System.Web.UI.StateBag, наследуемом от класса Control любым элементом управления и самой страницей.POST (по щелчку кнопки Submit ), а не
методом GET (по щелчку на гиперссылке).URL.
Видимы для пользователя в поле адреса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.
Он поддается .
В этом упражнении будем использовать программирование словаря самой
страницы. Мы создадим форму с интерфейсными элементами,
в которых отключим автоматическое сохранение состояния вида,
а будем его сохранять и восстанавливать вручную.
Интерфейсные
элементы TextBox мы позиционируем в серверной
таблице и они будут размещены в коллекции Page.Controls.
Мы будем
использовать рекурсивную логику для опроса всех дочерних элементов
таблицы, чтобы выявить объекты TextBox.
В качестве ключа для доступа к элементам словаря уровня
страницы будем использовать TextBox, а сохранять будем значение свойства Text.
Создайте
пустой проект с именем ViewState командой File/New/Web Site 
Добавьте
к проекту новую Web-страницу с отделенным кодом и именем ViewStateTest.aspx,
назначьте ее стартовой
Запустите
командой Website/ASP.NET Configuration страницу WAT (Web Site Application Configuration/Configure debugging and tracing
На
появившейся странице включите флажок Enable debugging и
закройте WAT Таким действием мы создали файл конфигурации Web. с
настройкой отладки.
В панели Solution Explorer щелкните на кнопке Refresh , чтобы увидеть
файл конфигурации в составе проекта
Откройте
интерфейсную часть страницы ViewStateTest.aspx в
режиме Source и вставьте внутрь дескрипторного
блока < div></div> следующий
заголовок<h1 style="color:
Red">Заполните анкету</h1>
Добавьте
в открывающую часть парного дескриптора <div> внутренний
стиль<div style="text-align:
center">
Откройте
интерфейсную часть страницы ViewStateTest.aspx в
режиме Design и поместите на нее элемент Table из вкладки HTML панели Toolbox
Щелкните
на элементе Table правой кнопкой мыши и
выполните опцию Run As Server Control,
чтобы превратить его в серверный
Откорректируйте
интерфейсный HTML-код заготовки таблицы следующим образом<table id="Table1" 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>
Поместите
после таблицы три кнопки Button из вкладки Standard панели Toolbox и
присвойте им имена cmdSubmit, cmdSave и cmdRestore соответственно
Поместите
в первую строку и первый столбец таблицы приведенный ниже
текст, а в остальные ячейки второго столбца - элемент TextBox из
вкладки Standard панели Toolbox После этих действий интерфейс страницы в режиме проектирования будет таким

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

Удалите
из секции <system.web>
файла Web.config параметр отключения проверки
достоверности <pages
validateRequest="false" /> и включите
его как атрибут ValidateRequest="false" в
состав директивы @ Page страницы ViewStateObjects.aspx,
чтобы он действовал только на одну эту страницуИногда состояние вида в элементе полезно отключить, чтобы
уменьшить нагрузку на каналы связи. Но для некоторых элементов
это нарушает их нормальную работу. Возьмем, например, списки ListBox и DropDownList. Когда
они заполнены большим количеством элементов, объем пересылаемой
информации состояния вида очень большой и это свойство лучше
отключить. Для восстановления состояния выделенного элемента
списка в этом случае можно воспользоваться информацией из коллекции страницы.
Добавьте
к проекту страницу с именем 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>
Этому
коду в режиме проектирования соответствует следующее представление
Двойным
щелчком на свободном месте страницы в режиме Design создайте
блок скриптов с заготовкой обработчика Page_Load()
Поместите
в обработчик Page_Load() код заполнения списков
видимым содержимым, который будет срабатывать при каждом запросе
страницы<script runat="server">
// Поле класса страницы
private const int maxCount = 100;
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 отмеченные пользователем
опции списков возвращаются сервером сохраненными
После
нескольких обратных отсылок щелкните правой кнопкой мыши
в клиентской области окна View
Source, чтобы просмотреть HTML-содержимое, присланное
серверомМы видим большое количество пересылаемой информации
в Add() в обработчике Page_Load().
Работает
механизм автоматического сохранения состояния вида ASP.NET.
Чтобы посмотреть, какой при этом получается объем пересылаемой
информации, именно касающийся работы этого механизма, нужно
включить работу ASP.NET в режиме выдачи трассировки на индивидуальных
страницах. Это можно сделать, если добавить атрибут в
директиву @ Page страницы, либо через
утилиту ,
запустив ее командой Website/ASP.NET Configuration (или
щелкнуть на пиктограмме в панели ).
Запустите
утилиту WAT , пойдите последовательно
по ссылкам Application
Configuration/Configure
debugging and tracing и включите флажки " Capture
tracing information "
и " Display tracing information on ondividual
pages "
Закройте
утилиту WAT В результате этих действий файл конфигурации примет вид
<?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 Tree | ||||
|---|---|---|---|---|
| Control UniqueID | Type | ControlState Size | ||
__Page |
ASP.viewstatelist_aspx |
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 в значение False
Запустите
страницу ViewStateList.aspx и убедитесь,
что объем сохраняемого состояния вида для списков стал нулевым
Вновь
включите сброшенные свойства EnableViewState для
списков
Добавьте
в директиву @Page общие для всех элементов
страницы атрибуты EnableViewState = "false" и EnableEventValidation = "false"Замечание. Если количество запросов
превысит заданное параметром трассировки requestLimit значение
(по умолчанию 10), то ASP.NET при исполнении не будет включать
результаты трассировки в страницу. В этом случае нужно при
очередном запуске тестового и
щелкнуть на ссылке " " этой страницы. Включить
безлимитную трассировку можно более просто - добавить в
директиву @ Page страницы
параметр
Удалите
из файла Web.config строку включения трассировки<
и добавьте
в директиву @Page страницы атрибут = "true",
чтобы включить индивидуальную трассировку страницы
Мы видим, что при отключении сохранения состояния вида
уменьшился и общий объем страницы. Но зато сервер в своих
откликах не стал сохранять выбранные клиентом опции списков.
Исправим этот недостаток программно, воспользовавшись тем фактом,
что с каждым запросом выбранные значения передаются на сервер
в словарь объекта страницы.
Добавьте
к странице в конец обработчика Page_Load() код восстановления
выбора пользователя в списках<script runat="server">
// Поле класса страницы
private const int maxCount = 100;
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,
которая попадает в словарь , доступный
для извлечения и использования в коде страницы на стороне сервера.
Мы находим это значение в сгенерированной коллекции значений
списка и настраиваем этот элемент коллекции как выделенный.
После таких настроек список сгенерирует в текущем отклике
сервера нужный HTML-код, соответствующий состоянию списка на
клиенте перед обратной отсылкой.
Запустите
страницу ViewStateList.aspx с включенной
трассировкой и убедитесь, что выбранные опции списков сохраняют
свои позиции и ASP.NET генерирует небольшой объем клиентского
кодаТаким образом мы выяснили, что состояние вида элементов управления, которое включено по умолчанию, не всегда является полезным либо просто не используется, и тогда его лучше отключать. Отключить состояние вида можно следующим образом:
web.config текущего
узла web-дерева сайта<?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 или this.EnableViewState = false )EnableViewState = "false" (в
коде свойство Элемент .EnableViewState = false )Если более общая настройка сохранения состояния вида выключена,
а для каких-то страниц или элементов управления ее нужно
включить, то в соответствующее место следует добавить атрибут EnableViewState = "true".
Состояние вида передается клиенту системой ASP.NET в скрытых
полях частично зашифрованным. Но это шифрование поверхностное,
чтобы не требовало много процессорного времени. При желании
злоумышленник может частично восстановить и прочитать эту информацию.
В этом упражнении рассмотрим способ
Добавьте
к проекту новую страницу (без файла отделенного кода) с именем DisplayViewState.aspx и
установите ее стартовой
Удалите
из интерфейсной части страницы дескриптор <div></div>
Включите
трассировку, добавив в директиву @ Page страницы
параметр Trace = true
Добавьте
в блок скриптов кодовую часть, чтобы окончательно страница
стала такой<%@ 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 = "Привет всем!!!";
label.ForeColor = System.Drawing.Color.Red;
label.Font.Bold = true;
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;// Чтобы не влияла на результат
// Вставляем в отклик напрямую
Response.Write("<b>Строка состояния в формате ASCII:</b><br />");
// Преобразуем строку из Base64 в массив ASCII-символов
byte[] ascii = Convert.FromBase64String(strViewState);
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 после обратной отсылки,
будет выглядеть примерно так
Мы видим, что некоторая информация, сохраняемая на клиенте в строке состояния вида, может быть частично расшифрована. Злоумышленник может извлечь из нее нужные для себя сведения.

Для большей защиты строки состояния вида можно использовать
встроенное шифрование, которое включается на странице параметром ViewStateEncryptionMode = "Always" директивы @ Page или
установкой в
<pages viewStateEncryptionMode="Always">
Включите
шифрование добавлением в директиву @Page атрибута ViewStateEncryptionMode = "Always" и
выполните страницу DisplayViewState.aspxСгенерированный отклик на обратную отсылку станет примерно таким

Теперь строка состояния стала зашифрованной и ее трудно расшифровать.
Однако не рекомендуется без надобности шифровать данные состояния
вида. Это негативно скажется на производительности, поскольку
сервер должен будет выполнять операцию шифрования и
Для предотвращения того, что злоумышленник может изменить
строку состояния нужным для себя образом и попытаться отправить
ее на сервер, предусмотрена встроенная защита в виде подсчета
@ Page параметром EnableViewStateMac = "true".
Во вкладке Standard панели имеется
элемент управления HiddenField, который генерирует Value.
Этот элемент не поддерживает шифрование, хэширование и хранение
по частям. Поэтому клиенты в исходном коде HTML-вывода могут
просматривать эти данные. Данные в скрытом поле хранятся до
тех пор, пока пользователь не перейдет к другой странице.
Добавьте
к проекту новую страницу с именем HiddenFieldTest.aspx без
файла отделенного кода и назначьте ее стартовой
Переведите
страницу в режим Design и двойным щелчком
на ее свободном месте создайте в скриптах обработчик Page_Load()
Поместите
на Web-форму из вкладки Standard панели Toolbox два
элемента управления HiddenField и присвойте
им имена beginTime и countRequest
Отключите
для всех элементов страницы механизм сохранения состояния
вида, добавив в директиву @ Page параметр EnableViewState = "false"
Заполните
страницу следующим кодом<%@ 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, в пределах одной страницы
сохраняют информациюОдно из самых серьезных ограничений состояния вида состояит
в том, что оно работает только на одной странице. При переходе
пользователя на другую страницу состояние вида прежней страницы
утрачивается. Одним из выходов является передача информации
в
Отправляющая запрос страница инициирует передачу либо с помощью
гиперссылки (элемент ), либо с помощью
кнопки Submit,
в обработчик которой вставлен код .
Для извлечения информации из строки
запроса на адресуемой (целевой) странице используется встроенный
в нее объект-словарь
QueryString.
Добавьте
к проекту страницу без файла отделенного кода с именем SourceQueryString.aspx и назначьте ее стартовой
Добавьте
внутрь контейнера <div></div> дескриптор строки-заголовка<h1 style="color: Red">Введите
Дополните
открывающий дескриптор <div> встроенным
стилем центрирования<div style="text-align: center">
Создайте
на Web-форме позиционирующую таблицу размером 3x2 и поместите
в ее ячейки из вкладки Standard панели Toolbox серверные
элементы с настройками согласно таблицы| Элемент | Свойство |
|---|---|
Label |
ID="Label1" |
|
Text="Имя:" |
TextBox |
ID="Name" |
Label |
ID="Label2" |
|
Text="Пароль:" |
TextBox |
ID="Password" |
|
TextMode="Password" |
Button |
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>
<tr>
<td style="text-align: right">
nbsp;<asp:Label ID="Label1" runat="server" Text="Имя:" />
</td>
<td>
nbsp;<asp:TextBox ID="Name" runat="server" />
</td>
</tr>
<tr>
<td style="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">
<asp:Button ID="Button1" runat="server" Text="Отправить"
OnClick="Button1_Click" />
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Теперь нужно создать принимающую запрос (целевую) страницу. На целевой странице значения поступивших параметров, извлекаемые из строки запроса, следует предварительно URL-раскодировать. Затем можно принять решение, что делать с поступившими данными.
Создайте
страницу с совмещенным кодом и именем 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 на клиенте
при наведении курсора мыши на гиперссылке будет отображать
переданное с другой страницы

В классах кнопочных элементов управления Button, и LinkButton имеется
свойство PostBackUrl, позволяющее сразу
адресоваться к целевой странице с доступом в ней ко всем данным
текущей страницы. Свойству PostBackUrl кнопки обратной отсылки присваивается
строка с URL целевой страницы, которая и будет загружена на
выполнение по щелчку пользователя на этой кнопке. При этом
на целевой странице, благодаря ее свойству PreviousPage,
будет полностью доступен объект страницы, инициировавшей обратную
отсылку.
Идея реализации механизма межстраничной отсылки достаточно проста. Любая страница на этапе описания является классом (инструкцией) с именем, равным имени страницы, которая при загрузке в память сервера превращается в объект (экземпляр класса), адресуемый средой исполнения. Исполняемый объект проходит все необходимые этапы жизни, начиная с инициализации элементов управления, корректировкой состояния, выполнением обработчиков, и заканчивая генерацией HTML-кода (рендерингом).
То же самое происходит и с исходной страницей, загруженной
по кнопке, у которой свойство PostBackUrl адресует
другую страницу.
Исходная страница также проходит все начальные стадии жизни,
включая выполнение обработчиков. Непосредственно перед генерацией
HTML-кода выполнение исходной страницы прерывается, но она
продолжает оставаться в памяти сервера. Одновременно запускается
объект целевой страницы, которому в свойство PreviousPage среда
исполнения записывает адрес объекта исходной страницы.
Теперь код целевой страницы может адресоваться к общедоступным данным исходной страницы, но рендеринг для клиента будет генерироваться только второй страницей. Когда целевая страница заканчивает свой цикл жизни, вместе с ней из памяти выгружается и исходная страница. Для того, чтобы закрытые или защищенные члены класса исходной страницы стали доступными в классе целевой страницы, их нужно обернуть в общедоступные свойства или функции доступа.
Подобная межстраничная связь может быть реализована не
только как "один к одному", но и в других сочетаниях: "один
ко многим", "многие к одному" или "многие
ко многим". Множественность
на исходной странице может быть организована программным формированием
значения свойства PostBackUrl в зависимости
от выбранных на этой странице предпочтений. Множественность
на целевой странице реализуется проверкой того, к какому типу
(к какой странице) относится записанный в свойство PreviousPage объект.
Межстраничная обратная отсылка целевой страницы может быть
инициирована не только по щелчку на кнопке с ненулевым
значением свойства PostBackUrl, но и в любом
месте кода вызовом соответствующей
public void Page.Server.
где path - вызываемая
целевая страница
preserveForm - флаг
передачи в целевую страницу данных QueryString и Form
Оптимальным будет вызов
Server.
Проиллюстрируем межстраничную отсылку на примере двух страниц-источников и двух страниц-приемников.
Добавьте
к проекту страницу с совмещенным кодом и именем SourcePostBack1.aspx
Назначьте
страницу SourcePostBack1.aspx стартовой
и оформите пользовательский интерфейс на 
Здесь использованы серверные элементы Label, TextBox, ListBox, Button и .
Для
элемента управления 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,
выделенные в листинге страницы SourcePostBack1.aspx (" вторая "
и " SourcePostBack1.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);// Просто кнопка Submit
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>
Атрибут ValidateRequest = "false" директивы @Page отключает контроль системы за вводом опасного кода в текстовые
поля.
Для того, чтобы продемонстрировать, что куки сохраняются и при переходе к другим страницам, создадим еще одну страницу, совершенно идентичную первой.
Создайте
в проекте копию страницы CookiesPage1.aspx и
присвойте ей имя CookiesPage2.aspx
Запустите
первую страницу CookiesPage1.aspx и испытайте
ее работу совместно со второй страницей, переходя друг на
друга по гиперссылке внизу страницВид страницы на клиенте будет таким

Механизм является очень удобным,
но неустойчивым, поскольку может либо не поддерживаться
Информация о функциональности 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(HtmlTextWriterTag.Hr.ToString()));
//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>
-набору можно назначить
то кук будет доступен только страницам папки MyFolder. По
умолчанию кук доступен любым страницам домена, как и, например,
при установке
Любой пользователь при первом обращении к приложению получает
свой сеанс, который выражается в выделении некоторого объема
памяти на стороне сервера для хранения данных любого типа.
Доступ к этим совместным данным осуществляется любой страницей
через присоединенный к ней объект-словарь Session.
За счет этого можно сохранять информацию на одной странице,
а получать к ней доступ с другой страницы.
При поступлении первого клиентского запроса на объект страницы
ASP.NET генерирует идентификатор
сеанса размером 42 байта. Этот идентификатор сеанса включается
в отклик сервера и является уникальным в пределах приложения.
Если ни одна из страниц приложения, к которым обращается пользователь,
не записывает в объект Session данные, то
Session,
идентификатор сеанса сразу фиксируется и запоминается Session.
Когда клиент (Session созданной
страницы. После чего данные сеанса становятся доступными в
коде этой страницы.
Идентификатор сеанса может пересылаться серверу с запросом двумя способами:
ASP.NET_SessionIdURL для клиентов, не поддерживающих
Идентификатор сеанса можно получить как значение -
при формировании первого отклика, или Session. SessionID -
всегда. Но конкретное его значение нас, как правило, не интересует,
поскольку процесс распознавания выполняется автоматически.
Данные сеанса могут сохраняться ASP.NET в одном из трех мест
на сервере, определяемых свойством Mode объекта Session:
InProc -
установлен по умолчанию)Windows под названием ASP.NET State Service (режим StateServer )SQLServer )Abandon ()Сеанс является экземпляром класса System.Web.SessionState. HttpSessionState и может частично управляться из кода. В
таблице приведены некоторые свойства и HttpSessionState
| Член | Описание |
|---|---|
Count |
Количество элементов в коллекции текущего сеанса. Только для чтения |
IsCookieless |
Булев флаг, показывающий способ хранения идентификатора сеанса: в куках или в модифицированном URL. Только для чтения |
IsNewSession |
Булево значение, которое указывает, был ли сеанс создан с текущим запросом. Если коллекция пуста или сеанс создан ранее, то возвращается false. Только для чтения |
Mode |
Свойство (только для чтения) содержит одно из значений перечисления SessionStateMode, указывающее способ хранения данных сеанса: Off (= 0) - сеанс не используетсяInProc (= 1) - данные сеанса хранятся
в памяти доменаStateServer (= 2) - сеанс хранится
в специальной службе Windows под названием ASP.NET
State ServiceSQLServer (= 3) - сеанс хранится
в базе данных SQL Server (= 4) - сеанс хранится на
основе нестандартного поставщика |
SessionID |
Строка, содержащая уникальный идентификатор сеанса. Только для чтения |
|
Максимальный срок простоя сеанса в минутах. Для чтения и записи |
|
Останавливает текущий сеанс |
Add(string name, object value) |
Добавляет новый элемент в коллекцию сеанса. Допускается
|
|
Очищает коллекцию сеанса |
RemoveAll() |
Очищает коллекцию сеанса |
Remove(string name) |
Удаляет |
RemoveAt(int index) |
Удаляет |
Начальное web. в секции <sessionState>.
Настройки секции <sessionState> поддерживаются
классом System.Web.Configuration.SessionStateSection через
его свойства, но имена свойств в кофигурационном файле начинаются
с символа нижнего регистра. Вот некоторые наиболее важные из
них
| Свойство | Описание |
|---|---|
Cookieless |
Задает режим использования UseUri (= 0) - никогда не использовать
cookieless="UseUri" или cookieless="true" )UseCookies (= 1) - всегда использовать
(= 2) - наиболее надежный
режим, в котором сервер сам определяет возможности
UseDeviceProfile (= 3) - способ
пересылки идентификатора сеанса основывается только
на потенциальной функциональности |
Mode |
Задает одно из значений
перечисления Off (= 0) - указывает, что сеанс
не задействуется для хранения данных. Такой режим
может существенно увеличить быстродействие, когда
необходимости в сеансе нетInProc (= 1) - данные сеанса хранятся
в памяти доменаStateServer = 2 - сеанс хранится
в специальной службе Windows под названием ASP.NET
State ServiceSQLServer (= 3) - сеанс хранится
в базе данных SQL Server (= 4) - сеанс хранится
на основе нестандартного поставщика |
|
Устанавливают время простоя сеанса в минутах |
RegenerateExpiredSessionId |
cookieless="true", когда идентификатор сеанса пересылается
|
CookieName |
Устанавливает имя идентификатора сеанса в ASP.NET_SessionID " |
При включенном режиме cookieless="true" URL всех относительных
ссылок. Это же справедливо и при автоматической переадресации
на новую страницу методом с полным циклом. Для того, чтобы указать системе, что идентификатор
сеанса должен передаваться секретным протоколом, нужно установить
Приведем пример использования сеанса.
Отредактируйте
файл web.config приложения как показано ниже<?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 с
совмещенным кодом, которую заполните так<%@ 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>
Назначьте
страницу SessionCookieless.aspx стартовой
и выполните ееКлиентский результат будет таким

Обратите внимание, что идентификатор сеанса встроен в URL
при значении параметра Cookieless = "UseUri". Отметьте также,
что интерфейсная часть страницы пустая - все создается динамически.
Часть выводимой в таблицу информации мы добыли из объекта класса SessionStateSection,
который создается по настройкам
Состояние приложения - это область памяти сервера, выделяемая
при запуске приложения для хранения глобальных данных, доступных
с любой страницы приложения. На эти данные в каждой странице
ссылается объект-словарь Application типа System.Web.HttpApplicationState.
Как и при
Данные состояния приложения существуют в течение всего периода
работы приложения и теряются при перезапуске сервера или перехода
приложения в новый домен. Для обеспечения
В качестве примера построим две страницы, в которых подсчитывается общее число посещений сайта с момента запуска приложения.
Удалите
из <?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>
Добавьте
к приложению файл Global.asax и заполните
его так<%@ Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
this.Application["clicks"] = (int)0;
}
</script>
Создайте
страницу без отделенного кода с именем 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,
чтобы страницы ссылались друг на друга
Запустите
само приложение несколько раз и пощелкайте по ссылкам чтобы
убедиться, что счетчик количества визитов увеличивается
В панели
задач нижней части экрана щелкните правой кнопкой мыши
на пиктограмме тестового сервера и остановите его командой Stop
Вновь
запустите страницу ApplicationState1.aspx,
чтобы убедиться, что счетчик сбросился в начальное значениеДля увеличения @ Application файла параметр ClassName, в котором указывается
При изменении в страницах глобальных данных ,
чтобы на время позволить воздействовать на переменную только
со стороны одного объекта приложения или одного потока, если
применяется многопоточная модель. Такой подход удобен тем,
что мы можем обернуть сохраняемые данные в статические свойства
и назначить для них контролирующий код, прежде чем сохранять
их в памяти сервера. Мы можем объявлять статические переменные
для хранения объектов любого типа и для доступа к ним явных
Приведем пример, в котором количество визитов всех пользователей,
а также список файлов текущего физического
Отредактируйте
код файла 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 следующим
кодом<%@ 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>
Запустите
страницу и убедитесь, что все работает и объявленные в классе
приложения статические члены сохраняют свои значения даже
при отключении клиента от сервера до тех пор, пока сервер
остается запущенным
В панели
задач нижней части экрана щелкните правой кнопкой мыши на
пиктограмме тестового сервера и остановите его командой Stop
Вновь
запустите страницу AppClassStaticMembers.aspx,
чтобы убедиться, что память сервера очистилась и счетчик
сбросился в начальное значениеА сейчас мы убедимся, что при модификации любого файла приложения, включая web.config, оно автоматически перекомпилируется при первом запросе и загружается в новый домен.
Не
нарушая кода, модифицируйте любой исходный файл приложения,
например, введите пробел после точки с запятой в конце
любой строки (или добавьте пустую строку) и сохраните изменения
Запустите
приложение и убедитесь, что счетчик сбросился за счет загрузки
приложения в новый доменИнтерфейс клиента для данного примера будет таким

Рассмотренные в данной теме способы хранения данных относятся, в основном, к внутрипроцессным, так как сохраняются в оперативной памяти сервера или передаются по каналам связи на компьютер клиента для временного хранения. Другие методы хранения информации в службах сервера, включая базы данных и файлы, являются внепроцессными, поскольку не зависят от среды исполнения ASP.NET. Но мы их здесь не рассматривали.

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