Архитектура ASP.NET 2.0
Дата :25 августа 2006
Постаравшись разобраться с внутренней инфраструктурой ASP.NET версии 2.0 можно обнаружить, что в ней нет никаких глобальных отличий от инфраструктуры версии 1.х. В жизненный цикл страницы добавились новые события, разработчик получил возможность использовать множество новых объектов и элементов управления, но общие принципы обработки HTTP остались те же, поскольку вероятность придумать что-либо более подходящее под концепцию ASP.NET представляется весьма небольшой.
В этой статье будут рассмотрены основные усовершенствования в модели компиляции ASP.NET страниц, разделение кода и представления и взаимодействие среды выполнения ASP.NET с веб-сервером IIS.
Среда выполнения ASP.NET и веб-сервер IIS
Параллельное использование с предыдущими версиями ASP.NET
Поскольку в ASP.NET 2.0 присутствуют изменения, которые могут стать фатальными для использующих предыдущие версии ASP.NET веб-приложений, разработчики и IIS предусмотрели возможность использовать разные версии ASP.NET для разных приложений на одном сервере.
В консоли управления веб-сервером IIS можно выбрать версию ASP.NET, которая будет использована для каждого приложения, а также установить версию ASP.NET, которая будет использоваться вновь создаваемыми приложениями.
Рис. 1. Конфигурирование версии ASP.NET
Эта возможность позволяет продолжить работу с веб-приложениями, созданными с использованием предыдущих версий ASP.NET.
Ручная установка ASP.NET
Для ручной установки, которая может понадобиться, если .NET Framework был установлен до установки веб-сервера IIS или по какой-либо другой причине, используется утилита командной строки aspnet_regiis.exe, которую можно найти в директории %WINDIR%\Microsoft.NET\Framework\v2.0.50727\.
Ключ | Описание |
---|---|
-i | Полная установка. Регистрирует ASP.NET 2.0 для сервера IIS и обновляет регистрацию для всех веб-приложений. |
-ir | Только регистрирует ASP.NET 2.0 для сервера IIS. Веб-приложения продолжают использовать ранее установленную версию ASP.NET. |
-s <путь> | Обновляет регистрацию для приложений, находящихся в директории <путь> и всех вложенных директориях. |
-sn <путь> | Обновляет регистрацию для приложений, находящихся только в директории <путь>. Не затрагивает регистрацию приложений, находящихся во вложенных директориях. |
-lv | Показывает список всех установленных версий ASP.NET. |
-lk | Показывает список всех приложений и зарегистрированную для них версию ASP.NET. |
Взаимодействие с IIS
Следует отметить, что веб-серверы IIS 5 и IIS 6 по-разному взаимодействуют с ASP.NET 2.0, поскольку для этих двух версий IIS используются разные модели обработки запросов.
При использовании IIS 5, среда выполнения ASP.NET представлена отдельным процессом aspnet_wp.exe. Этот процесс получает управление от IIS с помощью ASP.NET ISAPI расширения aspnet_isapi.dll. Если расширение запрошенного ресурса связано с ASP.NET ISAPI расширением, то запрос поступает на обработку рабочим процессом ASP.NET, который, в свою очередь, отвечает за загрузку CLR, создание управляемых объектов и выстраивания очереди событий для ASP.NET страницы. В этом случае особенно важно отметить, что рабочий процесс aspnet_wp.exe обслуживает все веб-приложения: каждое приложение выполняется в отдельном домене приложения AppDomain в рамках одного рабочего процесса. Рабочий процесс выполняется под специальной учетной записью ASPNET.
Процесс обработки запроса при использовании IIS 5 может быть разбит на следующие шаги:
- IIS получает запрос, определяет тип ресурса и, если данный тип связан с ASP.NET, передает его на обработку расширению aspnet_isapi.dll. ISAPI расширение передает запрос на дальнейшую обработку рабочему процессу ASP.NET.
- После получения запроса, рабочий процесс передает сообщение ISAPI расширению, сообщая о том, что запрос будет обработан.
- Запрос выполняется в контексте рабочего процесса ASP.NET.
- После окончания выполнения, рабочий процесс передает данные ISAPI расширению, которое отвечает за освобождение ресурсов, занятых рабочим процессом.
В случае использования IIS 6 модель обработки запросов несколько меняется, поскольку IIS 6 используем модель пула приложений – отдельного рабочего процесса, который обслуживает одно или несколько веб-приложений. Каждый пул приложений обслуживается отдельным экземпляром рабочего процесса w3wp.exe.
IIS 6 использует для получения и обработки запросов драйвер, работающий на уровне ядра операционной системы http.sys, все запросы проходят через этот драйвер, который отвечает за сопоставление запроса соответствующему пулу приложений. Рабочий процесс, обслуживающий пул приложений, загружает необходимые ISAPI расширения. В случае ASP.NET это рашсирение aspnet_isapi.dll, которое в свою очередь загружает CLR и начинает обработку HTTP запроса. Рабочие процессы выполняются под учетной записью NetworkService.
Структура ASP.NET страницы и модель компиляции
Структура страницы ASP.NET 2.0 не отличается от структуры страниц ASP.NET 1.х. Страница по-прежнему разделена на две основные части: директивы и представление. Код программной логики страницы может быть вынесен в отдельный файл (выделенный код, Code Behind), либо включен в представление страницы в качестве блока <script> с установленным атрибутом runat=”server” (встроенный код, inline code)).
Разделение кода программной логики и представления
Рассмотрим оба этих способа размещения кода и различия между подходом к разделению кода в ASP.NET 1.х и 2.0. В ASP.NET 1.x при использовании модели встроенного кода класс страницы ASP.NET наследовал классу System.Web.UI.Page и при выполнении приложения компилировался во временную сборку.
Рис. 2. Модель встроенного кода ASP.NET 1.x
При использовании модели разделения кода и представления в ASP.NET 1.x схема наследования становится более сложной, поскольку классу System.Web.UI.Page наследует класс, определенный в файле программной логики (Code Behind), которому, в свою очередь, наследует класс страницы ASP.NET. При этом класс, определенный в файле программной логики компилируется в единую сборку приложения, находящуюся в директории bin, а класс страницы компилируется во временную сборку.
Рис. 3. Модель выделенного кода ASP.NET 1.x
В ASP.NET 2.0, в связи с появлением возможность разделения классов, общая схема становится несколько иной. Для ASP.NET страницы создается частичный класс страницы (partial class), который объединяется частичным классом, объявленным в файле программной логики, который затем компилируется как единой целое, поэтому в файле с исходным кодом нет декларации элементов управления и создания экземпляров этих элементов. Код, создающий элементы управления, генерируется во время компиляции на основании файла с кодом разметки страницы.
Частичные классы (partial class)
Разделение классов позволяет разбивать код класса на несколько различных частей, исходный код которых может быть размещен в разных файлах. Например, класс User:
public class User { private int mInt; private long mLong; private string mString; private bool mBool; public int MyInt{ get{return this.mInt;} set{this.mInt = value;} } }
Может быть разбит на несколько частей с использованием ключевого слова partial:
public partial class User { private int mInt; private long mLong; private string mString; private bool mBool; } public partial class User { public int MyInt{ get{return this.mInt;} set{this.mInt = value;} } }
Рис. 4. Модель частичных классов ASP.NET 2.0
Если быть откровенным, то дело обстоит не совсем так, как показано на Рис. 4. Помимо класса, «собранного» из двух частей – частичного класса в файле исходного кода и частичного класса, сгенерированного средой выполнения, существует класс страницы, точно также как и в ASP.NET 1.x наследующий классу, определенному в файле исходного текста.
Если посмотреть код сборки, созданной для страницы, содержащей одну кнопку Button1, то можно найти два класса, имеющих отношение к одной странице. Собственно класс, определенный в файле исходного кода:
public class _Default : Page, IRequiresSessionState { static _Default(); public _Default(); protected void Button1_Click(object sender, EventArgs e); protected void Page_Load(object sender, EventArgs e); protected HttpApplication ApplicationInstance { get; } protected DefaultProfile Profile { get; } protected Button Button1; protected HtmlForm form1; public static string Hello; }
И класс страницы, наследующий ему:
[CompilerGlobalScope] public class default_aspx : _Default, IHttpHandler { public default_aspx(); private HtmlHead __BuildControl__control2(); private HtmlTitle __BuildControl__control3(); private Button __BuildControlButton1(); private HtmlForm __BuildControlform1(); private void __BuildControlTree(default_aspx __ctrl); protected override void FrameworkInitialize(); public override int GetTypeHashCode(); public override void ProcessRequest(HttpContext context); private static object __fileDependencies; private static bool __initialized; }
Модель компиляции
Для ASP.NET 1.x существовало две основных стратегии компиляции:
- Перекомпиляция исходного кода приложения, содержащегося в файлах программной логики, при этом создается единая сборка, помещаемая в каталог bin
- Динамическая компиляция приложения во время первого запроса, в этом случае в каталог на сервере помещаются все исходные файлы веб-приложения и полная компиляция осуществляется при поступлении первого запроса к приложению.
В ASP.NET 2.x появились новые дополнительные стратегии, связанные с новой моделью компиляции приложений: для каждой ASP.NET страницы создается своя собственная сборка. Эта модель компиляции открывает возможность не перекомпилировать все приложение при изменении одного файла исходного кода, а осуществлять компиляцию только измененных файлов. Поэтому ASP.NET 2.0 предлагает три основных стратегии компиляции приложений:
- Перекомпиляция файлов программной логики. В этом случае создаются сборки в директории bin, которые во время выполнения объединяются с динамическими сборками, генерируемыми для страниц с использованием механизма разделения классов.
- Полная пре-компиляция. Абсолютно новая возможность, появившаяся в ASP.NET 2.0 и позволяющая создать одну сборку для всех файлов приложения, включая файлы ASPX, содержащие HTML разметку. Сборка помещается в директорию bin веб-приложения, а содержимое всех ASPX файлов замещается на стоку «This is a marker file generated by the precompilation tool, and should not be deleted!».
- Динамическая компиляция. Эта стратегия аналогична используемой в ASP.NET стратегии динамической компиляции по запросы, с одним исключением, что страницы компилируются не одновременно, а по мере поступления запросов к каждой конкретной странице.
Прекомпиляция веб-приложения
Для осуществления пре-компиляции веб-приложения перед публикацией на веб-сервере можно использовать утилиту командной строки aspnet_compiler.exe, находящейся в директории %WINDIR%\Microsoft.NET\Framework\v2.0.50727\., либо графическую утилиту, встроенную в Visual Studio 2005 (утилита пре-компиляции не включена в Express версию Visual Studio 2005).
Ключ | Описание |
-p <path> | Указывает полный физический путь путь к приложению <path>. |
-u | Если этот ключ указан, то разметка в файлах ASPX сохраняется, в противном случае разметка замещается фразой «This is a marker file generated by the precompilation tool, and should not be deleted!». |
-с | Указывает утилите aspnet_compiler.exe на то, что необходимо осуществить полную перекомпиляцию всех файлов, даже не измененных со времени последней компиляции. |
Пример использования aspnet_compiler.exe:
aspnet_compiler.exe -p C:\ASPNET\WebApp c:\wwwroot\WebApp
Использование графической, утилиты, встроенной в Visual Studio 2005 еще проще: чтобы вызвать утилиту пре-компиляции необходимо выбрать пункт Publish в меню Build. Затем указать путь к каталогу, куда должны быть помещены скомпилированные файлы проекта, предназначенные для публикации и выбрать желаемые опции.
Рис. 5. Встроенная утилита пре-компиляции
Преимущества пре-компиляции заключаются выигрыше во времени динамической компиляции на сервере, а также в отсутствии необходимости распространять исходные тексты веб-приложений, что позволяет защитить исходные тексты от умышленного и неумышленного повреждения, а также нарушении авторских прав на исходный код.
Недостатком иногда может быть отсутствие возможности внести изменения в код веб-приложения, уже опубликованного на сервере.
Объектная модель страницы
Как уже было описано выше при обсуждении разделения кода программной логики и представления страницы, вне зависимости от того, используется ли модель разделения кода или нет, класс страницы наследует классу Page, который, в свою очередь, является элементом управления, поскольку наследует классу TemplateControl. А также, класс Page является HTTP обработчиком, поскольку реализует интерфейс IHttpHandler.
public class Page : TemplateControl, IHttpHandler {}
В ASP.NET 2.0 в классе Page появились новые свойства, методы, события и внутренние объекты, которые были добавлены для реализации новых возможностями ASP.NET 2.0, таких как поддержка шаблонов (master pages) и тем оформления, персонализация, встроенные счетчики посещений и асинхронная обработка, о которых будет рассказано в последующих главах этой книги.
Новые свойства класса Page
Свойство |
Описание |
EnableTheming |
Позволяет определить, используются ли темы для станицы или нет. |
IsAsync |
Определяет способ выполнения страницы: синхронный или асинхронный. |
IsCallback |
Указывает, была ли страница вызвана в результате обратного вызова со стороны клиента (Callback). |
IsCrossPagePostBack |
Указывает, была ли страница вызвана в результате отправки данных на сервер другой страницей. |
MasterPageFile |
Позволяет указать файл шаблона (master page) для данной страницы. |
PreviousPage |
Возвращает объект, представляющий страницу с которой был осуществлен вызов данной страницы. |
Title |
Позволяет получить или установить заголовок страницы. |
О свойствах IsCrossPagePostBack и IsCallback будет сказано несколько слов далее в этой главе, при рассмотрении возможности использования клиентских обратных вызовов и отправки данных другой странице.
Новые методы класса Page
Большинство новых методов класса Page наследуются от класса Control, от которого наследуется TemplateControl, поэтому применимы для всех элементов управления ASP.NET.
Метод |
Описание |
EnsureID |
Создает уникальный идентификатор для элемента управления (для страницы практической пользы не имеет). |
Focus |
Устанавливает фокус ввода для элемента управления (для страницы практической пользы не имеет). |
GetValidators |
Возвращает коллекцию влидаторов, относящихся к определенной группе. |
RegisterRequiresControlState |
Устанавливает для переданного в качестве параметра элемента управления необходимость управления состоянием элемента управления (control state). |
SetFocus |
Устанавливает фокус ввода на определенный элемент управления. |
TestDeviceFilter |
Проверяет, является ли текущий браузер, используемый для просмотра страницы, указанным в качестве параметра метода. |
Новые внутренние объекты класса Page
К унаследованным от контекста HTTP запроса объектам Application, Session, Request и Response и глобальным объектам среды выполнения Cache, Trace и User, доступных в объекте страницы, добавился набор объектов, упрощающих работу со страницей.
Объект |
Описание |
ClientScript |
Экземпляр класса ClientScriptManager, предоставляющего набор методов для работы с клиентскими сценариями. |
Header |
Экземпляр объекта HtmlHead, представляющего заголовок HTML страницы. |
Master |
Ссылка на объект, представляющий шаблон, используемый для данной страницы. |
Необходимо отметить, что объект Header доступен только когда в файле разметки в блоке <head> указан атрибут runar=”server”.
<header runat=”server”> <title>Это страница</title> </header>
В этом примере, с помощью свойства Header.Title можно получить заголовок страницы «Это страница».
Объект Master доступен, если для страницы определен шаблон одним из описанных в следующей главе книги, посвященной шаблонам, способом.
Новые события класса Page
Новые события страницы, добавленные в ASP.NET 2.0 позволяют разработчику получить больший контроль над жизненным циклом страницы и управлять внутренними механизмами ASP.NET. Подробное описание событий и порядка выполнения будет рассмотрено в параграфе, посвященном жизненному циклу страницы. Здесь же будет рассмотрены только новые события.
Событие |
Описание |
InitComplete |
Возникает после инициализации объекта страницы. |
LoadComplete |
Происходит после окончания загрузки страницы. |
PreInit |
Первое событие в жизненном цикле страницы. |
PreLoad |
Возникает перед загрузкой информации о состоянии. |
PreRenderComplete |
Происходит после создания всех дочерних элементов управления. |
Новые атрибуты директивы @Page
Поведение страницы ASP.NET определяется директивами, заданными в директиве @Page в файле разметки ASPX, либо установленные непосредственно в коде. Директива @Page в ASP.NET 2.0 унаследовала большинство атрибутов от предыдущих версий ASP.NET. В этом параграфе будут рассмотрены только новые атрибуты ASP.NET 2.0. Для подробного изучения возможностей директивы @Page рекомендуется воспользоваться документацией MSDN.
Атрибут |
Описание |
Async |
Если атрибут установлен в true, то генерируемый для страницы класс будет наследовать IHttpAsyncHandler, если установлен в false, то класс наследует IHttpHandler. |
AsyncTimeout |
Устанавливает временной интервал тайм-аута, используемого для асихронного выполнения страницы. |
CompilationModel |
Атрибут позволяет управлять компиляцией страницы. |
EnableTheming |
Указывает, должна ли страница использовать оформление определенное в теме визуального оформления. |
MasterPageFile |
Путь к файлу шаблона, используемого для данной страницы. |
Theme |
Имя темы визуального оформления, используемой для страницы. |
Об асинхронном выполнении страниц будет рассказано в следующих главах этой книги. Пока же стоит сказать, что установка директивы Async в true позволяет асинхронно выполнять код параллельно с обработкой жизненного цикла страницы.
Директива CompilationModel может принимать одно из трех значений, указанных в таблице.
Значение |
Описание |
Always |
Страница всегда должна быть скомпилирована. |
Auto |
Страница не должна быть скомпилирована, если это возможно. |
Never |
Страница никогда не должна быть скомпилирована. |
Если атрибут CompilationModel установлен в Never, то наличие встроенного программного кода приведет к ошибке.
Жизненный цикл страницы
Жизненный цикл страницы ASP.NET начинается с получения и обработки Web-сервером IIS запроса к данной странице и передачи этого запроса среде выполнения ASP.NET. В момент получения запроса, среда выполнения загружает класс вызываемой страницы, устанавливает свойства класса страницы, выстраивает дерево элементов, заполняет свойства Request и Response и вызывает метод IHttpHandler.ProcessRequest. После этого среда выполнения проверяет, каким образом была вызвана эта страницы и если страница вызвана путем передачи данных с другой страницы, о чем будет рассказано далее, то среда выполнения устанавливает свойство PreviousPage.
Стоит отметить также, что помимо рассмотренных ниже этапов выполнения страницы существуют еще и этапы уровня приложения, не специфичные для страницы.
Этап |
Описание |
Запрос станицы | Запрос страницы осуществляется ядо начала жизненного цикла страницы. Когда пользователь осуществляет запрос, среда выполнения ASP.NET устанавливает, необходимо ли осуществить компиляцию страницы и начать жизненный цикл, либо можно выдать в качестве ответа страницу из кеша, таким образом, не выполняя страницы. |
Начало жизненного цикла | На этом этапе происходит установка свойство Response и Request и свойства UICulture. Также, на этом этапе устанавливается, была ли эта страница запрошена в результате постбэка (отправления данных на сервер) и соответствующим образом устанавливается свойство IsPostBack. |
Инициализация страницы | Ко времени инициализации страницы все дочерние пользовательские элементы управления уже созданы и имеют установленный свойства UniqueID. В это же время к странице применяются темы оформления. Если страница вызвана в результате постбэка, то данные, отправленные на сервер, еще не загружены в свойства элементов управления, на этом этапе. |
Загрузка | Если страница вызвана в результате постбэка, то на этом этапе устанавливаются свойства элементов управления, на основании информации о состоянии (ViewState и ControlState). |
Валидация | Вызывается метод Validate() для всех, находящихся на странице валидаторов. |
Обработка постбэка | Вызываются обработчики событий (при условии, что постбэк произошел). |
Рендеринг | Сохраняется информация о состоянии, затем класс страницы вызывает соответствующие методы дочерних элементов управления для генерации HTML представления и передачи его в Response.OutputStream. |
Выгрузка | Выгрузка происходит после того, как создано HTML представление для всей страницы. |
Во время прохождения этапов жизненного цикла возникают события, подписавшись на которые, разработчик может выполнять свой собственный код. Стоит упомянуть атрибут AutoEventWireup, директивы @Page: если этот атрибут установлен в true (значение по умолчанию), то методы класса страницы, названные Page_НазваниеСобытия, автоматически становятся обработчиками соответствующих событий жизненного цикла станицы.
Для того, чтобы проследить жизненный цикл страницы и последовательность возникновения событий, можно установить атрибут Trace директивы @Page в true, а атрибут TraceMode в "SortByTime". Тогда в разделе Trace Information можно найти список произошедших событий (колонка Message). Например:
Trace Information |
|||
Category |
Message |
From First(s) |
From Last(s) |
aspx.page | Begin PreInit | ||
aspx.page | End PreInit | 0.0364973314167865 | 0.036497 |
aspx.page | Begin Init | 0.0379050459346291 | 0.001408 |
aspx.page | End Init | 0.047693704143491 | 0.009789 |
aspx.page | Begin InitComplete | 0.0477864508468221 | 0.000093 |
aspx.page | End InitComplete | 0.0481875670270608 | 0.000401 |
aspx.page | Begin PreLoad | 0.0489879732516718 | 0.000800 |
aspx.page | End PreLoad | 0.0494462283607275 | 0.000458 |
aspx.page | Begin Load | 0.0494924892194238 | 0.000046 |
aspx.page | End Load | 0.0553441897381414 | 0.005852 |
aspx.page | Begin LoadComplete | 0.0554711043059809 | 0.000127 |
aspx.page | End LoadComplete | 0.055942153615399 | 0.000471 |
aspx.page | Begin PreRender | 0.0561455634022874 | 0.000203 |
aspx.page | End PreRender | 0.0618604874695332 | 0.005715 |
aspx.page | Begin PreRenderComplete | 0.06269871008062 | 0.000838 |
aspx.page | End PreRenderComplete | 0.0633259746265858 | 0.000627 |
aspx.page | Begin SaveState | 0.080360541216174 | 0.017035 |
aspx.page | End SaveState | 0.213795377788888 | 0.133435 |
aspx.page | Begin SaveStateComplete | 0.213911298043872 | 0.000116 |
aspx.page | End SaveStateComplete | 0.214385763389788 | 0.000474 |
aspx.page | Begin Render | 0.214440078745078 | 0.000054 |
aspx.page | End Render | 0.315044337228923 | 0.100604 |
Из всех событий жизненного цикла страницы, разработчик может подписаться только на пять, помимо событий дочерних элементов управления. Эти события: PreInit, Init, Load, PreRender, Unload. Рассмотрим варианты использования этих событий.
Событие |
Использование |
PreInit | Во время этого события можно использовать свойство IsPostBack, для того, чтобы определить вызвана ли эта страница в первый раз или в результате постбэка. В плане управления страницей разработчик может: создавать динамически элементы управления, динамически устанавливать шаблон дизайна или тему оформления, считывать или устанавливать свойства объекта Profile.
Стоит особо отметить, что на данном этапе, если страница была вызвана в результате постбэка, свойства элементов управления еще не установлены. В случае, если разработчик самостоятельно установит свойства на этом этапе, на следующем установленный значения могут быть изменены. |
Init | На этом этапе разработчик может считывать или инициализировать свойства элементов управления. |
Load | На этом этапе разработчик может считывать или изменять свойства элементов управления. |
PreRender | Последняя возможность внести изменения во внешний вид страницы. |
Unload | Освобождение занятых ресурсов (закрытие открытых соединений с базой данных, завершение работы с файлами и т.п.)
Важно, что на этом этапе уже создано HTML представление страницы и попытка внести какие-либо изменения (например, вызвав метод Response.Write()), приведет к исключению. |
Новые возможности страницы ASP.NET
Настало время вернуться к упомянутым выше возможностям передачи данных формы другой странице и использовании клиентских обратных вызовов для обновления данных страницы без полного цикла обращения к серверу и перезагрузки всей странице в браузере клиента.
Передача данных формы другой странице
Существенное ограничением серверных форм в ASP.NET 1.x – отсутствие возможности непосредственно передавать данные, введенные в форме, другой странице. Чтобы отправить значения элементов форму другой странице необходимо использовать простою HTML форму и в атрибуте action указать путь к странице, которая должна получить данные, либо создать свой собственный класс страницы, наследник класса Page, в котором переопределить метод Render так, чтобы получить возможность замещать атрибут action на какой-либо другой.
В ASP.NET 2.0 тег элемента управления, вызывающего отправление данных на сервер, может иметь дополнительный атрибут PostBackUrl, позволяющий указать какой странице система должна передать Web-форму.
Рис. 6. Передача данных на сервер
<form id="frmTest" runat="server"> <asp:textbox id="txtFirstName" runat="server" /><br /> <asp:textbox id="txtLastName" runat="server" /><br /> <asp:button id="btnSend" Text="Отправить данные" PostBackUrl="crosspost.aspx" runat="server" /> </form>
После щелчка по кнопке, браузер пользователя будет переадресован на страницу crosspost.aspx, при этом вся информация об элементах управления формы, с которой произошло отправление данных, будет также передано.
Чтобы реализовать эту возможность, среда ASP.NET 2.0 осуществляет проверку ASPX страниц на предмет наличия элементов управления с заданным атрибутом PostBackUrl и, при наличии таковых, создает на странице дополнительное скрытое поле __PREVIOUSPAGE, которое и содержит информацию о состоянии элементов формы. Эта информация доступна странице-получателю через свойство PreviousPage.
void Page_Load(object sender, EventArgs e) { if (PreviousPage != null) { // Получаем объекты отправленной формы TextBox txtFirstName = (TextBox)PreviousPage.FindControl("txtFirstName"); TextBox txtLastName = (TextBox)PreviousPage.FindControl("txtLastName"); // Используем данные txtInfo.Text = "Добрый день, " + txtFirstName.Text + "!"; } }
На уровне HTML кода, отправка данных другой форме выглядит следующим образом.
<form method="post" action="GetValues.aspx" id="frmTest"> <input type="submit" name="btnSend" value="Post Data" onclick="javascript:WebForm_DoPostBackWithOptions( new WebForm_PostBackOptions("btnSend", "", false, "", "CrossPage.aspx", false, false))" id="btnSend" /> </form>
Из чего можно заключить, что данные формы не направляются из браузера непосредственно странице CrossPage.aspx, а предварительно направляются все той же странице GetValues.aspx, которая содержит форму frmTest. Это необходимо для поддержки серверной проверки (validation) введенных данных с использованием элементов управления RequiredFieldValidation, RangeValidation и других.
Клиентские обратные вызовы
В ASP.NET 2.0 стало возможным обновлять данные на странице без отправки страницы на сервер и ее полного обновления. Это стало возможно благодаря появлению клиентских сценариев с обратным вызовом (callback scripts). После того, как некоторое событие вызывает запрос к серверу, обновленные данные передаются непосредственно в клиентский сценарий в качестве аргументов функции.
Подобный способ обновления данных на странице удобно и выгодно применять, когда для обновления всей информации требуется много ресурсов и достаточно длительное время, при этом эти часть данных обновляется часто, а остальные данные статичны. Тогда время на обновления данных, например, одного пользовательского элемента, учитывая время запроса к серверу, обработки и получения ответа, будет существенно ниже, чем время обновления всей страницы.
Допустим, существует страница, на которой находится выпадающий список. Когда пользователь выбирает некоторое значение из списка, в некоторый элемент управления загружается значение, логически связанное с выбранным из списка значением. При этом задача такова, что нельзя хранить все данные на стороне клиента (возможно данных слишком много и страница будет долго передаваться по сети, либо данные генерируются на сервере не только в зависимости от выбранного пользователем элемента выпадающего списка). В ASP.NET 1.x для решения этой задачи необходимо привязать к событию изменения значения в выпадающем списке серверный метод. При этом список должен вызывать отправку страницы на сервер при каждом изменении значения (AutoPostBack="True").
protected void ddStatic_SelectedIndexChanged(object sender, EventArgs e) { // На основании значения ddStatic.Items[ddStatic.SelectedIndex].Value // метод устанавливает свойства дригих элементов управления }
В ASP.NET 2.0, как уже было сказано выше, существует возможность не обновлять всю страницу целиком. В данном случае разумно обновить только необходимые данные, поскольку обновлять всю страницу только для того, чтобы установить одно значение слишком расточительно.
Для реализации механизма обновления данных без перезагрузки страницы необходимо создать клиентскую функцию обратного вызова, принимающую переданные с сервера параметры, серверную функцию, принимающую параметры от клиента и возвращающую клиенту значения на основании полученных параметров и связать эти две функции. Мало того, механизм обратного вызова в ASP.NET 2.0 позволяет возвращать результат асинхронно. Для этого в интерфейсе ICallbackEventHandler определены два метода: RaiseCallbackEvent, для получения параметров на сервере и GetCallbackResult для возвращения результата клиенту.
Для реализации функциональности предыдущего примера в ASPX файле помещается следующий код.
<script> function UpdateText(result, context) { dSpan.innerText = result; } </script> <asp:DropDownList ID="ddDynamic" runat="server"> </asp:DropDownList><br /> <span id="dSpan" style="font-weight: bold;"></span>
Класс страницы, использующей функции с обратным вызовом должен реализовывать интерфейс ICallbackEventHandler.
public partial class ScriptCallback_aspx : System.Web.UI.Page, System.Web.UI.ICallbackEventHandler { }
Сигнатуры функций, поддерживающие обратный вызов выглядят следующим образом:
public virtual void RaiseCallbackEvent(string Аргументы) public virtual string GetCallbackResult() private string EventArgument = ""; public void RaiseCallbackEvent (string eventArgument) { EventArgument = eventArgument; } public string GetCallbackResult() { return EventArgument; // значение типа string }
Последним шагом к поставленной цели является связывание серверной и клиентских функций.
protected void Page_Load(object sender, EventArgs e) { // создаем ссылку на функцию обратного вызова string callbackFunction = Page.ClientScript.GetCallbackEventReference ( this, "document.all['ddDynamic'].value", "UpdateText", "null" ); // Привязываем сценарий к событию изменения значения выпадающего // списка ddDynamic.Attributes["onchange"] = String.Format("javascript:{0}", callbackFunction); }
Метод GetCallbackEventReference объекта ClientScriptManager принимает в качестве параметров ссылку на объект страницы, строку, указывающую на значение, которое необходимо передавать на сервер при обратном вызове, имя метода на стороне клиента и принимающего ответ сервера. Подробное описание можно получить в документации MSDN или с помощью инструмента Visual Studio - Object Browser.
Используя эту технологию можно создавать сложные методы обновления данных страницы и позволяющие получить значительный выигрыш в производительности, для этого достаточно разобраться в том, какой код генерирует среда выполнения ASP.NET, реализуя эту функциональность.
// Список, вызывающий перезагрузку страницы <select name="ddStatic" onchange="javascript:setTimeout('__doPostBack(\'ddStatic\',\'\')', 0)" id="ddStatic"> </select> // Список вызывающий метод с обратным вызовом <select name="ddDynamic" id="ddDynamic" onchange="javascript:WebForm_DoCallback('__Page',document.all['ddDynamic'].value,UpdateText,null,null,false)">Стандартная функция doPostBack весьма проста и служит для сохранения данных о событии в скрытые поля формы.
var theForm = document.forms['frmCallBack']; function __doPostBack(eventTarget, eventArgument) { if (theForm.onsubmit == null || theForm.onsubmit()) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } }
При использовании функций с обратным вызовом механизм значительно сложнее. Код функции WebForm_DoCallback гораздо больше, чем doPostBack, поскольку эта функция определяет тип объектной модели браузера, после чего загружает необходимый для передачи данных с модуль. Например, для браузера поддерживающего DOM это будет Microsoft.XMLHTTP.
Посмотреть код различных клиентских функций, используемых средой ASP.NET 2.0 можно сохранив страницу, поддерживающую функции с обратным вызовом на жесткий диск и открыв в текстовом редакторе файл WebResource.axd, ссылка на который имеется в HTML странице.
Заключение
В этой статье было рассказано о новых элементах, привнесенных в архитектуру ASP.NET 2.0 по сравнению с ASP.NET 1.x.