将旧版 ASP.NET 迁移到 MVC 2 (RC):HttpApplication 事件未触发,用户主体为 null
问题
我正在尝试采用现有的 ASP.NET Web 应用程序并将其手动迁移以使用 MVC 2(我当前正在运行 RC)。我遵循了几个步骤(我稍后会列出)并且似乎可以正常工作,但后来我注意到我无法设置 AuthorizeAttribute
因为控制器上的 User 为 null。然后我注意到,当我导航到某个操作时,没有引发 HttpApplication 的常见生命周期事件(例如 BeginRequest 等)。我认为这可能与用户主体为空的原因有关。 Web 主机是本地 IIS7 实例(在我的 Vista 工作站上)。
我创建了一个全新的 MVC 2 Web 应用程序作为迁移时的参考。它运行得很好,引发应用程序事件并填充用户主体,正如我所期望的那样。
如果您想惩罚自己,请继续阅读下面的完整详细信息(尽我所能)。
迁移步骤
- 确保应用程序目录的应用程序池为 .NET 2.0 并集成
- 引用 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 作为本地引用,以便更轻松地集成和部署。
- 修改了 csproj 以启用 MVC VS 加载项优点(请参阅本文)
- 在项目中添加了 Controllers 和 Views 目录,从我的示例 MVC 应用程序添加到 /Views/web.config 中。
- 修改了我的 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>
system.webServer/modules 部分(记住,IIS7 已集成):
<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>
更新了我的 global.asax 以注册路由,并忽略会干扰我的旧内容的内容:
private static void RegisterRoutes(RouteCollection 路由) { // 在这里放置任何你需要忽略的路由,无论它们是 // 是遗留资源或合法资源。 路线.IgnoreRoute(""); // 我目前在“/”处具有功能,并且此路由释放根以供我的 Default.aspx 使用 路线.IgnoreRoute(“{webForms}.aspx/{*pathInfo}”); 路线.IgnoreRoute("{webServices}.asmx/{*pathInfo}"); 路线.IgnoreRoute(“ajaxpro/{*pathInfo}”); 路线.IgnoreRoute("{resource}.axd/{*pathInfo}"); 路线.MapRoute( "默认", // 路由名称 "{controller}/{action}/{id}", // 带参数的 URL new {controller = "Home", action = "Index", id = ""} // 参数默认值 ); } protected void Application_Start() { // 这里是遗留的废话 AreaRegistration.RegisterAllAreas(); 注册路由(RouteTable.Routes); }
其他信息
我还使用 Autofac 的最新发行版本(v1.4.5 .676)并利用他们的 Web 集成处理程序。我已经检查了两种方式,所有自动事实的东西都完全删除/禁用,并且一切都按照我想要的方式设置;无论如何,对这个问题都没有影响。
我也尝试过使用和不使用专门的超甜蜜 IgnoreRoute 设置。没有效果。
另外,我想澄清的是,路由似乎确实有效,我正确地发送到我的控制器和操作,我在 HttpContext.Current.User 上只有一个 Null 主体,它似乎完全不会引发任何应用程序生命周期事件。如果我不需要,你知道,获取当前主体或进行任何烦人的授权,我永远不会知道有什么问题;)
是的,常规 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
这很糟糕:(。感谢您阅读我的短篇故事。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
已更新
好吧,我认为这个问题是我对 MVC 真正工作原理的理解失败(直到我回过头来并用新的想法比较 ASP.NET 和 MVC Web 配置之间的差异时才清楚)。
原因是因为 MVC 应用程序需要以下设置才能在集成模式下正常运行(重要部分是
runAllManagedModulesForAllRequests="true"
):我的问题的 对我来说,一个问题是我的 ASP.NET 应用程序使用默认授权规则拒绝未经身份验证的用户的访问:
而 MVC 不鼓励这种做法,而是使用
AuthorizeAttribute
或其他过滤器。在我将所有现有代码移至 MVC 之前(这将需要相当长的时间),我将不得不想出一个巧妙的解决方案来保持 ASP.NET 风格的授权以推送未经身份验证的请求(可能是自定义的) HttpModule?)用于安全资源(对我们来说,除了图像、javascript、css、静态 html 和登录/注销页面之外的所有内容),并从 web.config 中删除上述内容。
此外,我的应用程序最初只是 ASP.NET,假设 Application_BeginRequest(等)只是重要的资源请求,例如 ASPX、ASMX、ASHX 和 AXD 页面。因此,我需要调整任何应用程序事件,以停止对我不关心的静态资源(同样,图像等)的任何资源密集型处理(访问数据库的安全检查等)。
SUMMARY
对于集成模式下的 MVC,
runAllManagedModulesForAllRequests="true"
是必需的。如果您的 ASP.NET 应用程序大量使用基于位置的授权,和/或对请求生命周期事件进行大量处理,那么您将需要做一些额外的工作才能让 MVC 与 ASP.NET 表单一起工作。UPDATED
Alright, I think the issue is a failure in my understanding of how MVC really works (which was not clear until I pulled back and with a fresh mind compared the differences between ASP.NET and MVC web configs).
The reason for my problem is because the following setting is required for MVC applications to run properly in integrated mode (the important part is
runAllManagedModulesForAllRequests="true"
):Where this causes an issue for me would be the fact that my ASP.NET application uses a default authorization rule denying access to unauthenticated users:
Whereas MVC does not encourage this practice, but rather using
AuthorizeAttribute
or some other filter.Until I move all of my existing code over to MVC (which will take quite some time), I will have to come up with a clever solution for keeping the ASP.NET-style authorization in place to push out unauthenticated requests (maybe a custom HttpModule?) for secured resources (which for us is everything except images, javascript, css, static html, and the login/logout pages), and remove the above from the web.config.
Furthermore, my application, being originally just ASP.NET, assumed that Application_BeginRequest (and the likes) would only be significant resource requests, like ASPX, ASMX, ASHX, and AXD pages. So, I need to adjust any application events to stop any resource intensive processing (security checks that hit the DB, etc.) for static resources that I don't care about (again, things like images, etc.).
SUMMARY
runAllManagedModulesForAllRequests="true"
is required for MVC in integrated mode. If your ASP.NET application uses location-based authorization heavily, and/or does a lot of processing on request lifecycle events, you're going to have some extra work cut out for you to get MVC working along side your ASP.NET forms.这可能有帮助,也可能没有帮助:我刚刚审查完书呆子晚餐代码,他们专门在单元测试领域讨论了这个主题。尝试浏览此 http://www.wrox.com/WileyCDA/Section/id -321793.html 并滚动到几乎底部,其中覆盖了示例单元测试。他们有一些伪造的魔法,使他们的示例帐户控制器可以让您提供虚假的用户 ID。可能对你有用。
This may or may not help: I just finished reviewing the Nerd Dinner code and theycovered this topic specifically in the area of unit testing. Try browsering this http://www.wrox.com/WileyCDA/Section/id-321793.html and scrolling nearly to the bottom where they cover the sample unit tests. They have some fake out magic that makes their sample Account controller work to let you supply a fake user ID. Might work for you.