Обработка веб-форм

98

На стороне сервера обработка веб-формы ASP.NET происходит поэтапно. На каждом этапе генерируются различные события. Это позволяет странице включиться в поток обработки на любом этапе и выдать ответ любым удобным способом. В следующем списке перечислены основные этапы потока обработки страницы ASP.NET:

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

Инициализация структуры страницы

На этом этапе ASP.NET создает страницу. Генерируются все элементы управления, определенные в дескрипторах внутри веб-страницы .aspx. Более того, если страница запрашивается не впервые (иначе говоря, если это обратная отправка), ASP.NET десериализирует информацию о состоянии представления и применяет ее ко всем элементам управления.

На этом этапе запускается событие Page.Init. Однако это событие редко обрабатывается веб-страницей, поскольку еще слишком рано выполнять инициализацию страницы. Причина в том, что объекты элементов управления еще не созданы, а информация о состоянии представления пока не загружена.

Инициализация кода пользователя

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

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

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

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

Определить текущее состояние страницы можно, проверив ее свойство IsPostBack, которое при первом запросе страницы будет иметь значение false, например:

if (!Page.IsPostBack) 
{ 
   // Инициализировать элементы управления при первом 
   // обращении вполне безопасно. 
   FirstName.Text = "Enter your name here"; 
} 

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

Наоборот, устанавливайте свойства в дескрипторе элемента управления (вручную либо в окне Properties (Свойства)). В результате эта информация не будет сохраняться в состоянии представления. В тех случаях, когда действительно легче инициализировать элемент управления в коде, отключите состояние представления для элемента управления, установив EnableViewState в false и инициализируя элемент управления каждый раз при запуске события Page.Load независимо от того, является ли текущий запрос обратной отправкой.

Проверка достоверности

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

Обработка событий

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

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

Например, предположим, что имеется страница с кнопкой Submit и текстовым полем, которое не выполняет обратную отправку данных автоматически. Вы изменяете текст в текстовом поле и щелкаете на кнопке Submit. После этого ASP.NET запускает все следующие события (именно в таком порядке):

  1. Page.Init

  2. Page.Load

  3. TextBox.TextChanged

  4. Button.Click

  5. Page.PreRender

  6. Page.Unload

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

Положительная сторона заключается в том, что модель событий предоставляет более высокий уровень абстракции, что избавляет код от стереотипных фрагментов поддержки состояния. Отрицательная сторона — очень легко забыть о том, что модель событий на самом деле является просто эмуляцией. Это может привести к некорректному предположению (например, ожиданию того, что информация остается в переменных экземпляров) или же неудачному проектному решению (например, сохранению большого количества информации в состоянии представления).

Очистка

В конце своего жизненного цикла страница преобразуется в HTML-разметку. После этого начинается реальная очистка и запускается событие Page.Unload. В этот момент объекты страницы все еще доступны, но окончательная HTML-разметка уже сгенерирована и не может быть изменена.

Вспомните, что у .NET Framework имеется служба сборки мусора, периодически запускаемая для освобождения памяти, занятой объектами, на которые уже нет ссылок. Неуправляемые ресурсы должны освобождаться явно на этапе очистки или, что лучше, перед ним. Когда сборщик мусора уничтожает страницу, запускается событие Page.Disposed. На этом жизненный цикл веб-страницы завершен.

Пример потока обработки страницы

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

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="PageFlow.aspx.cs" Inherits="WebApplication1.PageFlow" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Page Flow</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="lblInfo" runat="server" EnableViewState="false"></asp:Label>
        <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click"/>
    </div>
    </form>
</body>
</html>

Далее необходимо добавить обработчики событий. По завершении в файле отделенного кода будет содержаться пять обработчиков событий, реагирующих на разные события, включая Page.Init, Page.Load, Page.PreRender, Page.Unload и Button.Click.

Обработчики событий страницы являются особым случаем. В отличие от обработчиков событий других элементов управления, их не требуется специально присоединять с помощью атрибутов разметки, поскольку при условии, что в них указывается корректное имя метода (и предполагая, что в директиве Page для AutoEventWireup указано значение true, устанавливаемое по умолчанию), они присоединяются автоматически:

        protected void Page_Load(object sender, EventArgs e)
        {
            lblInfo.Text += "Событие Page.Load произошло<br/>";
            if (Page.IsPostBack)
                lblInfo.Text += "<b>Страница загружена не в первый раз.</b><br/>";
        }

        protected void Page_Init(object sender, EventArgs e)
        {
            lblInfo.Text += "Событие Page.Init произошло<br/>";
        }

        protected void Page_PreRender(object sender, EventArgs e)
        {
            lblInfo.Text += "Событие Page.PreRender произошло<br/>";
        }

        protected void Page_Unload(object sender, EventArgs e)
        {
            // Этот текст никогда не отображается, поскольку к этому 
            // моменту весь HTML-код для страницы уже сгенерирован. 
            lblInfo.Text += "Событие Page.Unload произошло<br/>";
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            lblInfo.Text += "Событие Button.Click произошло<br/>";
        }

Обработчики страниц подключаются явно с использованием делегатов в скрытой части кода конструктора. Поскольку этот код конструктора все равно считается частью вашего класса (благодаря "волшебному" механизму частичных классов), он может подключать любой метод, в том числе и приватный. Обработчики событий элементов управления присоединяются с помощью другого механизма — дескриптора элемента управления. И происходит это на более поздней стадии обработки, а точнее — после объединения содержащейся в файле .aspx разметки и класса отделенного кода. ASP.NET выполняет это объединение путем создания нового класса, производного от класса отделенного кода.

На рисунке показана страница ASP.NET после щелчка на кнопке, инициирующей обратную отправку и событие Button.Click. Обратите внимание, что хотя к обратной отправке привело именно событие Button.Click, сначала все-таки были обработаны события Page.Init и Page.Load:

Порядок выполнения операций в ASP.NET
Пройди тесты
Лучший чат для C# программистов