Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Как правло, для решения большинства задач в ASP.NET MVC не требуется управление жизненным циклом приложения. Однако понимание работы приложения и обработки запроса дает нам дополнительные возможности по настройке веб-приложения.
Жизненный цикл приложения ASP.NET MVC начинается с запуска веб-приложения для обработки HTTP-запросов. И когда приложение завершает свою работу, завершается и его жизненный цикл. Ключевую роль в запуске приложения играет файл Global.asax. Он определяет глобальный класс приложения. Например, стандартное содержимое файла Global.asax может выглядеть следующим образом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace LifeCycleApp { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } } }
По умолчанию глобальный класс приложения называется MvcApplication. Он наследуется от класса System.Web.HttpApplication. И при поступлении запроса к приложению фреймворк ASP.NET создает объект класса MvcApplication.
Класс приложения поддерживает два метода, которые позволяют управлять жизненным циклом приложения: Application_Start и
Application_End. Метод Application_Start
вызывается при старте приложения, а метод Application_End
-
перед завершением его работы.
По умолчанию в классе приложения определяется только метод Application_Start, в котором производится конфигурационная настройка: регистрация маршрутов, фильтров и т.д.
Метод Application_End вызывается перед завершением работы приложения и, как правило, используется для освобождения связанных ресурсов, наподобие подключений к базам данных, но сейчас, как правило, освобождение ресурсов можно произвести в коде самого приложения, и поэтому в методе Application_End нет большой необходимости.
При получении запроса приложением опять же создается объект приложения в виде объекта класса HttpApplication, определенного в Global.asax.
Для обработки каждого запроса создается свой объект приложения. И затем начинается жизненный цикл запроса. То есть фактически создание объекта приложения происходит два раза - в рамках жизненного цикла приложения и в рамках жизненного цикла
запроса. У объекта приложения, который создается для обработки конкретного запроса, методы Application_Start
и Application_End
не вызываются.
Подобная конфигурация облегчает обработку запросов и позволяет сохранять в нестатических глобальных переменных запроса те данные, которые являются специфичными для каждого запроса.
Жизненный цикл запроса предполагает вызов ряда событий в следующей последовательности:
BeginRequest: событие возникает, когда приложение получает новый запрос
AuthenticateRequest/PostAuthenticateRequest: событие AuthenticateRequest возникает при идентификации (аутентификации) пользователя, сделавшего запрос. А после его обработки срабатывает событие PostAuthenticateRequest
AuthorizeRequest/PostAuthorizeRequest: AuthorizeRequest возникает при авторизации запроса, после этого срабатывает событие PostAuthorizeRequest
ResolveRequestCache/PostResolveRequestCache: событие ResolveRequestCache возникает, когда приложение обращается к кэшу для получения данных. При получении данных их кэша затем срабатывает событие PostResolveRequestCache
MapRequestHandler/PostMapRequestHandler: MapRequestHandler срабатывает при определении обработчика запроса. После выбора обработчика срабатывает событие PostMapRequestHandler
AquireRequestState/PostAquireRequestState: событие AquireRequestState возникает при получении данных состояния, связанных с запросом (например, данные сессии). И после него срабатывает событие PostAquireRequestState
PreRequestHandlerExecute/PostRequestHandlerExecute: событие PreRequestHandlerExecute происходит непосредственно перед началом работы обработчика запроса, а событие PostRequestHandlerExecute - после его работы
ReleaseRequestState/PostReleaseRequestState: событие ReleaseRequestState возникает, когда приложению больше не требуются данные, ассоциированные с запросом. И после освобождения этих данных просиходит событие PostReleaseRequestState
UpdateRequestCache: возникает при обновлении данных в кэше
LogRequest/PostLogRequest: событие LogRequest происходит непосредственно перед каждым логгированием, а PostLogRequest - после завершения всех обработчиков событий
EndRequest: возникает при завершении обработки запроса, когда данные для ответа уже готовы к отправке клиенту
PreSendRequestHeaders: возникает перед отправкой HTTP-заголовков браузеру клиента
PreSendRequestContent: возникает после отправки заголовков, но перед отправкой основного содержимого ответа
И если на определенной стадии возникнет ошибка, то сработает событие Error
.
Схематично жизненный цикл приложения и запроса можно представить следующим образом:
При необходимости мы можем обработать эти события. Например, в файле Global.asax.
Для этого нам надо определить в классе MvcApplication методы, имеют следующее наименование: Application_[Название_события]. Например, изменим класс в Global.asax следующим образом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace LifeCycleApp { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } // обработка события BeginRequest protected void Application_BeginRequest() { AddEvent("BeginRequest"); } // обработка события AuthenticateRequest protected void Application_AuthenticateRequest() { AddEvent("AuthenticateRequest"); } // обработка события PreRequestHandlerExecute protected void Application_PreRequestHandlerExecute() { AddEvent("PreRequestHandlerExecute"); } private void AddEvent(string name) { List<string> eventList = Application["events"] as List<string>; if (eventList == null) { Application["events"] = eventList = new List<string>(); } eventList.Add(name); } } }
Обработка каждого события сведена в к добавлению информации в методе AddEvent. Для хранения информации о событиях создается список, хранящийся в
свойстве Application["events"]
. Объект Application является экземпляром класса HttpApplicationState
,
который представляет состояние приложение. И таким образом, мы можем определить в нем свойство events.
После этого мы можем обратиться к этому свойству в приложении, например, в контроллере HomeController. Пусть в нем происходит вывод всех отработавших событий на страницу:
public class HomeController : Controller { public string Index() { List<string> events = HttpContext.Application["events"] as List<string>; string result = "<ul>"; foreach (string e in events) result += "<li>" + e + "</li>"; result += "</ul>"; return result; } }
Кроме специальных методов мы также можем обработать события стандартным для C# способом - через подключения обработчиков событий. Для этого изменим класс MvcApplication в Global.asax следующим образом:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace LifeCycleApp { public class MvcApplication : System.Web.HttpApplication { public MvcApplication() { BeginRequest += (src, args) => AddEvent("BeginRequest"); AuthenticateRequest += (src, args) => AddEvent("AuthentucateRequest"); PreRequestHandlerExecute += (src, args) => AddEvent("PreRequestHandlerExecute"); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } private void AddEvent(string name) { List<string> eventList = Application["events"] as List<string>; if (eventList == null) { Application["events"] = eventList = new List<string>(); } eventList.Add(name); } } }
С помощью лямбда-выражений устанавливаются обработчики событий (src, args) => AddEvent("BeginRequest")
. В качестве параметров передаются
объект, вызвавший событие - src
, и параметр EventArgs - args
. Результат выполнения метода в контроллере HomeController будет тем же.
И как и в контроллерах приложения, в Global.asax мы можем использовать контекст запроса HttpContext и его свойства. Например, обработаем событие
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } protected void Application_PostRequestHandlerExecute(object sender, EventArgs e) { HttpContext.Current.Response.Write("Application_PostRequestHandlerExecute"); } }
В данном случае в ответ с помощью метода Response.Write()
добавляется название сработавшего метода.