Миграция устаревшего ASP.NET на MVC 2 (RC): события HttpApplication не запускаются, участник пользователя имеет значение null

Проблема

Я пытаюсь взять существующее веб-приложение ASP.NET и вручную перенести его на использование MVC 2 (в настоящее время я запускаю RC). Я выполнил несколько шагов (сейчас я перечислю) и, похоже, все заработало, но потом я заметил, что не могу установить AuthorizeAttribute, потому что пользователь имеет значение null на контроллере. Затем я заметил, что когда я перехожу к действию, ни одно из обычных событий жизненного цикла HttpApplication не возникает (например, BeginRequest и т. д.). Я полагаю, что это, вероятно, связано с тем, почему принципал пользователя имеет значение null. Веб-хост — это локальный экземпляр IIS7 (на моей рабочей станции Vista).

Я создал совершенно новое веб-приложение MVC 2, чтобы использовать его в качестве эталона при миграции. Он работает просто отлично, вызывая события приложения и заполняя принципала пользователя, как я и ожидал.

Если вы чувствуете, что хотите наказать себя, читайте все подробности (в меру своих возможностей) ниже.


Этапы миграции

  1. Убедитесь, что пул приложений для каталога приложений — это .NET 2.0 и интегрированный
  2. Ссылки на System.Web.Abstractions (v.3.5.0.0), System.Web.routing (v3.5.0.0) и System.Web.Mvc (v2.0.0.0) в моем веб-проекте. Теперь я добавил System.Web.Mvc в качестве локальной ссылки, чтобы упростить интеграцию и развертывание.
  3. Изменен csproj, чтобы включить надстройку MVC VS (см. эту статью)
  4. В проект добавлены каталоги Controllers и Views, добавленные в /Views/web.config из моего примера приложения MVC.
  5. Изменил мой web.config (ПРИМЕЧАНИЕ: у меня есть другая хрень в модулях и обработчиках, но я скрыл это для простоты и безопасности... но это вполне может быть частью проблемы):

Раздел compilation:

<compilation defaultLanguage="c#" debug="true">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
      </assemblies>
    </compilation>

Раздел pages:

<pages enableEventValidation="false"
       pageBaseType="MyAssembly.ThemedBasePage">
  <controls>
    <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  </controls>

  <namespaces>
    <add namespace="System.Web.Mvc"/>
    <add namespace="System.Web.Mvc.Ajax"/>
    <add namespace="System.Web.Mvc.Html"/>
    <add namespace="System.Web.Routing"/>
    <add namespace="System.Linq"/>
    <add namespace="System.Collections.Generic"/>
  </namespaces>
</pages>

The system.webServer/modules section (remember, IIS7 integrated):

<modules>
  <remove name="ScriptModule" />
  <remove name="UrlRoutingModule" />
  <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

</modules>

Раздел system.webServer/handlers:

<handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
      <remove name="ScriptHandlerFactory"/>
      <remove name="ScriptHandlerFactoryAppServices"/>
      <remove name="ScriptResource"/>
      <remove name="MvcHttpHandler"/>
      <remove name="UrlRoutingHandler"/>
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </handlers>
  1. Обновлен мой global.asax для регистрации маршрутов и игнорирования вещей, которые мешали бы моим устаревшим вещам:

    private static void RegisterRoutes(RouteCollection routes)
    {
      // place any routes here you need to ignore, whether they
      // be legacy or legitimate resources.
      routes.IgnoreRoute(""); // i currently have functionality at "/", and this route frees up the root to be used by my Default.aspx
      routes.IgnoreRoute("{webForms}.aspx/{*pathInfo}");
      routes.IgnoreRoute("{webServices}.asmx/{*pathInfo}");
      routes.IgnoreRoute("ajaxpro/{*pathInfo}");
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
      routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new {controller = "Home", action = "Index", id = ""} // Parameter defaults
      );
    }   
    
    
    
    protected void Application_Start()
    {
       // legacy crap here
    
       AreaRegistration.RegisterAllAreas();    
       RegisterRoutes(RouteTable.Routes);
    }
    

Дополнительная информация

Я также использую последнюю версию Autofac (v1.4.5.676) и их обработчики веб-интеграции. Я проверил оба способа: все автофакты полностью удалены/отключены и все настроено так, как я хочу; никак не влияет на проблему в любом случае.

Я также пробовал это с моими специализированными супер-сладкими настройками IgnoreRoute и без них. Нет эффекта.

