Problem
I am trying to take an existing ASP.NET web application and manually migrate it over to use MVC 2 (I am currently running the RC). I followed several steps (I'll list in a moment) and seemed to have it working, but then I noticed I can't set the AuthorizeAttribute
because User is null on the controller. Then I noticed that when I navigate to an action, none of the HttpApplication's usual lifecycle events are being raised (such as BeginRequest, etc.). I figure that's probably related to why User principal is null. The web host is a local IIS7 instance (on my Vista workstation).
I created a brand new MVC 2 web application to use as a reference while migrating. It runs just fine, raising application events and populating the User principal as I would expect.
If you're feeling like punishing yourself, read on for full details (to the best of my ability) below.
Migration Steps
- Ensure the App Pool for the application directory is .NET 2.0 and Integrated
- Referenced System.Web.Abstractions (v.3.5.0.0), System.Web.routing (v3.5.0.0), and System.Web.Mvc (v2.0.0.0) in my web project. Now, I added System.Web.Mvc as a local reference to make it easier on integration and deployment.
- Modified the csproj to enable the MVC VS add-in goodness (See this article)
- Added the Controllers and Views directories in the project, added in the /Views/web.config from my example MVC application.
- Modified my web.config (NOTE: I have other crap in modules and handlers, but I hid that for simplicity and security sake... but those may very well be part of the problem):
The compilation
section:
<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>
The pages
section:
<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>
The system.webServer/handlers
section:
<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>
Updated my global.asax to register routes, and ignore stuff that would interfere with my legacy stuff:
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); }
Additional Info
I also use the latest release version of Autofac (v1.4.5.676) and utilize their Web integration handlers. I've checked both ways, with all autofact stuff completely removed/disabled and with everything set up the way I want; no effect on the issue either way.
I've also tried this with and without my specialized super-sweet IgnoreRoute settings. No effect.
Also, I'd like to make it clear that the routes do seem to work, I get sent to my controllers and actions properly, I just have a Null principal at HttpContext.Current.User and it seems to totally not raise any application lifecycle events. If I didn't have to, you know, get the current principal or do any pesky authorization, i'd never have known anything was wrong ;)
And yes, the regular ASPX pages work correctly and application lifecycle events are raised normally.
Here's an example of a simple test controller I made that fails:
[Authorize]
public class FartsController : Controller
{
//
// GET: /Farts/
public ActionResult Index()
{
return View();
}
}
yielding the following exception:
[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
This sucks :(. Thanks for reading my short story.