Файл Global.asax, объекты приложения и сеанса
Еще одним глобальным понятием является обработка событий уровня приложения. Это события вроде: "на одной из страниц приложения началась обработка запроса" или "на какой-то странице произошла ошибка". Такие события обрабатываются в коде файла global.asax.
Добавьте в проект новый файл типа Global Application Class. Это можно сделать через меню Website, или щелкнув правой кнопкой мыши на названии проекта в Visual Studio, или через меню File —> New в WebMatrix.
увеличить изображение
Рис. 15.1.
Файл Global.asax — это текстовый файл, который хранится в корневой папке приложения ASP .NET. В проекте может быть только один Global.asax. Он содержит объекты, события, переменные уровня приложения. При создании файла в нем находятся функции, которые должны выполняться при наступлении любого из четырех событий, описанных в таблице.
Файл Web.config
ASP .NET конфигурируется с помощью нескольких глобальных файлов .config, которые находятся в директории .NET Framework. Это файлы формата XML, которые позволяют легко изменить поведение ASP .NET. Например, там находятся machine.config и machine.config.comments, в которых содержатся настройки сервера.
Например, в узле DbProviderFactories файла machine.config находится список провайдеров баз данных. Эти провайдеры появляются в диалоге Add Connection при добавлении новых соединений с источниками данных. В список можно добавлять новых провайдеров:
<DbProviderFactories> <add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add name="SQL Server CE Data Provider" invariant="Microsoft.SqlServerCe.Client" description=".NET Framework Data Provider for Microsoft SQL Server 2005 Mobile Edition" type="Microsoft.SqlServerCe.Client.SqlCeClientFactory, Microsoft.SqlServerCe.Client, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" /> </DbProviderFactories>
В файле machine. config содержится информация, необходимая для работы служб персонализации и управления ролями. Профили пользователей хранятся в aspnetdb.mdf локального сервера SQL Express:
<connectionStrings> <add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" /> </connectionStrings>
Каждая страница ASP .NET по умолчанию может обращаться к некоторым пространствам имен. Список этих пространств имен определен в конфигурационном файле <windir>\Microsoft.NET\Framework\<version>\CONFIG\web.config:
<pages> <namespaces> <add namespace="System" /> <add namespace="System.Collections" /> <add namespace="System.Collections.Specialized" /> <add namespace="System.Configuration" /> <add namespace="System.Text" /> <add namespace="System.Text.RegularExpressions" /> <add namespace="System.Web" /> <add namespace="System.Web.Caching" /> <add namespace="System.Web.SessionState" /> <add namespace="System.Web.Security" /> <add namespace="System.Web.Profile" /> <add namespace="System.Web.UI" /> <add namespace="System.Web.UI.WebControls" /> <add namespace="System.Web.UI.WebControls.WebParts" /> <add namespace="System.Web.UI.HtmlControls" /> </namespaces>
Вы можете изменять этот список, например добавить те пространства имен, которые используете во всех проектах. Но будьте осторожны, потому что в разных пространствах имен определены классы с одинаковыми именами. Например, класс Label определен в System.Windows.Forms и в System.Web.UI.WebControls, но в ASP .NET нужен только второй. Использование класса Label, когда включены оба заголовка, приведет к ошибке компиляции.
Классы находятся в сборках (assembly), и если вы хотите использовать нестандартную сборку, в проекте нужно создать ссылку на нее. Например, при работе с базой данных Oracle включается сборка System.Data.OracleClient. Чтобы создать ссылку, выберите в меню Website пункт Add Reference и выберите нужный компонент. В файл web.config проекта добавится пункт
<assemblies> <add assembly="System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> </assemblies>
Какие сборки включаются по умолчанию, также определено в конфигурационном файле .NET Framework web.config:
<compilation> <add assembly="mscorlib" /> <add assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add assembly="System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add assembly="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add assembly="System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add assembly="System.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add assembly="System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <add assembly="System.Drawing, Version=2.0.0.0, Culture=neu- tral, PublicKeyToken=b03f5f7f11d50a3a" /> <add assembly="System.EnterpriseServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add assembly="System.Web.Mobile, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add assembly="*" /> </assemblies>
В этот список можно добавить те сборки, которые используются во всех ваших проектах.
Глобальные настройки применяются иерархически, то есть ко всем приложениям сайта, если какая-то настройка не переопределена в локальном файле Web.config. Web.config имеет XML-формат и находится в корневой директории сайта. В поддиректориях проекта могут находиться свои конфигурационные файлы, действие которых распространяется на данную директорию.
В этот список можно добавить те сборки, которые используются во всех ваших проектах.
Глобальные настройки применяются иерархически, то есть ко всем приложениям сайта, если какая-то настройка не переопределена в локальном файле Web.config. Web.config имеет XML-формат и находится в корневой директории сайта. В поддиректориях проекта могут находиться свои конфигурационные файлы, действие которых распространяется на данную директорию.
Корневой узел Web.config называется configuration:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
В нем содержится информация трех видов:
1. Настройки приложения, доступные во время разработки.
Они находятся в узле appSettings:
<configuration> <appSettings> <add key="SalesTax" value="0.08" /> </appSettings>
При этом получить доступ к настройкам из приложения можно с помощью свойства AppSettings:
ConfigurationManager.AppSettings["SalesTax"];
2. Строки соединения с источниками данных. Примеры были приведены в лекции 7.
Доступ к коллекции строк соединения происходит с помощью свойства ConnectionStrings:
using System.Configuration;
ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings["NorthwindConnectionString" ];
ConnectionStringSettings наследует Класс ConfigurationElement.
3. Установки System.Web и System.Net
В секции System.Net хранятся установки почтового сервера, если он есть.
Настройки System.Web состоят из нескольких категорий:
Установка глобальных тем.
<pages styleSheetTheme="White"/> Способ аутентификации.
<authentication mode="Forms"> <forms loginUrl="Default.aspx" protection="Validation" time- out="300"/> </authentication> Включена ли анонимная аутентификация.Настройки глобализации:
<globalization requestEncoding="utf-8" responseEncoding="utf-8"/> Подключение менеджера ролей:
<roleManager enabled="true"/> Параметры, хранящиеся в профилях пользователей. Модули HTTP, которые обрабатывают выходной поток сервера.Провайдер карты сайта.Настройки обработки ошибок:
<customErrors mode="RemoteOnly"> <error statusCode="404" redirect="missingPage.aspx"/> </customErrors>
Здесь задается страница, на которую перенаправляется пользователь, когда запрашиваемый им файл не найден.
Атрибут mode="RemoteOnly" определяет, что эту страницу увидят только пользователи, а администратор сайта увидит стандартное сообщение об ошибке.
Настройки компилятора находятся в узле compilation. Во время разработки обычно включается отладка:
<compilation debug="true"/>
Если при работе страницы возникает ошибка, система генерирует страницу с сообщениями об ошибках и показывает ее в браузере. После окончания отладки проекта эту опцию необходимо отключить, так как возможность отладки замедляет работу сервера.
Это лишь некоторые из доступных настроек.В файле <windir>\ Microsoft.NET\Framework\<version>\CONFIG \web.config.comments находится подробное описание всех возможных настроек web.config.
Изменять настройки web.config можно двумя способами. Первый — вручную редактировать его текст в редакторе. И второй — с помощью web-интерфейса, который работает на локальном сервере. Для этого из меню Website выберите пункт ASP .NET Configuration или нажмите на крайнюю справа кнопку в окне Solution Explorer.
События для файла Global.asax
Событие | Условия наступления |
Application_Start | Первая страница приложения открывается любым пользователем |
Application_End | Работа приложения завершается |
Session_Start | Посетитель активизирует приложение |
Session_End | Пользователь покидает приложение или не запрашивает страницу в течение некоторого периода времени |
Application_Error | При выполнении приложения возникает необработанная ошибка |
Данные события предназначены для выполнения кода или создания переменных, которые существуют до тех пор, пока существует программа или пока посетитель пребывает на узле. За счет этого можно сохранять регистрационную информацию, неизменные данные для web-узла или постоянные соединения с источниками данных:
<%@ Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e) { // Code that runs on application startup
}
void Application_End(object sender, EventArgs e) { // Code that runs on application shutdown
}
void Application_Error(object sender, EventArgs e) { // Code that runs when an unhandled error occurs
}
void Session_Start(object sender, EventArgs e) { // Code that runs when a new session is started
}
void Session_End(object sender, EventArgs e) { // Code that runs when a session ends. // Note: The Session_End event is raised only when the sessionstate mode // is set to InProc in the Web.config file. If session mode is set to StateServer // or SQLServer, the event is not raised.
}
</script>
В приложении Starter Kit, например в Application_Start, создаются роли администратора и друга.
ViewState
В лекции 3 упоминалось о скрытом поле ViewState, которое обычно есть у сгенерированных ASP .NET страниц. Говорилось, что там хранится информация о состоянии отображения страницы, которая передается на сервер и обратно, чтобы после перезагрузки на ней сохранялись ее динамические изменения. Там не хранятся введенные пользователем данные, они отправляются, как обычно, в теле запроса POST.
Понимание, что такое состояние отображения и как оно работает, очень важно, особенно для разработчиков, которые пишут собственные элементы управления.
В HTML было введено скрытое поле <input type=hidden> именно для того, чтобы передавать нужную для программиста, но не нужную или не интересную для пользователя информацию на сервер. ASP .NET пользуется этими полями, при этом шифруя информацию (хотя опытный пользователь сможет ее расшифровать). Состояние отображения хранится в скрытом поле формы с идентификатором __VIEWSTATE.
Каким же образом генерируется это поле, что в нем хранится и когда оно читается при возврате формы? Чтобы это понять, надо более подробно рассмотреть жизненный цикл страницы. Каждый раз, когда на сервере генерируется страница, в первый раз или после постбэка, заново создается объект Page и все его элементы. Если свойство элемента управления декларировано на странице или меняется во время события инициализации, оно будет таким всегда.
Рис. 15.2.
Из картинки видно, что между событиями Init и Load при постбэке происходят еще два события: загрузка состояния отображения и загрузка данных постбэка.
И те, и другие читаются из тела запроса POST. Перед событием Render состояние отображения вновь записывается с помощью функции SaveViewState. Функция SaveViewState объекта рекурсивно вызывает ее же вложенных в нее элементов. При этом эта информация кодируется по алгоритму base-64. Во время отрисовки страницы создается скрытый элемент __VIEWSTATE. Следовательно, во время события Load или в обработчиках событий элементов управления можно изменять свойства страницы или любых ее элементов.
Свойство ViewState есть как у страницы, так и у всех элементов управления на ней.
Пусть у нас есть простая форма:
<form id="form1" runat="server"> <asp:Label ID="Label1" runat="server" ></asp:Label><br /> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Get Time" /><br /> <asp:Button ID="Button2" runat="server" Text="Button" /> </form>
Во время первой загрузки страницы ничего связанного с сохранением состояния не происходит.
При нажатии на первую кнопку динамически меняется текст метки. Кнопка вызывает отправку данных на сервер, и во время второго жизненного цикла выполняется функция RaisePostBackEvent, которая вызывает обработчик:
protected void Button1_Click(object sender, EventArgs e) { Label1.Text = DateTime.Now.ToLongTimeString(); }
Измененное состояние метки сохраняется в свойстве ViewState. Если теперь нажать на вторую кнопку, то сразу после инициализации страницы в текст метки будет загружаться текст, сохраненный в ViewState.
Если отключить возможность сохранения состояния, то текст метки вернется к своему пустому состоянию. То же самое произойдет и при нажатии на первую кнопку, но так как RaisePostBackEvent происходит после LoadPostBackData, значение, загруженное из ViewState, переписывается новым значением.
Состояние страницы доступно программисту, и в него можно добавлять дополнительные данные. Например, когда нужно было сортировать DataGrid, в переменную ViewState["sort"] записывалось выражение сортировки.
ViewState представляет собой коллекцию пар "ключ/значение", подобно объектам приложения и сессии. В него можно помещать только объекты с атрибутом Serializable. Свойство ViewState имеет тип System.Web.UI.StateBag, и синтаксис доступа к данным похож на доступ к значениям Hashtable.
Поле __VIEWSTATE может стать довольно большим (порядка десятков килобайт), например, когда в форме присутствует DataGrid или GridView. Эти элементы управления хранят в состоянии отображения все свое содержимое. И вся эта информация путешествует с клиента на сервер и обратно. Увеличивает это поле и элемент Wizard, и TreeView. В общем, чем больше возможностей и свойств, тем дороже приходится за это платить. Поэтому рассмотрим методы сокращения размера поля __VIEWSTATE. У каждого элемента управления есть свойство EnableViewState, которое по умолчанию равно True. Если установить его в False, то состояние данного элемента управления не будет записано в объект ViewState. Это свойство можно отключить и у всей страницы — с помощью атрибута EnableViewState директивы Page или программно:
Page.EnableViewState = false;
Такой же атрибут есть у директив MasterPage, Control.
Атрибут директивы Page EnableViewStateMac определяет, проводится ли машинный контроль аутентификации (MAC). Это контроль, гарантирующий, что состояние отображения не сфальсифицировано злонамеренными пользователями.
Хотя отключение EnableViewState позволяет уменьшить количество передаваемой информации, не всегда это можно делать безнаказанно. Если на странице есть DataGrid, который заполняется во время события Page_Load при условии if (!Page.IsPostBack), то есть при первой загрузке страницы, то после постбэка он просто исчезнет со страницы. Следовательно, при выключенном EnableViewState каждый раз необходимо читать данные из источника заново. Что выбрать, зависит от ситуации. В одних случаях бывает важнее сэкономить на времени загрузки страницы, в других — на соединении с базой данных.
Web-приложение и сессия, обработка глобальных событий
Понятие приложения только тогда можно применить к проекту, когда все его страницы работают с общей информацией. Когда пользователь регистрируется на сайте, все страницы подстраиваются под его настройки. Например, если приложение — электронный магазин, то выбранные товары помещаются в "корзину", которая "путешествует" вместе с пользователем и позволяет добавлять в нее новые товары.
Однако известно, что протокол HTTP изначально не поддерживает сессии. Обычно сервер посылает страницу в ответ на запрос, и на этом соединение обрывается. Хотя HTTP 1.1 поддерживает режим keep-alive, сервер не в состоянии определить, что запросы идут с одного и того же клиента. В ASP .NET при каждом соединении на сервере создается сессия, идентификатор которой обычно хранится в файле-cookie (или передается в командной строке, если это невозможно).
До сих пор мы рассматривали работу ASP .NET в пределах одной страницы. Нажали кнопку — получили результат. На практике при работе с web-приложениями к цели ведут десятки взаимосвязанных запросов, делающихся на разных страницах. Что объединяет страницы приложения в одно целое?
В ASP .NET есть специальный класс — HttpApplication, представляющий все приложение. Он контролирует его общее состояние и обрабатывает глобальные события.
В основном этот класс используется для хранения объектов уровня приложения. Он предоставляет словарь для хранения объектов, который доступен в любое время с любой страницы.
Также в ASP.NET присутствует другой тип окружения — сессия (объект Session класса HttpSessionState).
Понятие приложения только тогда можно применить к проекту, когда все его страницы работают с общей информацией. Когда пользователь регистрируется на сайте, все страницы подстраиваются под его настройки. Например, если приложение — электронный магазин, то выбранные товары помещаются в "корзину", которая "путешествует" вместе с пользователем и позволяет добавлять в нее новые товары.
Однако известно, что протокол HTTP изначально не поддерживает сессии. Обычно сервер посылает страницу в ответ на запрос, и на этом соединение обрывается. Хотя HTTP 1.1 поддерживает режим keep-alive, сервер не в состоянии определить, что запросы идут с одного и того же клиента. В ASP .NET при каждом соединении на сервере создается сессия, идентификатор которой обычно хранится в файле-cookie (или передается в командной строке, если это невозможно).
До сих пор мы рассматривали работу ASP .NET в пределах одной страницы. Нажали кнопку — получили результат. На практике при работе с web-приложениями к цели ведут десятки взаимосвязанных запросов, делающихся на разных страницах. Что объединяет страницы приложения в одно целое?
В ASP .NET есть специальный класс — HttpApplication, представляющий все приложение. Он контролирует его общее состояние и обрабатывает глобальные события.
В основном этот класс используется для хранения объектов уровня приложения. Он предоставляет словарь для хранения объектов, который доступен в любое время с любой страницы.
Также в ASP.NET присутствует другой тип окружения — сессия (объект Session класса HttpSessionState).
Сессия объединяет серию запросов с одного адреса в течение некоторого времени. В пределах сессии можно контролировать текущего пользователя, так что именно в сессии удобно отслеживать последовательность его действий. В сессии можно хранить данные, полученные из разных источников, которые относятся к пользователю, или даже объекты классов приложения.
Текущая сессия и приложение доступны с любой страницы через свойства Session и Application, так что получить доступ к ним очень просто.
Параметры сессии также доступны в виде словаря через индексатор со строковым параметром. Область видимости переменной сеанса — весь сеанс взаимодействия посетителя с приложением в конкретном окне браузера. Сеансом является период времени, когда пользователь находится на сайте. Он начинается, когда посетитель впервые заходит на сайт. Пользователь может закрыть браузер, и сервер не будет знать об этом. Поэтому в сессии существует таймаут, который по умолчанию равен 20 минутам. Если в течение этого времени пользователь не совершал активных действий на сайте, сессия считается закрытой. Для приложений, где потеря данных критична (например, финансовых), обычно таймаут уменьшают.
С каждой сессией связан 120-битный идентификатор. Он передается от сервера браузеру и обратно через cookie или через командную строку. В приложениях электронной коммерции в переменных сеанса может храниться корзина — то, что уже выбрал пользователь.
Так добавляется переменная сервера:
Session.Add("Username", "Tom"); Session["Username"]= "Tom";
Эти присваивания равносильны.
В сессии могут храниться переменные любых типов, в том числе пользовательские.
Запрос к данным сессии возвращает тип object, поэтому после получения переменной необходимо привести его к нужному типу:
Username= Session["Username"].ToString();
Таймаут страницы можно поменять:
Session.TimeOut = 5;
Явно завершить сеанс позволяет метод Abandon:
Session.Abandon;
Приложение выполняется на сервере. Новое приложение создается, когда хотя бы один пользователь обращается к сайту. Приложение завершается, когда сервер перезагружается или меняется его конфигурация. Следующая строка кода создает переменную приложения с именем Name:
Application["Name"] = "Myname"
Сессия объединяет серию запросов с одного адреса в течение некоторого времени. В пределах сессии можно контролировать текущего пользователя, так что именно в сессии удобно отслеживать последовательность его действий. В сессии можно хранить данные, полученные из разных источников, которые относятся к пользователю, или даже объекты классов приложения.
Текущая сессия и приложение доступны с любой страницы через свойства Session и Application, так что получить доступ к ним очень просто.
Параметры сессии также доступны в виде словаря через индексатор со строковым параметром. Область видимости переменной сеанса — весь сеанс взаимодействия посетителя с приложением в конкретном окне браузера. Сеансом является период времени, когда пользователь находится на сайте. Он начинается, когда посетитель впервые заходит на сайт. Пользователь может закрыть браузер, и сервер не будет знать об этом. Поэтому в сессии существует таймаут, который по умолчанию равен 20 минутам. Если в течение этого времени пользователь не совершал активных действий на сайте, сессия считается закрытой. Для приложений, где потеря данных критична (например, финансовых), обычно таймаут уменьшают.
С каждой сессией связан 120-битный идентификатор. Он передается от сервера браузеру и обратно через cookie или через командную строку. В приложениях электронной коммерции в переменных сеанса может храниться корзина — то, что уже выбрал пользователь.
Так добавляется переменная сервера:
Session.Add("Username", "Tom"); Session["Username"]= "Tom";
Эти присваивания равносильны.
В сессии могут храниться переменные любых типов, в том числе пользовательские.
Запрос к данным сессии возвращает тип object, поэтому после получения переменной необходимо привести его к нужному типу:
Username= Session["Username"].ToString();
Таймаут страницы можно поменять:
Session.TimeOut = 5;
Явно завершить сеанс позволяет метод Abandon:
Session.Abandon;
Приложение выполняется на сервере. Новое приложение создается, когда хотя бы один пользователь обращается к сайту. Приложение завершается, когда сервер перезагружается или меняется его конфигурация. Следующая строка кода создает переменную приложения с именем Name:
Application["Name"] = "Myname"
и глобальные объекты позволяют работать
Конфигурационные файлы, состояние отображения и глобальные объекты позволяют работать с приложением как с единым целым.