Кроме того, я хотел бы прояснить, что маршруты, похоже, работают, я правильно отправляюсь к своим контроллерам и действиям, у меня просто есть нулевой принцип в HttpContext.Current.User, и, похоже, он совершенно не увеличивает жизненный цикл приложения. Мероприятия. Если бы мне не нужно было, знаете ли, получать текущего принципала или делать какие-то надоедливые авторизации, я бы никогда не узнал, что что-то не так ;)

И да, обычные страницы ASPX работают правильно, и события жизненного цикла приложения вызываются нормально.

Вот пример простого тестового контроллера, который я сделал, но он не работает:

    [Authorize]
    public class FartsController : Controller
    {
        //
        // GET: /Farts/
        public ActionResult Index()
        {
            return View();
        }

    }

что приводит к следующему исключению:

[NullReferenceException: Object reference not set to an instance of an object.]
   System.Web.Mvc.AuthorizeAttribute.AuthorizeCore(HttpContextBase httpContext) +48
   System.Web.Mvc.AuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext) +35
   System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) +103
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +316
   System.Web.Mvc.Controller.ExecuteCore() +104
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +36
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
   System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__4() +34
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +53
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +43
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +7
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8678910
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

Это отстой :(. Спасибо, что прочитали мой рассказ.


person moribvndvs    schedule 30.12.2009    source источник


Ответы (2)


ОБНОВЛЕНО Хорошо, я думаю, что проблема в моем понимании того, как на самом деле работает MVC (что было неясно, пока я не отступил и на свежую голову не сравнил различия между веб-конфигурациями ASP.NET и MVC). ).

Причина моей проблемы в том, что для правильной работы приложений MVC в интегрированном режиме требуется следующий параметр (важная часть - runAllManagedModulesForAllRequests="true"):

<system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true">
        <remove name="ScriptModule"/>
        <remove name="UrlRoutingModule"/>
        <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

       <!-- omitted for clarify -->
    </modules>
    <handlers>
        <!-- omitted for clarity -->
        <add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
    </handlers>
</system.webServer>

Если это вызывает проблему для меня, так это тот факт, что мое приложение ASP.NET использует правило авторизации по умолчанию, запрещающее доступ неаутентифицированным пользователям:

  <system.web>
    <authorization>
      <deny users="?"/>
    </authorization>
  </system.web>

Принимая во внимание, что MVC не поощряет эту практику, а использует AuthorizeAttribute или какой-либо другой фильтр.

Пока я не перенесу весь мой существующий код в MVC (что займет довольно много времени), мне придется придумать умное решение для сохранения авторизации в стиле ASP.NET для отправки неаутентифицированных запросов (возможно, пользовательского HttpModule?) для защищенных ресурсов (для нас это все, кроме изображений, javascript, css, статического html и страниц входа/выхода), и удалите вышеуказанное из web.config.

Кроме того, мое приложение, которое изначально было просто ASP.NET, предполагало, что Application_BeginRequest (и ему подобные) будут только значительными запросами ресурсов, такими как страницы ASPX, ASMX, ASHX и AXD. Итак, мне нужно настроить любые события приложения, чтобы остановить любую ресурсоемкую обработку (проверки безопасности, которые поражают БД и т. д.) для статических ресурсов, которые мне не нужны (опять же, такие вещи, как изображения и т. д.).

ОБЗОР

runAllManagedModulesForAllRequests="true" требуется для MVC в интегрированном режиме. Если ваше приложение ASP.NET интенсивно использует авторизацию на основе местоположения и/или выполняет большую обработку событий жизненного цикла запроса, вам потребуется дополнительная работа, чтобы заставить MVC работать вместе с вашими формами ASP.NET. .

person moribvndvs    schedule 30.12.2009
comment
Спасибо! просто потратьте уйму времени, чтобы не заметить отсутствующий runAllManagedModulesForAllRequests=true! - person Whisk; 19.02.2010

Это может помочь, а может и нет: я только что закончил просматривать код Nerd Dinner, и они затронули эту тему конкретно в области модульного тестирования. Попробуйте просмотреть этот http://www.wrox.com/WileyCDA/Section/id-321793.html и прокручивая почти до конца, где они охватывают примеры модульных тестов. У них есть какая-то магия подделки, которая заставляет их образец контроллера учетной записи работать, позволяя вам предоставить поддельный идентификатор пользователя. Может работать на вас.

person No Refunds No Returns    schedule 30.12.2009