ГГУ им. Ф. Скорины

http://www.gsu.by

Разработка Web-приложений ASP .NET с использованием
Visual Studio .NET

4. Лекция: Основы Web-программирования с
использованием ASP.NET: версия для печати и PDA
Рассматриваются вопросы формирования отклика сервера и
структура HTML-файла, получаемого при этом, а также вопросы вывода
информации в окне браузера клиента и особенности этого процесса.
Приводится ряд примеров, демонстрирующих возможности организации
ввода клиентом информации и передачи ее на сервер для дальнейшей
обработки Web-приложением. Рассматриваются типичные ошибки,
возникающие в процессе работы с исходным кодом Web-приложения, пути
их обнаружения и исправления. Изучаются возможности динамического
создания элементов управления в Web-приложении и добавления их на
страницу, создания обработчиков событий для них. Изучается структура
Web-приложения и таких ее составляющих, как класс Page, коллекция
ViewState, объекты Session и Application. Рассматриваются принципы
работы с файлами cookies.

Цель лекции: изучение принципов создания
Web-приложений с использованием ASP.NET, рассмотрение основ
формирования HTML-кода страницы в процессе выполнения
Web-приложения, изучение принципов вывода информации в окне браузера
клиента, использования динамических элементов
управления, а также способов передачи параметров между
обратными отсылками данных клиента на сервер.

Ранее, в лекции
2 был приведен пример создания простого Web-приложения,
содержащего кнопку, при нажатии на которую на экране отображается
строка "Это простейшее приложение ASP.NET".

Для более глубокого понимания кода, созданного ASP.NET в
процессе обработки события нажатия на кнопку, просмотрим исходный
код страницы, отображенной в браузере. Для этого необходимо вызвать
контекстное меню страницы, отображенной в браузере, и выбрать пункт
"Просмотр HTML-кода". Текст HTML-кода страницы приведен ниже.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
Untitled Page
</title></head>
<body>
<form name="form1" method="post" action="Default.aspx"
id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUKMTQ2OTkzNDMyMWRkgXPwyzoFdQxaRTEKBnZBmc4T/Wk=" />
</div>

<div>
<input type="submit" name="Button1" value="Button"
id="Button1" />
</div>

<div>

<input type="hidden" name="__EVENTVALIDATION" id=
"__EVENTVALIDATION" value=
"/wEWAgKn1OmODgKM54rGBoECiAp4b8+PZ5Qm6BWb5i+pgLWp" />
</div></form>
</body>
</html>

В приведенном примере HTML-кода страницы особый интерес
вызывают скрытые поля, задаваемые с помощью тега

<input type="hidden" name="" id="" value="" />.

Эти поля используются для возврата информации серверу.
Возврат информации серверу осуществляется с помощью механизма
состояний вида (ViewState). Как видно,
строка состояния вида не является читабельной, т. к. выглядит как
последовательность случайных символов. На самом деле, значение
состояния вида представляет собой строку, заданную в формате Base64,
и пользователь может достаточно легко интерпретировать эти данные,
преобразовав их в формат ASCII. Подробнее механизм передачи данных
серверу в ASP.NET рассмотрен в разделе "ViewState" данной лекции [1].

В нашем примере для вывода строки в окно браузера
использовался объект Response. Применение данного объекта
является наследием ASP, и он не рекомендован в настоящее время в
качестве средства вывода в окно браузера информации, т. к.
современные серверные элементы управления позволяют делать это
способами, очень похожими на приемы программирования, используемые
при создании обычных Windows-приложений - с помощью обращения к
свойствам элементов управления.

Например, в нашем случае мы можем применить объект Label (он представляет собой статический
текст, размещенный на странице) для вывода информации в окне
браузера. Для выполнения примера необходимо перейти в режим
редактирования приложения и разместить на странице элемент Label. В свойстве Text этого элемента установим пустое значение
(необходимо удалить текст, находящийся там по умолчанию). Текст
страницы после произведенных действий будет выглядеть следующим
образом (рис.
4.1).

увеличить изображение
Рис. 4.1.
Окно редактора кода страницы после добавления элемента Label

