MVC路由使用可选的子文件夹(就像一组虚拟目录)

时间:2012-10-01 22:51:46

标签: asp.net-mvc asp.net-mvc-routing maproute

这是我从星期五开始撕裂头发的问题。

我有一个MVC应用程序,为单个客户端提供多个不同的子组。对于品牌和某些样式元素,Url需要格式化为:     www.site.com/Login     www.site.com/Client1/Login     www.site.com/Client2/Login     www.site.com/Client3/Login 等等。

我们还希望保持这种结构,转移到www.site.com/Client1/News等。

静态路由不在桌面上。甚至是生成它们的工具。该网站将拥有X页面,为Y客户提供了一条独特的路线,我不禁想到性能。而且由于动态性,在飞行中创建虚拟目标并不是我想要旅行的路线。

起初,解决方案似乎微不足道。我尝试了两种测试解决方案。

第一个派生自CustomRouteBase并且能够在重写的GetRouteData方法中确定正确的路由,然后使用GetVirtualPath生成正确的Url。第二种解决方案使用约束来查看客户端是否在模式中并相应地路由。两者都会击中正确的控制器并生成正确的链接。

然后我添加了区域(这只是一个原型,但真实的网站使用了区域)。

两种解决方案都失败了。这些区域已经正确注册并按照通常应该的方式工作。但是使用第一个解决方案,我找不到覆盖GetVirtualPath进行区域注册的方法。我知道Area类有一个扩展方法,但这不符合我的需要。

我也尝试过使用约束,但是Url的“客户端”部分没有被添加到区域的任何动作链接中,并尝试使用约束路由到控制器,这给了我找不到好的旧视图错误(即使我在路线中指定了区域,也从根搜索)。

所以我的第一个问题是我是否采取了错误的方式?如果有更好的方法来实现这一点,我就是时代。如果没有,那我该如何管理区域呢?

我可以在这里放一些代码,但这一切都适用于我想要它做的事情。我有点迷失在如何处理区域问题上。但不幸的是,我一直在发布前3个月继承了这个项目,而我的团队根本没有资源重新构建没有它们的网站。

@Max ...我尝试了类似的东西,但在www.site.com/Client1中,这些区域仍然无法正确显示链接...这是来自原型6,我尝试了几种不同的方式。

     public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        //throws view engine can't find view error. If I use www.website.com/Client1, hvering over area links does not give the correct path
        routes.MapRoute("Area",
           "{folder}/{area}/{controller}/{action}/{id}",
            new { area = "Authentication", controller = "Authentication", action = "Index", id = UrlParameter.Optional },
            new { folder = new IsFolderContsraint(), area = new IsArea() }
        );

        //works fine.. if I use www.website.com/Client1, hovering over regular (non area) links works fine
        routes.MapRoute("Branded",
        "{folder}/{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        new { folder = new IsFolderContsraint() }
        );

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

这是我尝试解决它的另一种方式。请不要破解实施,它只是一个POC。这里的问题是Area不是RouteBase的一部分,所以我无法修改它们的虚拟路径。因此,只要动作不在某个区域内,每个动作链接等都会正确呈现并且一切正常。

public class Folders
{
    private static Dictionary<string, string> _folders= new Dictionary<string, string>()
                                                           {   {"test1", "style1"},
                                                               {"test2", "style2"},
                                                               {"test3", "style3"}
                                                            };

    public static Dictionary<string, string> FolderNames { get { return _folders; } }

}

public class AreaDefinitions
{
    private static Dictionary<string, string> _areas = new Dictionary<string, string>() 
    {  {"Authentication", "Authentication"} };

    public static Dictionary<string, string> AreaDefinition { get { return _areas; } }
}


public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.Add(new CustomRouteBase());

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

    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    }
}


public class CustomRouteBase : RouteBase
{
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        //Creates routes based on current app execution pass
        //a bit like doing my own routing constraints, but is more dynamic.
        //get route data (and CreateVirtualPath) will be called for any action needing to be rendered in the current view.
        //But not for areas :( Ugly but just a prototype

        string url = httpContext.Request.AppRelativeCurrentExecutionFilePath;
        url = url.StartsWith("~/") ? url.Substring(2, url.Length - 2) : url;
        string[] urlParts = url.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

        RouteData rd = new RouteData(this, new MvcRouteHandler());

        if (urlParts.Length == 0)
        {
            rd.Values.Add("controller", urlParts.Length > 0 ? urlParts[0] : "Home");
            rd.Values.Add("action", urlParts.Length > 1 ? urlParts[1] : "Index");
            return rd;
        }

        if (Folders.FolderNames.ContainsKey(urlParts[0]))
        {
            if (urlParts.Length > 1 && AreaDefinitions.AreaDefinition.ContainsKey(urlParts[1]))
            {
                rd.DataTokens["area"] = urlParts[1];
                rd.Values.Add("controller", urlParts.Length > 2 ? urlParts[2] : "Home");
                rd.Values.Add("action", urlParts.Length > 3 ? urlParts[3] : "Index");
            }
            else
            {
                rd.Values.Add("controller", urlParts.Length > 1 ? urlParts[1] : "Home");
                rd.Values.Add("action", urlParts.Length > 2 ? urlParts[2] : "Index");

            }
            rd.DataTokens.Add("folder", urlParts[0]);
        }
        else
        {
            rd.Values.Add("controller", urlParts.Length > 0 ? urlParts[0] : "Home");
            rd.Values.Add("action", urlParts.Length > 1 ? urlParts[1] : "Index");

        }
        return rd;

    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        //Assembled virtial path here using route and values.
        //Worked (ugly but functioned, but was never called for areas.) 
        string url = "";
        if (requestContext.RouteData.DataTokens.ContainsKey("folder"))
            url += requestContext.RouteData.DataTokens["folder"] + "/";
        if (values.ContainsKey("controller"))
            url += values["controller"] + "/";
        if (values.ContainsKey("action"))
            url += values["action"];

        var vpd = new VirtualPathData(requestContext.RouteData.Route, url);
        return vpd;
    }
}

感谢Liam ...这与我们的视图引擎的定制方式类似,首先从“plugins”文件夹中读取视图覆盖。但这里的问题有点不同。这是一个已经拥有一个具有视图覆盖的站点的客户端,但是它拥有自己的多个客户端。这里的行为只是为了控制样式表,徽标等。用户登录后,我可以确定样式和品牌应该是什么,但对于登录页面,客户端想要使用像(www)这样的网址。 site.com/Client1“来识别这个。我已经放弃并编写了一个处理程序,只需将请求转换为www.site.com/?client=client1,这样我就可以处理登陆页面样式,但是将URL保留为www.ste.com会更好。 / Client1 / login等。这是一个经典的asp应用程序的转换,它使用vdirs来托管不同的目录。由于潜在客户端数量(100个),静态路由会变得很重。我的解决方案都有所作为......这是导致所有问题的领域。如果我能找到一种方法来动态地重新映射他们的虚拟路径,就像我在RouteBase中创建的路由一样,我会在商业中......我想。

0 个答案:

没有答案