我正在开发一个ASP.NET MVC解决方案,它有许多不同的菜单。要显示的菜单取决于当前登录用户的角色。
在MVC 3中,我有一些自定义代码来支持这种情况,通过使用单个控制器方法返回正确的菜单。它会通过将请求推迟到适当的控制器和操作来执行此操作,具体取决于当前用户。
此代码似乎在MVC 4中被破坏,我正在寻求帮助来解决它。
首先,我添加了一个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 ), new RouteData() ), 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时放弃这种灵活性。
答案 0 :(得分:5)
有时我认为“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中弃用的任何东西的方式让你朝着正确的方向前进。
答案 1 :(得分:1)
我认为TransferResult应该包含在框架中,而不是每个开发人员都必须在不同的版本被破坏时重新实现它。 (如在此主题中,例如以下主题:Implementing TransferResult in MVC 3 RC - does not work)。
如果您同意我的观点,我想鼓励您投票选择“Server.Transfer”以包含在MVC框架中: http://aspnetwebstack.codeplex.com/workitem/798