Перейдем в окно редактора кода и изменим обработчик
события Button1_Click следующим образом.
Удалим строку Response.Write("Это простейшее
приложение ASP.NET"), и введем следующий код:

Label1.Text = "Это простейшее приложение ASP.NET";

После запуска приложение ведет себя так же как и раньше,
однако если посмотреть исходный код страницы, полученной после
нажатия на кнопку, можно увидеть, что в нем присутствует новая
строка:

<span id="Label1">Это простейшее приложение ASP.NET</span><br />

Это и есть результат размещения на странице элемента
Label.

Немного усложним пример. Создадим страницу, которая
предоставляет пользователю возможность ввести свое имя и фамилию, а
при нажатии на кнопку выводит приглашение, содержащее введенное имя
и фамилию.

Модифицируем созданный ранее пример следующим образом.
Добавим на страницу два элемента управления TextBox, предназначенных для ввода фамилии и
имени, и два элемента Label и зададим
значения их идентификаторов так, как показано на рис.
4.2.

Рис. 4.2.  Окно редактора
страницы в режиме дизайна

Как видно из рисунка, идентификаторы элементов, которые
в данном случае заменяют имена, с чьей помощью можно обращаться к
элементу из программного кода, содержат префиксы (приставки,
расположенные до самого имени и отделенные от него символом "_").
Наименования префиксов обычно получают путем сокращения названия
объектов, к которым относятся элементы управления. Например, Label - lbl, Button - btn, TextBox - tb и т.
д. Использование префиксов имен элементов управления является
желательным, поскольку дает возможность программисту определять по
имени, к какому именно элементу происходит обращение. В нашем
примере два элемента имеют одинаковые имена - LastName, однако их предназначение различно:
один используется для вывода статического текста "Фамилия", а другой
- для ввода фамилии. Префиксы lbl и
tb позволяют, с одной стороны,
использовать одинаковые логические имена, а с другой - отличать
элементы друг от друга.

Изменим код обработчика события нажатия на кнопку
"Отправить" так, чтобы с помощью элемента lbl_Result на экран выводилось приглашение,
которое состоит из фамилии и имени, введенных в соответствующие
поля. Кроме того, установим красный цвет выводимого на экран текста.
Для этого в обработчике события Button1_Click введем следующий код:

lbl_Result.Text = "Здравствуйте, "+tb_FirstName.Text+" "+
tb_LastName.Text+"! Добро пожаловать в приложение ASP.NET";
lbl_Result.ForeColor = Color.Red;

При попытке запуска приложения на экране появится
следующее сообщение:

Рис. 4.3.  Сообщение о
наличии ошибки в запускаемом приложении

Оно означает, что в коде нашего приложения существует
ошибка, которая препятствует корректному его исполнению. После
нажатия на кнопку No в окне Visual Studio будет выведено сообщение,
детализирующее возникшую ошибку (рис.
4.4). В нашем случае ошибка означает, что ключевое слово Color, которое мы использовали для того, чтобы
задать тексту красный цвет, не существует в данном контексте. Причем
при двойном щелчке по строке, содержащей расшифровку ошибки, Visual
Studio выделяет то место в исходном коде приложения, которое вызвало
данную ошибку.

увеличить изображение
Рис. 4.4.
Выделение места ошибки в коде приложения и детализированное ее
описание

Для выяснения подробностей ошибки можно воспользоваться
следующим инструментом, имеющимся в арсенале Visual Studio 2005.
Если навести указатель мыши на выделенный фрагмент кода, содержащий
ошибку, на экране появляется смарт-тег,
при раскрытии которого выводятся некоторые подробности относительно
возникшей ошибки (рис.
4.5).

Рис. 4.5.  Смарт-тег,
содержащий подробности возникшей в процесс выполнения приложения
ошибки

В данном случае из рисунка видно, что для исправления
ошибки необходимо подключить к проекту область имен (namespace),
содержащую определение типа Color. Эта
область имен называется Drawing и
содержится в System. Для подключения
области имен необходимо ввести в начале файла Default.aspx.cs следующую строку:

using System.Drawing;

Произведем запуск приложения. Введем в соответствующие
поля ввода произвольные фамилию и имя и нажмем кнопку "Отправить". В
результате обработки нажатия кнопки окно браузера будет выглядеть
следующим образом (рис.
4.6).

Рис. 4.6.  Результат
работы Web-приложения

Динамическое создание элементов управления

Иногда в процессе работы над приложением возникает
необходимость создания динамического интерфейса. Это становится
особенно актуальным в случае, когда интерфейс приложения должен
меняться в зависимости от действий пользователя. В этом случае
удобным инструментом реализации динамического интерфейса становится
возможность динамического создания и отображения на странице
элементов управления. Для создания элемента управления необходимо
создать объект соответствующего класса, присвоить его атрибутам
необходимые значения и добавить его к коллекции элементов управления
страницы. При этом следует помнить о том, что поскольку в момент
исполнения программного кода, создающего объект и добавляющего его к
коллекции элементов управления, страница уже создана, добавление
будет всегда происходить в конец коллекции. Это означает, что
элемент будет добавляться после последнего элемента управления
страницы.

Чтобы получить больший контроль над расположением
динамически создаваемого элемента управления, можно воспользоваться
специально предназначенным для этого элементом Placeholder. Данный элемент управления
предназначен для размещения других элементов внутри себя и может
быть расположен в любом месте страницы. При этом, если внутри Placeholder отсутствует содержимое, это никак
не повлияет на содержимое страницы.

При использовании динамически создаваемых элементов
управления необходимо помнить, что они существуют только до
очередной обратной отсылки. Если после обратной отсылки динамически
созданный элемент управления необходимо по-прежнему отображать на
странице, нужно создавать элемент управления в обработчике события
Page.Load. При создании элемента
управления в обработчике события Page.Load ASP.NET использует любую информацию
о состоянии вида по завершении этого обработчика события.

Чтобы иметь возможность программного взаимодействия с
динамически созданным элементом управления, последнему необходимо
присвоить уникальный идентификатор (ID), являющийся аналогом ID
любого другого элемента управления. В дальнейшем этот идентификатор
можно использовать для того, чтобы найти данный элемент управления в
коллекции элементов управления страницы с помощью метода Page.FindControl() и использовать его в
программном коде.

Проиллюстрируем все вышесказанное следующим примером.
Предположим, необходимо в рассматриваемом выше примере ввести
возможность ввода отчества по желанию пользователя. При этом
нежелательно, чтобы поле для ввода отчества появлялось на экране,
если пользователь включит соответствующий режим. Реализовать это
можно следующим образом.

Разместим на форме элемент управления CheckBox и установим свойство AutoPostBack этого элемента равным True.

Разместим на форме элемент управления Placeholder. В результате этих действий
страница в режиме дизайна будет выглядеть следующим образом (рис.
4.7):

Рис. 4.7.  Отображение
страницы примера в режиме дизайна

Модифицируем исходный код нашего приложения. В
обработчик события Page.Load добавим
следующий код, создающий объекты типа Label и TextBox и
добавляющий их к коллекции Controls
элемента Placeholder:

if (CheckBox1.Checked)
{
Label lblSName = new Label();
lblSName.ID = "lbl_SName";
lblSName.Text = "Отчество";
PlaceHolder1.Controls.Add(lblSName);
TextBox tbSName = new TextBox();
tbSName.ID = "tb_SName";
PlaceHolder1.Controls.Add(tbSName);
}

Внесем изменения в программный код обработчика события
нажатия на кнопку "Отправить". Полный текст программного кода этого
обработчика представлен ниже.

string sname=string.Empty;
TextBox tbSname=(TextBox)Page.FindControl("tb_SName");
if (tbSname!=null)
{
sname = tbSname.Text;
}
lbl_Result.Text = "Здравствуйте, "+tb_FirstName.Text+"
"+sname+" "+
tb_LastName.Text+"! Добро пожаловать в приложение ASP.NET";
lbl_Result.ForeColor = Color.Red;

Из примера видно, что с помощью метода Page.FindControl происходит поиск элемента
управления tb_SName. В случае, если он
будет найден на странице, значение свойства Text данного элемента записывается в
переменную sname, которая затем используется для вывода информации
на экран.

Результат работы программы изображен на рис.
4.8.

Рис. 4.8.  Результат
использования динамически созданного элемента управления

Динамически созданные элементы управления могут
обрабатывать события. Для реализации этой возможности необходимо
подключить обработчик события с помощью кода делегата в событии Page.Load. Т.к. все события элементов
управления генерируются после Page.Load,
подключение обработчика события динамического
элемента управления за пределами Page.Load приведет к тому, что событие
элемента управления обрабатываться не будет.

Чтобы подключить обработчик события изменения текста, в
динамически созданном элементе управления необходимо выполнить
следующие действия.

Добавить процедуру обработчика события:

protected void tb_SName_TextChanged(object sender, EventArgs e)
{

}

В событии Page.Load
привязать этот обработчик к соответствующему событию элемента
управления:

tbSName.TextChanged += new System.EventHandler
(this.tb_SName_TextChanged);

Класс Page

Ранее было показано, что все элементы управления, из
которых состоит Web-приложение, добавляются в контейнер, роль
которого выполняет страница приложения. Страница отвечает за
генерацию HTML-кода, передаваемого впоследствии клиенту. При этом,
однако, каждый элемент управления отвечает за генерацию своей части
HTML-кода, описывающего именно этот элемент внутри страницы. Можно
сказать, что страница как бы руководит процессом создания страницы,
заставляя каждый элемент управления генерировать свою часть кода. В
ASP.NET любая страница является экземпляром класса Page.

Класс Page играет исключительно важную роль в
ASP.NET, так как с помощью него осуществляется доступ к большому
количеству объектов, используемых для управления страницами
Web-приложения.

Ниже приводятся свойства и методы объекта Page.

Application

Сохранение данных в переменных
состояния приложения.

Cache

Управление кэшированием
откликов на сервере.

Controls

Получение элементов управления
страницы.

Request

Чтение запросов и получение из
текущего запроса объектов Browser,
Cookies, Files, ClientCertificates.

Response

Запись текста и данных в
отклик, получение из текущего отклика объектов Cache, Cookies, Output.

Server

Обработка запросов и откликов,
вспомогательные методы кодирования и декодирования URL.

Session

Сохранение элементов данных в
переменных состояния сеанса.

Trace

Включение и выключение
трассировки, запись трассировочных событий в
журнал.

Рассмотрим самые важные из них.

Коллекция ViewState, объекты Session и Application

Коллекция ViewState

Ранее указывались некоторые сложности, возникающие в
процессе разработки Web-приложения. В частности, говорилось о том,
что значения всех данных, введенных в элементы управления, после
передачи клиенту уничтожаются, т.к. уничтожается сама страница,
содержащая эти элементы управления. Для того чтобы сохранять
значения элементов управления между обращениями к странице
приложения, можно использовать несколько механизмов, рассматриваемых
ниже.

Прежде всего, необходимо рассмотреть возможность
применения состояния вида (ViewState). Его целесообразно использовать в
том случае, когда необходимо организовать хранение данных в пределах
одной страницы. Все элементы управления используют состояние вида по умолчанию для сохранения
значений свойств между операциями обратной отсылки данных. Здесь же
возможно организовать хранение своих собственных данных, состоящих
из простых типов и специальных объектов.

Состояние вида организовано
по принципу коллекции, которая, в свою очередь, имеет тип словаря.
Это означает, что данные хранятся в формате "имя-значение". Каждый
элемент при этом индексируется с помощью уникального строкового
имени. Следующий пример добавляет в коллекцию ViewState элемент с именем Name и присваивает
ему значение "Иван":

ViewState["Name"]="Иван";

При этом, если в коллекции до этого не существовало
элемента с именем Name, то он
добавляется, если же такой элемент был, его значение заменяется
новым.

Для извлечения элемента из коллекции необходимо
использовать имя элемента. Кроме того, т. к. коллекция ViewState позволяет сохранять не только
данные, состоящие из простых типов, но и специальные объекты (в
общем случае - любые объекты), во время извлечения значения элемента
необходимо преобразовать его тип к тому, который будет извлекаться.
Следующий пример позволяет извлечь значение элемента Name и преобразовать его в строку:

string name;
if (ViewState["Name"]!=null)
name=(string)ViewState["Name"];

Проверка на наличие элемента коллекции необходима, т.
к. при обращении к несуществующему элементу коллекции возникает
исключение NullReferenceException.

В качестве примера создадим возможность сохранения
значений, введенных в текстовые поля предыдущего примера.

Для демонстрации использования ViewState создадим пример на базе предыдущего,
в котором после нажатия на кнопку "Отправить" данные текстовых полей
сохраняются в состоянии вида, а поля очищаются. После нажатия на
кнопку "Восстановить значения полей ввода" сохраненные значения
полей считываются из состояния вида и помещаются в текстовые поля.
Данный пример необходим только для демонстрации использования
коллекции ViewState.

Для начала поместим на странице HTML-элемент "Таблица",
который представляет собой описание обычной таблицы, и разместим в
ее ячейках уже существующие элементы Label и TextBox.
Кроме того, добавим на страницу кнопку. Присвоим вновь добавленной
кнопке следующие параметры: ID=btn_RestoreTextBox, Text="Восстановить значения
полей ввода".

Получившаяся в результате страница в режиме
редактирования дизайна должна выглядеть следующим образом (рис.
4.9).

Рис. 4.9.  Web-страница
примера в режиме дизайна

Внесем в исходный код страницы исправления в
соответствии с рис.
4.10.

увеличить изображение
Рис. 4.10.
Исходный код страницы

Как видно из рис.
4.10, в стандартный элемент HTML Table были добавлены два элемента:
идентификатор ID=Table и ключевое слово,
позволяющее обращаться к нему на стороне сервера - runat="server". Это необходимо для того, чтобы
во время выполнения приложения у нас была возможность обращения к
данному элементу из программного кода.

Теперь нужно изменить исходный код приложения, добавив
к нему возможность сохранения и восстановления значений полей ввода
с использованием ViewState. Для этого
нам необходимо считать содержимое полей и поместить их в коллекцию.
Т. к. в нашем случае таких полей всего два, проще будет использовать
прямое обращение к ним, которое можно представить в такой форме:

ViewState["FirstName"]=tb_FirstName.Text;
ViewState["LastName"]=tb_LastName.Text;

Для восстановления значений, сохраненных в состояние вида, можно воспользоваться следующим
фрагментом кода:

tb_FirstName.Text=(string)ViewState["FirstName"];
tb_LastName.Text=(string)ViewState["LastName"];

Тем не менее на практике часто возникает необходимость
сохранения в состояние вида значений
сразу нескольких элементов управления, которых на странице может
присутствовать достаточно много. Для того чтобы сделать программный
код более гибким и не перечислять в нем наименования всех элементов
управления, расположенных на форме, можно организовать сохранение
данных путем последовательного обращения к каждому из них в цикле.
Известно, что все элементы управления, размещенные на форме,
помещаются в коллекцию элементов управления, обращение к которой
возможно осуществить при помощи свойства Controls. Этот механизм можно применять и к
другим элементам управления, т. к. многие из них сами могут
содержать другие элементы управления, которые, в свою очередь, могут
содержать внутри себя еще элементы и т. д. Таким образом, для того,
чтобы обеспечить чтение значений, введенных во все необходимые
элементы управления, необходимо выбрать первый элемент управления,
перебрать все элементы, входящие в него, при этом перебирая все их
вложенные элементы до тех пор, пока не будут перебраны все элементы
управления. Проще всего это сделать с помощью рекурсивной функции,
которая вызывает себя в случае, если внутри текущего элемента
управления существуют еще элементы управления. Также необходимо
учесть, что в данном случае нас интересуют только элементы
управления типа TextBox. Реализация
такой функции может выглядеть следующим образом:

private void SaveTextinTextBox(ControlCollection controls, bool
SaveNested)
{
foreach (Control control in controls)
{
if (control is TextBox)
{
//Сохранить текст
ViewState[control.ID]=((TextBox)control).Text;
}
if (control.Controls != null && SaveNested)
{
SaveTextinTextBox(control.Controls, true);
}
}
}

Функция содержит два аргумента. Первый предназначен для
передачи набора элементов управления, с которыми и предстоит
работать, второй - для указания на необходимость осуществления
перебора элементов управления, вложенных в текущий.

Цикл foreach осуществляет
перебор всех элементов управления, переданных с помощью первого
аргумента. Условие if (control is
TextBox) необходимо для определения типа элемента управления.
В случае, если текущий элемент управления является текстовым полем,
его значение сохраняется в состояние
вида, причем идентификатором элемента в коллекции ViewState будет являться ID элемента
управления.

Условие if (control.Controls !=
null && SaveNested) позволяет вызвать функцию SaveTextinTextBox в случае, если текущий
элемент управления имеет внутри себя вложенные элементы. Таким
образом, функция позволяет осуществить перебор всех элементов
управления, которые расположены на странице либо внутри другого
элемента, избранного отправной точкой. В нашем примере таковым может
являться объект Table1.

Функция чтения значений ViewState и их записи в соответствующие
элементы управления может выглядеть следующим образом:

private void RestoreTexttoTextBox(ControlCollection controls, bool
saveNested)
{
foreach (Control control in controls)
{
if (control is TextBox)
{
if (ViewState[control.ID]!=null)
((TextBox)control).Text=(string)ViewState[control.ID];
}
if (control.Controls !=null && saveNested)
{
RestoreTexttoTextBox(control.Controls,true);
}
}
}

Она действует аналогичным образом, являясь рекурсивной.
Создадим две приведенные функции внутри исходного кода
рассматриваемого примера, а также внесем изменения в процедуру
обработки нажатия на кнопку "Отправить". Теперь полная версия
обработчика события нажатия на эту кнопку выглядит следующим
образом:

protected void Button1_Click(object sender, EventArgs e)
{
string sname=string.Empty;
TextBox tbSname=(TextBox)Page.FindControl("tb_SName");
if (tbSname!=null)
{
sname = tbSname.Text;
}
lbl_Result.Text = "Здравствуйте, "+tb_FirstName.Text+"
"+sname+" "+
tb_LastName.Text+"! Добро пожаловать в приложение ASP.NET";
lbl_Result.ForeColor = Color.Red;
//сохраняем значения полей ввода в коллекции ViewState
SaveTextinTextBox(Table1.Controls, true);
tb_FirstName.Text = "";
tb_LastName.Text = "";
}

Создадим обработчик для события нажатия на кнопку
"Восстановить значения полей ввода". Он выглядит следующим
образом:

protected void btn_RestoreTextBox_Click(object sender, EventArgs e)
{
RestoreTexttoTextBox(Table1.Controls,true);
}

Теперь можно запустить приложение. При вводе значений в
поля ввода текста и нажатии на кнопку "Отправить" на экран выводится
надпись приветствия, а текст внутри полей ввода уничтожается. После
нажатия на кнопку "Восстановить значения полей ввода" поля вновь
заполняются текстом, введенным ранее. Это становится возможным
благодаря использованию коллекции ViewState.

Сохранение объектов в состоянии вида

Как уже говорилось, состояние
вида позволяет хранить, кроме простых типов, также и объекты.
Для того чтобы элемент мог быть сохранен в состояние вида, он должен быть сперва
преобразован в последовательность байтов. Такой процесс называется
сериализацией. При создании нового
класса по умолчанию его объекты не поддаются сериализации. При
попытке поместить их в состояние вида
появляется сообщение об ошибке. Для того чтобы создать объект,
поддающийся сериализации, необходимо перед объявлением класса
данного объекта разместить атрибут Serializable. Пример создания и сохранения
класса в состоянии вида приведен ниже.

Здесь создается класс User,
который может быть подвергнут сериализации, и создается объект этого
класса, который затем сохраняется в состояние
вида. Полный исходный текст приложения приведен ниже.

public partial class _Default : System.Web.UI.Page
{
[Serializable]
public class User
{
public string FirstName;
public string LastName;
public User(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
protected void Button1_Click(object sender, EventArgs e)
{
lbl_Result.Text = "Здравствуйте, "+tb_FirstName.Text+" "+
tb_LastName.Text+"! Добро пожаловать в приложение ASP.NET";
lbl_Result.ForeColor = Color.Red;
//Сохраняем объект в ViewState
User user = new User(tb_FirstName.Text, tb_LastName.Text);
ViewState["CurrentUser"] = user;
tb_FirstName.Text = "";
tb_LastName.Text = "";
}
protected void btn_RestoreTextBox_Click(object sender, EventArgs e)
{
//Восстанавливаем значение объекта User
User user;
if (ViewState["CurrentUser"] != null)
{
user = (User)ViewState["CurrentUser"];
tb_LastName.Text = user.LastName;
tb_FirstName.Text = user.FirstName;
}
}
protected void Page_Load(object sender, EventArgs e)
{

}
}

Использование состояния вида является хорошим вариантом
сохранения значений переменных и полей форм между вызовами, т. к. не
предполагает задействование ресурсов памяти сервера. Тем не менее
существуют несколько отрицательных сторон применения ViewState.

1. Данные ViewState
сохраняются в скрытых полях страницы и передаются на сторону
клиента. В этом можно убедиться, просмотрев исходный код такой
страницы. Конечно, эти данные не хранятся в открытом виде, они
представлены в формате Base64, однако их легко можно преобразовать в
массив байт, представляющих символы ASCII. Для этого достаточно
использовать функцию FromBase64String
как показано в следующем примере:

Byte[] stringBytes=Convert.FromBase64String(ViewStateString);
String decodedString=System.Text.Encoding.ASCII.GetString(stringBytes);

В результате этих действий в строке decodedString будет содержаться строка,
полученная в результате преобразования Base64 строки в набор
символов ASCII, которые могут быть выведены на экран и легко
прочитаны.

Все это заставляет сделать два важных вывода.
Во-первых, количество информации, сохраняемой в состояние вида, не должно быть большим, т. к.
это приводит к увеличению объема передаваемых данных от сервера к
клиенту и наоборот. В случае необходимости сохранения большого
количества данных лучше воспользоваться средствами базы данных либо
использовать состояние сеанса. Во-вторых, в ViewState нельзя сохранять критичные данные
(данные, доступ пользователя к которым необходимо запретить), т. к.
они легко могут быть декодированы и прочитаны, более того, опытный
пользователь сможет изменить эти данные при осуществлении запроса на
обратную отсылку. В этом случае лучше воспользоваться состоянием
сеанса.

2. ViewState не позволяет
сохранять информацию, которая будет использоваться несколькими
страницами. В этом случае лучше воспользоваться состоянием сеанса,
наборами cookie либо строкой запроса.

Объект Session

Объект Session предназначен для реализации
механизма состояния сеанса, используемого для хранения любого типа
пользовательских данных, которые необходимо сохранять между
запросами Web-страниц. Пользовательские данные при этом сохраняются
в формате "имя-значение". Такой механизм, в частности, можно
применять при создании Интернет-магазина, где покупатель перед
покупкой складывает товары в виртуальную корзину, которая после
завершения сеанса (перехода на другую страницу либо завершения
работы с браузером) должна быть удалена. Для сохранения данных о
выбранных товарах можно воспользоваться объектом Session.

При подключении пользователя к приложению создается
отдельный сеанс и отдельная коллекция данных. Такие возможности
имеют и свою цену - данные, сохраняемые в Session, хранятся в оперативной памяти
сервера. Даже при маленьком объеме этих данных их использование
может угрожать производительности приложения в том случае, если
доступ к сайту начнут получать сотни и тысячи клиентов.

Работа с состоянием сеанса практически аналогична
работе с состоянием вида, за исключением того, что вместо ключевого
слова ViewState применяется ключевое
слово Session. Например, для сохранения
объекта user в памяти сеанса необходимо
выполнить следующий код:

Session["user"]=user;

Для восстановления сохраненного объекта user необходимо
воспользоваться следующим кодом:

user=(User)Session["user"];

Состояние сеанса уничтожается в следующих случаях:

если пользователь закрывает браузер;

по истечении 20 минут с момента последней активности
пользователя;

при явном завершении сеанса из программного кода с помощью
вызова метода Session.Abandon();

Объект Application

Объект Application во многом аналогичен объекту
Session. Различие заключается только в
том, что данные, хранящиеся в нем, глобальны для всего приложения.
Это так называемое состояние приложения, доступ к данным которого
может получить любой клиент.

Состояние приложения похоже на состояние сеанса, т. к.
хранит информацию на сервере, позволяет сохранять объекты такого же
типа и использует формат "имя-значение" для хранения данных.

С помощью состояния приложения можно создать счетчик и
отслеживать информацию о том, сколько раз то или иное действие
выполнялось всеми клиентами данного приложения.

Для этого можно воспользоваться следующим кодом:

protected void Page_Load(object sender, EventArgs e)
{
int count;
if (Application["HitsNumber"] == null)
{
Application["HitsNumber"] = 1;
count = (int)Application["HitsNumber"];
Label1.Text = count.ToString();
return;
}
count = (int)Application["HitsNumber"];
count++;
Application["HitsNumber"]=count;
Label1.Text = count.ToString();
}

Время жизни элементов состояния приложения никогда не
истекает - они существуют до тех пор, пока приложение или сервер не
будут перезапущены.

В современной практике состояние приложения применяется
крайне редко, т. к. не является эффективным. Так, предыдущий пример
не гарантирует корректного отображения значения счетчика. Это
происходит в том случае, если к приложению одновременно обращаются
несколько клиентов. В этом случае каждый из них считывает одно и то
же текущее значение состояния приложения HitsNumber, затем увеличивает его на единицу и
вновь записывает в состояние приложения. Таким образом, все
одновременные подключения, сколько бы их ни было, приводят к
увеличению состояния приложения лишь на единицу. Для того чтобы
избежать этой проблемы, возможно применение методов Lock() и Unlock(),
которые позволяют одновременно получить доступ к коллекции только
одному клиенту. Например, таким образом:

protected void Page_Load(object sender, EventArgs e)
{
int count;
Application.Lock();
if (Application["HitsNumber"] == null)
{
Application["HitsNumber"] = 1;
count = (int)Application["HitsNumber"];
Label1.Text = count.ToString();
return;
}
count = (int)Application["HitsNumber"];
count++;
Application["HitsNumber"]=count;
Application.UnLock();
Label1.Text = count.ToString();
}

Недостатком этого подхода является то, что все клиенты,
которые запрашивают страницу, должны ожидать, пока коллекция Application не освободится. Это может привести
к задержкам и значительно снизить производительность приложения. Вот
почему рекомендуется использовать файл web.config для хранения констант уровня
приложения, а часто используемую информацию лучше всего сохранять в
кэше ASP.NET.

Использование строки запроса

Одной из часто возникающих проблем при разработке
Web-приложений является передача информации от одной страницы к
другой и от одного приложения к другому приложению. Существует
несколько способов решения этой проблемы, одним из которых является
использование строки запроса, когда данные передаются в
URL-адресе.

Этот подход очень часто применяется в поисковых
системах. Например, вот как выглядят URL наиболее популярных
поисковых систем при поиске строки "Web design":

Поисковая система Yandex http://www.yandex.ru/yandsearch?text=Web+design

Поисковая система Rambler http://www.rambler.ru/srch?words=Web+design

Поисковая система Google http://www.google.ru/search?hl=ru&newwindow=1&q=Web+design&lr=

Из представленных примеров видно, что ключевые слова,
составляющие запрос, указаны в строке URL после знака вопроса.
Именно эта часть URL называется строкой запроса. Таким образом
возможно организовать передачу значений параметров прямо в строке
запроса. Преимущество такого подхода заключается в том, что строка
запроса проста по своей структуре и не вызывает нагрузки на сервер,
- с помощью такого механизма можно легко переносить информацию с
одной страницы на другую. Недостаток этого подхода состоит в том,
что с помощью строки запроса возможно передавать только информацию в
виде простых строк, содержащих символы, которые допускается
использовать в URL-адресе.

Для передачи информации в строке запроса, ее
(информацию) необходимо поместить в URL-адрес страницы, к которой
должен произойти переход. Это можно сделать используя элемент
управления HyperLink, либо
воспользовавшись оператором Response.Redirect().

Например, для того чтобы перейти на страницу login.aspx и передать в строке запроса
переменную username, необходимо
выполнить следующий код:

String FirstName="Иван";
String LastName="Иванов";
Response.Redirect("login.aspx?username="+FirstName+" "+LastName);

Для передачи нескольких параметров в строке запроса
параметры необходимо разделять знаком амперсанд - "&". С учетом
этого предыдущий пример можно переделать так, чтобы имя и фамилия
пользователя передавались отдельно. Для этого изменим строку Response.Redirect следующим образом:

Response.Redirect("login.aspx?firstname="+FirstName+"&lastname=
"+LastName);

Для извлечения строки запроса необходимо использовать
метод QueryString объекта Request. Для извлечения значений параметров,
передаваемых в предыдущих примерах, нужно применять следующий
код:

string FN=Request.QueryString["firstname"];
string LN=Request.QueryString["lastname"];

При использовании строки запроса следует помнить, что
она передается всегда в открытом виде, поэтому является очень
уязвимой. Для защиты информации, передаваемой в строке запроса,
можно применять шифрование. Подробнее об этом можно узнать в [1].

Объекты Response и Request

Объект Request содержит информацию, присланную
клиентским браузером при запросе страницы приложения. Свойства и
методы Request позволяют решать задачи,
связанные с аутентификацией пользователя, приемом файлов от клиента,
определением типа браузера клиента. Ниже приведен перечень свойств и
методов объекта Request.

Browser

Определение номера версии
запрашивающего браузера, возможность поддержки им файлов cookie и другой служебной
информации.

ClientCertificates

Аутентификация клиента.

Cookies

Получение файлов cookie от клиента.

Files

Получение файлов, передаваемых
клиентом.

InputStream

Чтение и запись переданного
запроса в виде неструктурированных
данных.

Одной из наиболее часто используемых возможностей
объектов Request и Response является работа с файлами cookie. Файлы cookie являются одним из возможных механизмов
сохранения информации во время работы приложения для ее дальнейшего
использования. Эти файлы сохраняются на жестком диске компьютера
клиента. Преимуществом cookie является
то, что они работают автоматически, пользователь же при этом даже не
знает, что какая-то информация должна быть сохранена. Файлы могут
храниться достаточно долго, что обеспечивает возможность передачи
информации между посещениями. Файлы cookie могут хранить только простую строковую
информацию, а за счет того, что они сохраняются на жестком диске
компьютера, пользователь может легко найти соответствующий файл и
легко прочитать его содержимое. Таким образом, cookie присущи те же недостатки, что и строке
запроса, поэтому не рекомендуется использовать такого рода механизм
для сохранения сложной или секретной информации, а также при
необходимости сохранения большого объема данных.

Например, для проверки того, поддерживает ли браузер
клиента файлы cookie, необходимо
выполнить следующий код:

if (!IsPostBack)
if (Request.Browser.Cookies)
Response.Write("Ваш Браузер поддерживает cookies");
else
Response.Write("Ваш Браузер не поддерживает cookies");

Для определения типа браузера клиента полезно
воспользоваться свойством Browser
объекта Request. Данный код выводит в
окне браузера его тип.

Response.Write("Ваш браузер - "+Request.Browser.Browser);

Объект Response необходим
для формирования отклика сервера на запрос клиента. С помощью
свойств и методов Response можно
управлять кэшированием откликов перед отправкой клиенту, изменять
содержимое файлов cookie, читать и
записывать неструктурированные данные, передаваемые клиенту.

Ниже приведен перечень свойств и методов объекта Response.

Cashe

Определяет кэширование откликов
перед отправкой их клиенту.

Cookies

Позволяет задавать содержимое
файлов cookie, передаваемых клиенту.

Output

Позволяет читать и записывать
неструктурированные данные, возвращаемые клиенту в виде
отклика.

В следующем примере при наличии в браузере поддержки
создается cookie UserName, которому
присваивается значение "Иванов Иван".

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
if (Request.Browser.Cookies)
if (Request.Cookies["UserName"] != null)
{
Session["User"] = Request.Cookies["UserName"].Value;
Response.Write(Request.Cookies["UserName"].Value);
}
else
{
HttpCookie uname = new HttpCookie("UserName");
uname.Value = "Иванов Иван";
Response.Cookies.Add(uname);
}
else
Response.Write("Ваш Браузер не поддерживает cookies");
}

Cookie, используемый в
предыдущем примере, будет сохраняться до тех пор, пока пользователь
не закроет окно браузера, при этом он будет отправляться с каждым
запросом. При необходимости сохранения cookie в течение определенного времени
необходимо установить дату истечения срока действия cookie. В следующем примере cookie будет храниться в течение одного
года.

uname.Expires=DateTime.Now.AddYears(1);

При необходимости удаления cookie нужно установить для него
"просроченную" дату истечения срока действия. Это можно сделать
следующим образом:

uname.Expires=DateTime.Now.AddDays(-1);

Краткие итоги

Способы взаимодействия программного кода с
интерфейсными элементами управления ASP.NET аналогичны таковым при
разработке классических Windows-приложений. При генерации HTML-кода
страницы ASP.NET элементы управления преобразуются в теги HTML.
Элементы управления, используемые для отображения данных на
Web-странице, могут размещаться на странице как путем перетаскивания
с панели Toolbox, так и динамическим
способом в процессе работы приложения. При этом возможно создание
обработчиков событий таких элементов управления с использованием
делегатов.

Любые элементы управления, размещаемые на странице,
помещаются в контейнер, роль которого выполняет страница приложения,
представленная в ASP.NET классом Page. С
помощью данного класса можно получить доступ к большому количеству
объектов, используемых для управления страницами Web-приложения. В
силу специфики работы интернет-приложений существуют определенные
сложности организации обработки информации. Эти сложности связаны с
тем, что после генерации кода HTML и отправки его клиенту экземпляр
формы, расположенный на сервере, уничтожается, следовательно,
уничтожаются и данные элементов управления, содержащихся внутри этой
формы. Для того чтобы сохранять значения элементов управления между
обращениями к странице, необходимо использовать коллекции ViewState, объекты Session или Application.

Существует несколько способов передачи информации от
одной страницы к другой и от одного приложения к другому. Одним из
таких способов является использование строки запроса.

Для обмена информацией между браузером клиента и
сервером существуют объекты Response и
Request. Одной из наиболее часто
используемых возможностей этих объектов является работа с файлами
cookie. Эти файлы сохраняются на жестком
диске клиента и могут задействоваться для сохранения данных между
сеансами работы с Web-приложением. Однако в силу слабой защищенности
информации, сохраняемой в файлах cookie,
не рекомендуется использовать их для хранения
сложноструктурированной или секретной информации.

© gsu.by