Опубликован: 07.05.2010 | Уровень: для всех | Доступ: платный
Лекция 3:

Web-формы

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

При формировании обратной отсылки броузер собирает с элементов дескриптора <form> значения полей и состояния. Он не проверяет соответствие типов собираемых данных ожидаемым значениям. Например, если текстовое поле ожидает цифры, а пользователь ввел туда буквы, значение поля нормально будет передано на сервер. Но серверный код ожидает цифры и готов применить к ним математические операции.

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

  • Запустите страницу Default.aspx и введите в поле TextBox1 какой-нибудь текст в угловых скобках

Сервер немедленно прервет выполнение страницы и выдаст сообщение Server Error in '/WebSite1' Application с подробным анализом ошибки.

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

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

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

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

  1. События, требующие немедленного ответа сервера (щелчок на кнопке Submit, изменение состояния элемента управления с установленным свойством AutoPostBack, и др.). Эти события инициируют немедленную обратную отсылку.
  2. Отложенные события изменения состояния - включают смену выбора в элементе управления или изменение текста в текстовом поле, но обнаруживаемые ASP.NET только по прибытию очередной обратной отсылки.

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

Например, пусть у нас есть текстовое поле без автоматической обратной отсылки и кнопка Submit. Мы изменяем текстовое поле и щелкаем на кнопке Submit, требуя от броузера обратную отсылку. Запрос поступает на сервер, анализируется средой исполнения ASP.NET, которая запускает следующие события в таком порядке:

  1. Page.Init
  2. Page.Load
  3. TextBox.TextChanged
  4. Button.Click
  5. Page.PreRender
  6. Page.Unload

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

Автоматическая привязка данных

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

Существуют два типа операций с источниками данных. Любые изменения (вставка, удаление, изменение записей БД) выполняются после обработки всех событий элементов управления, но непосредственно перед генерацией события Page.PreRender. После запуска события Page.PreRender наступает черед элементов управления источниками данных, которые выполняют свои запросы и вставляют извлеченные данные в связанные с этими данными элементы управления. При этом ни один обработчик событий элементов отображения уже не будет иметь доступа к новым данным.

Это последняя остановка в жизненном цикле страницы. Исторически сложилось так, что событие Page.PreRender обозначает последнее действие перед преобразованием страницы в HTML.

Очистка

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

Пример страничного потока событий

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

Добавление к сайту новой страницы

  • Выполните опцию меню оболочки Website/Add New Item... или такую же опцию в контекстном меню узла сайта панели Solution Explorer
  • Заполните окно мастера добавления как показано на снимке. Имя странице присвойте PageFlow.aspx, включите галочку Place code in separate file (размещать код в отдельном файле), шаблон страницы выберите Web Form. Щелкните на кнопке Add
  • Выполните команду Website/Set As Start Page (сделать страницу стартовой), чтобы при запуске проекта из оболочки она выполнялась первой. Такая установка применяется только в среде разработки и не влияет на порядок загрузки страниц на промышленном сервере.
  • Поместите на страницу из вкладки Standard панели Toolbox серверный элемент управления Label и Button. Отцентрируйте их в режиме Design командой Format/Justify/Center
  • Переименуйте элемент Label1 в lblInfo
  • Через панель Document Outline в режиме Design выделите объект lblInfo и в панели Properties установите свойство EnableViewState в состояние false

Отметьте для себя, что установленные отличными по умолчанию значения свойств элементов управления выделяются в поле значений панели Properties полужирным стилем шрифта.

  • Очистите значение свойства Text элемента lblInfo
  • Двойным щелчком на странице в режиме Design создайте заготовку обработчика Page_Load() и растиражируйте ее редактором кода еще на три обработчика
  • Двойным щелчком на кнопке Button1 в режиме Design создайте для нее заготовку обработчика Button1_Click()
  • Заполните обработчики следующим кодом
    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 PageFlow : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // Объекты созданы и инициализированы начальными значениями
            // и возвращенным состоянием вида
            lblInfo.Text += "Сработал обработчик события Load <br />";
    	
            if (Page.IsPostBack)
            {
                lblInfo.Text += "<b>Эта страница запрашивается не впервые</b><br />";
            }
        }
    	
        protected void Page_Init(object sender, EventArgs e)
        {
            lblInfo.Text += "Сработал обработчик события Init <br />";       
        }
    	
        protected void Page_PreRender(object sender, EventArgs e)
        {
            lblInfo.Text += "Сработал обработчик события PreRender <br /><hr />";       
        }
    	
        protected void Page_Unload(object sender, EventArgs e)
        {
            // Не увидим, HTML уже сгенерирован
            lblInfo.Text += "Сработал обработчик события Unload <br />";       
        }
    	
        protected void Button1_Click(object sender, EventArgs e)
        {
            lblInfo.Text += "Сработал обработчик события Click кнопки <br />";       
        }
    }
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="PageFlow.aspx.cs" Inherits="PageFlow" %>
    	
    <!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 runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div style="text-align: center">
                <asp:Label ID="lblInfo" runat="server" EnableViewState="False"></asp:Label>
                <br />
                <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
            </div>
        </form>
    </body>
    </html>
  • Запустите приложение первый раз. Сервер сгенерирует следующий HTML-ответ
    Ответ сервера при первом запросе

    Ответ сервера при первом запросе
  • Щелкните на кнопке Submit (Button), чтобы инициировать обратную отсылку. На эту и все последующие обратные отсылки сервер будет присылать такой HTML-ответ
    Ответ сервера на обратную отсылку

    Ответ сервера на обратную отсылку

Обратите внимание на файл поддержки PageFlow.aspx.cs. Он не полон - в производном классе страницы нет объявлений объектов lblInfo и Button1 элементов управления. Такое в языке C# недопустимо. Однако страница нормально компилируется и успешно выполняется. Дело все в ключевом слове partial (частичный) в объявлении класса. Это нововведение в Visual Studio 2005. Оболочка сама перед компиляцией файла добавляет необходимые объявления объектов на основании информации о дескрипторах, связанных с серверными элементами на основной странице .aspx. Это справедливо, поскольку на странице со смешанным кодом и раньше не было никаких объявлений (как и не было классов).

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

И еще одно замечание. Если бы мы не установили свойство EnableViewState метки lblInfo в значение false, то получился бы совсем другой результат. Текстовое значение метки, сохраняемое в состоянии вида, дополнялось бы новыми изменениями элемента при каждом следующем ответе. Мы с вами говорили, что если состояние вида не выключено (включено по умолчанию), то любые изменения состояния элемента управления немедленно в нем сохраняются.

Илья Онучин
Илья Онучин
Россия
Igor Chelyadinski
Igor Chelyadinski
Беларусь, Минск, №54, 2013