【问题标题】:How to redirect MVC action without returning 301? (using MVC 4 beta)如何在不返回 301 的情况下重定向 MVC 操作? (使用 MVC 4 测试版)
【发布时间】:2012-03-22 12:16:22
【问题描述】:

我正在开发一个具有许多不同菜单的 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 时放弃这种灵活性。

【问题讨论】:

  • 您应该将该方法放在Controller 基类中,而不是在每个控制器中。
  • @SLaks 这似乎是一个微优化,因为代码是由 T4MVC 生成的,而且它只是一个单行代码。也没有做任何事情来帮助解决问题;)
  • 一定是慢节奏的一天;甚至无法获得赞成票 :o)

标签: c# asp.net-mvc asp.net-mvc-4 t4mvc


【解决方案1】:

有时我认为“MVC”应该被称为“RCMV”来表示“Router Controller Model View”,因为这确实是事情发生的顺序。此外,由于它只是“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 用于提出问题的解决方案。但是,旧代码确实提供了一个共享解决方案,我可以根据需要在整个控制器方法中使用它,而这需要每次使用都自定义代码。如果存在,我更喜欢更通用的解决方案。
  • 更新的答案更通用。此解决方案的另一个优点是它可以放入可重用的库中(假设您的角色名称也可以放入该库中),然后您可以在多个 MVC 应用程序中使用它。您的静态方法在您的应用内部。
  • 使用旧的解决方案,我有一个带有共享位的类库和一个修改过的 T4MVC.tt 模板来生成控制器方法,但无论如何我都会包含它。据我所知,这仍然只适用于我手动配置以通过指定自定义处理程序来启用此覆盖的路由。还是我错过了什么?
【解决方案2】:

我认为 TransferResult 应该包含在框架中,而不需要每个开发人员在它损坏时都必须为不同的版本重新实现它。 (如在此线程中,例如以下线程:Implementing TransferResult in MVC 3 RC - does not work)。

如果您同意我的观点,我只是想鼓励您投票支持“Server.Transfer”,使其包含在 MVC 框架本身中: http://aspnetwebstack.codeplex.com/workitem/798

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多