我正在开发一个ASP.NET MVC解决方案,它有许多不同的菜单.要显示的菜单取决于当前登录用户的角色.
在MVC 3中,我有一些自定义代码来支持这种情况,通过一个单一的控制器方法返回正确的菜单.它会通过将请求推迟到适当的控制器和操作来执行此操作,具体取决于当前用户.
首先,我添加了一个TransferResult助手类来执行重定向:
public class TransferResult : RedirectResult { #region Transfer to URL public TransferResult( string url ) : base( url ) { } #endregion #region Transfer using RouteValues public TransferResult( object routeValues ) : base( GetRouteUrl( routeValues ) ) { } private static string GetRouteUrl( object routeValues ) { var url = new UrlHelper( new RequestContext( new HttpContextWrapper( HttpContext.Current ),new RouteData() ),RouteTable.Routes ); return url.RouteUrl( routeValues ); } #endregion #region Transfer using ActionResult (T4MVC only) public TransferResult( ActionResult result ) : base( GetRouteUrl( result.GetT4MVCResult() ) ) { } private static string GetRouteUrl( IT4MVCActionResult result ) { var url = new UrlHelper( new RequestContext( new HttpContextWrapper( HttpContext.Current ),RouteTable.Routes ); return url.RouteUrl( result.RouteValueDictionary ); } #endregion public override void ExecuteResult( ControllerContext context ) { HttpContext httpContext = HttpContext.Current; httpContext.RewritePath( Url,false ); IHttpHandler httpHandler = new MvcHttpHandler(); httpHandler.ProcessRequest( HttpContext.Current ); } }
其次,我修改了T4MVC以发出一些控制器辅助方法,导致每个控制器都有这种方法:
protected TransferResult Transfer( ActionResult result ) { return new TransferResult( result ); }
这使我可以使用共享控制器操作来返回菜单,而不必使用任何条件逻辑来混淆视图:
public virtual ActionResult Menu() { if( Principal.IsInRole( Roles.Administrator ) ) return Transfer( MVC.Admin.Actions.Menu() ); return View( MVC.Home.Views.Partials.Menu ); }
但是,TransferResult类中的ExecuteResult中的代码似乎不适用于MVC 4的当前预览版本.它给出了以下错误(指向“httpHandler.ProcessRequest”行):
'HttpContext.SetSessionStateBehavior' can only be invoked before 'HttpApplication.AcquireRequestState' event is raised.
知道如何解决这个问题吗?
PS:我意识到我可以使用简单的HtmlHelper扩展来实现相同的功能,这是我目前正在使用的解决方法.但是,我还有许多其他场景,这种方法允许我混合和重用动作,我不想在转移到MVC 4时放弃这种灵活性.
解决方法
有时我认为“MVC”应该被称为“RCMV”用于“路由器控制器模型视图”,因为这确实是事情发生的顺序.此外,由于它只是“MVC”,人们总是倾向于忘记路由. MVC的优点在于路由可配置和可扩展.我相信你想要做的事情可以通过自定义路由处理程序来解决.
我没有测试过这个,但你应该可以这样做:
routes.Add( new Route( "{controller}/{action}/{id}",new RouteValueDictionary(new { controller = "Home",action = "Menu" }),new MyRouteHandler(Roles.Administrator,new { controller = "Admin" })));
然后您的路由处理程序将如下所示:
public class MyRouteHandler : IRouteHandler { public string Role { get; set; } public object RouteValues { get; set; } public MyRouteHandler(string role,object routeValues) { Role = role; RouteValues = routeValues; } public IHttpHandler GetHttpHandler(RequestContext requestContext) { return new MyHttpHandler(Role,RouteValues); } }
最后在HttpHandler中处理重新路由:
public class MyHttpHandler : IHttpHandler { public string Role { get; set; } public object RouteValues { get; set; } public MyHttpHandler(string role,object routeValues) { Role = role; RouteValues = routeValues; } public void ProcessRequest(HttpContext httpContext) { if (httpContext.User.IsInRole(Role)) { RouteValueDictionary routeValues = new RouteValueDictionary(RouteValues); // put logic here to create path similar to what you were doing // before but you will need to replace any keys in your route // with the values from the dictionary created above. httpContext.RewritePath(path); } IHttpHandler handler = new MvcHttpHandler(); handler.ProcessRequest(httpContext); } }
这可能不是100%正确,但它应该以一种不应该遇到MVC4中弃用的任何东西的方式让你朝着正确的方向前进.