2011-11-30 53 views
0

因为它不是,我有一个网站,你必须在一个网址进来,并设置一个cookie来跟踪你所属的客户。我想改变这使某些控制器只使用这样的URL:改变MVC路由与动态前缀,同时保持向后url兼容

/{的friendlyName}/{控制器}/{指数}/{ID}

是友好名称是独一无二的,让我选择正确的客户,而不使用cookie kludge。

我有控制器:Home,Redirect,我不想友好名称的一部分(和可能更多)。

我还有其他几个适合这个类别的,我想搬入他们自己的领域。我怎么能不把这些区域作为有效的友好名称?例如,我有一个控制器,用于处理名为Framed的iframe中的内容。目前,这个网址看起来像/ Framed/action/id。我可以把它放在一个名为Framed的区域,其中一个控制器的名称与动作相同,我仍然可以保持相同的URL。

对于控制器Error我想好记的名称是可选的

我有,我想需要的友好名称其它控制器:SignInSignOutAccount

一旦我有路由时,问题正在改变代码,以便我的重定向保持friendlyurl。任何想法如何做到这一点?

我的问题是刚刚就如何改变我的网站路由提出了一个很好的计划。我必须维护一些URL的向后兼容性 - 即任何我不希望友好的URL部分,包括我讨论的切入他们自己的区域的控制器。我正在寻找关于如何解决这个问题并改变这些变化的好建议。

回答

3

为了实现您的目标,您将需要路线和RouteConstraints的组合。此外,您需要强制执行friendlyName是唯一的规则,并且与任何控制器或区域的名称不同。

以下航线应足以在RegisterRoutes()在Global.asax.cs中:

routes.MapRoute(
    "WithFriendlyName", 
    "{friendlyName}/{controller}/{index}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }, 
    new { friendlyName = new MustBeFriendlyName() } 
); 

routes.MapRoute(
    "Default", // Route name 
    "{controller}/{action}/{id}", // URL with parameters 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }, 
    new { controller = new MustNotRequireFriendlyName() } 
); 

的RouteConstraints应该是这个样子:

using System; 
using System.Web; 
using System.Web.Routing; 

namespace Examples.Extensions 
{ 
    public class MustBeFriendlyName : IRouteConstraint 
    { 
     public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
     { 
      // return true if this is a valid friendlyName 
      // MUST BE CERTAIN friendlyName DOES NOT MATCH ANY 
      // CONTROLLER NAMES OR AREA NAMES 
      var _db = new DbContext(); 
      return _db.FriendlyNames.FirstOrDefault(x => x.FriendlyName.ToLowerInvariant() == 
       values[parameterName].ToString().ToLowerInvariant()) != null; 
     } 
    } 

    public class MustNotRequireFriendlyName : IRouteConstraint 
    { 
     private const string controllersRequiringFriendlyNames = 
      "SignIn~SignOut~Account"; 

     public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
     { 
      // return true if this controller does NOT require a friendlyName 
      return controllersRequiringFriendlyNames.ToLowerInvariant() 
       .Contains(values[parameterName].ToString().ToLowerInvariant()); 
     } 
    } 
} 

这应该让你开始。

至于由重定向生成的URL,如果路由设置正确,则应生成URL生成,以便您可能需要的唯一更改是确保{friendlyName}正在传递的唯一更改。

当您进一步了解您的更改时,您可能必须添加一些其他路线和约束条件。

+0

我想我在你的代码中发现了一些拼写错误,因为我试图实现它。如果你有''友好名称}/{控制器}/{索引}/{id}“'而不是''{友好名称}/{控制器}/{动作}/{id}”''和'MustNotRequireFriendlyName'意见是正确的,声明需要一个不是操作员。 – Josh

1

只是想补充一点,因为可选的前缀在过去的几天里一直在咬我。虽然我想使用@counsellorben提供的解决方案,但我也需要能够使用相同的名称来寻址路由,这在使用2条路由时是不可能的。

我花了一些头疼,但最终解决方案似乎很简单。我只需要创建一个中间聚合路由:

public class AggregateRoute : RouteBase 
{ 
    private readonly RouteBase[] _routes; 

    public AggregateRoute(params RouteBase[] routes) 
    { 
    _routes = routes; 
    } 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
    RouteData routeData = null; 
    foreach (var route in _routes) 
    { 
     routeData = route.GetRouteData(httpContext); 
     if (routeData != null) break; 
    } 

    return routeData; 
    } 

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 
    { 
    VirtualPathData virtualPath = null; 
    foreach (var route in _routes) 
    { 
     virtualPath = route.GetVirtualPath(requestContext, values); 
     if (virtualPath != null) break; 
    } 

    return virtualPath; 
    } 
} 

这让我做的事情:

routes.Add(
    "RouteName", 
    new AggregateRoute(
     new Route("{path}", new MvcRouteHandler()), 
     new Route("{prefix}/{path}", new MvcRouteHandler()) 
    ) 
); 

其中由同一个名字,分别加入两种途径时,这是不可能使解决两种途径:

Url.RouteLink("RouteName", new RouteValueDictionary{ 
    new{path="some-path"}}); 

Url.RouteLink("RouteName", new RouteValueDictionary{ 
    new{path="some-prefix/some-path"}});