Управление жизненным циклом приложения

Жизненный цикл приложения и запроса

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 31.10.2015

Как правло, для решения большинства задач в 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 не вызываются. Подобная конфигурация облегчает обработку запросов и позволяет сохранять в нестатических глобальных переменных запроса те данные, которые являются специфичными для каждого запроса.

Жизненный цикл запроса предполагает вызов ряда событий в следующей последовательности:

  1. BeginRequest: событие возникает, когда приложение получает новый запрос

  2. AuthenticateRequest/PostAuthenticateRequest: событие AuthenticateRequest возникает при идентификации (аутентификации) пользователя, сделавшего запрос. А после его обработки срабатывает событие PostAuthenticateRequest

  3. AuthorizeRequest/PostAuthorizeRequest: AuthorizeRequest возникает при авторизации запроса, после этого срабатывает событие PostAuthorizeRequest

  4. ResolveRequestCache/PostResolveRequestCache: событие ResolveRequestCache возникает, когда приложение обращается к кэшу для получения данных. При получении данных их кэша затем срабатывает событие PostResolveRequestCache

  5. MapRequestHandler/PostMapRequestHandler: MapRequestHandler срабатывает при определении обработчика запроса. После выбора обработчика срабатывает событие PostMapRequestHandler

  6. AquireRequestState/PostAquireRequestState: событие AquireRequestState возникает при получении данных состояния, связанных с запросом (например, данные сессии). И после него срабатывает событие PostAquireRequestState

  7. PreRequestHandlerExecute/PostRequestHandlerExecute: событие PreRequestHandlerExecute происходит непосредственно перед началом работы обработчика запроса, а событие PostRequestHandlerExecute - после его работы

  8. ReleaseRequestState/PostReleaseRequestState: событие ReleaseRequestState возникает, когда приложению больше не требуются данные, ассоциированные с запросом. И после освобождения этих данных просиходит событие PostReleaseRequestState

  9. UpdateRequestCache: возникает при обновлении данных в кэше

  10. LogRequest/PostLogRequest: событие LogRequest происходит непосредственно перед каждым логгированием, а PostLogRequest - после завершения всех обработчиков событий

  11. EndRequest: возникает при завершении обработки запроса, когда данные для ответа уже готовы к отправке клиенту

  12. PreSendRequestHeaders: возникает перед отправкой HTTP-заголовков браузеру клиента

  13. PreSendRequestContent: возникает после отправки заголовков, но перед отправкой основного содержимого ответа

И если на определенной стадии возникнет ошибка, то сработает событие Error.

Схематично жизненный цикл приложения и запроса можно представить следующим образом:

Жизенный цикл запроса в ASP.NET MVC

При необходимости мы можем обработать эти события. Например, в файле 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() добавляется название сработавшего метода.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850