2014-07-24 101 views
5

我需要在ASP MVC應用程序中將兩個可能包含斜槓的變量映射到控制器。我們來看一個例子。MVC:如何在URL中管理slahses第一個路由參數

enter image description here

  • 庫和路徑將被URL編碼參數。
  • 存儲庫最多可以有0個斜線或1個斜線(代表或代表/模塊)
  • 路徑可以有任意數量的斜線。

例如,這些都是有效的URL:

http://mysite/rep/Items 
http://mysite/rep/module/Items/foo/bar/file.c 

有人可能會提供有關如何定義這條路一些建議嗎?

+0

它必須是那個確切的URL,還是這些參數可以被URL編碼? (我猜前者,因爲後者很容易,但值得明確詢問) – David

+0

@David:查看我的編輯 –

+0

如果你不能進行URL編碼,你唯一能做的就是把整個URL作爲一個字符串並自己解析。 – DavidG

回答

2

最後,基於在Darin Dimitrov答案,我採取了以下定義路由,可以解決我的問題:

public class RepositoryRoute : Route 
{ 
    public RepositoryRoute(string name, string url, object defaults) 
     : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler()) 
    { 
     string moduleUrl = url.Replace(
      REPOSITORY_PARAMETER, REPOSITORY_PARAMETER + MODULE_PARAMETER); 
     mModuleRoute = new Route(
      moduleUrl, new RouteValueDictionary(defaults), new MvcRouteHandler()); 
    } 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     RouteData rd = mModuleRoute.GetRouteData(httpContext); 

     if (rd == null) 
      return base.GetRouteData(httpContext); 

     if (!rd.Values.ContainsKey(MODULE)) 
      return rd; 

     // set repository as repo/submodule format 
     // if a submodule is present in the URL 
     string repository = string.Format("{0}/{1}", 
      rd.Values[REPOSITORY], 
      rd.Values[MODULE]); 

     rd.Values.Remove(MODULE); 
     rd.Values[REPOSITORY] = repository; 

     return rd; 
    } 

    Route mModuleRoute; 

    const string REPOSITORY = "repository"; 
    const string MODULE = "module"; 

    const string REPOSITORY_PARAMETER = "{" + REPOSITORY + "}/"; // {repository}/ 
    const string MODULE_PARAMETER = "{" + MODULE + "}/"; // {module}/ 
} 

這是通過以下方式註冊:

 routes.Add(new RepositoryRoute(
         "Items", 
         "{repository}/Items/{*path}", 
         new { controller = "Items", action = "Index", path = "/" } 
     )); 

該路由使用一個內部路由,它定義了一個模塊參數,如果找到了,我將它連接到存儲庫並將其刪除。因此,映射存儲庫或存儲庫/模塊是透明的。

-1

由於此問題的性質,您無法正確執行此映射。 試着拿支鉛筆和地圖下面的URL庫和路徑:

http://mysite/rep/Items/Items/Items/Items/Items/Items/Items/Items/Items 

有多個映射:

1) 庫=項目 路徑= /條目/項目/產品/項目/項目/商品

2) 庫=項/項 路徑=項/項目/項目/項目/項目/商品

等....

因此,無論你應該

  1. 傳遞參數作爲查詢字符串
  2. 定義多個路由每個倉庫的格式(和控制方法中添加部分滿倉庫名)
+0

Items是一個常量(是控制器名稱),因此您可以離散化:控制器名稱左側的所有塊都是存儲庫,並且所有控制器名稱右側的塊是路徑。 –

0

如果你不能忍受「老式」的風格參數和URL編碼,我認爲你能實現它的唯一方法就是這樣。請注意,這未經測試,但應基本上工作。此外,我已將控制器名稱放在開頭,現在除了作爲分隔符外,現在分隔符基本上沒有意義。

控制器

創建一個單一的,無參數方法的控制器:

public class GetRepo : Controller 
{ 
    public ActionResult Index() 
    { 
     //TBC 
     return View(); 
    } 
} 

路由

確保路由設置爲允許http://www.example.com/GetRepo/什麼路由到您的指數法。

請注意,GetRepo部分是重要的,否則如果您的URL是www.example.com/blah/repo/items/other/stuff而您碰巧有一個名爲blah的控制器會發生什麼情況?

魔術

現在您使用Request.Url.AbsolutePath手動解構的URL。

var urlPath = Request.Url.AbsolutePath; 

//Split the path by '/', drop the first entry as it's the action method 
var parts = urlPath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries) 
    .Skip(1).ToList(); 

//Find where "Items" separator appears: 
var posOfItems = parts.IndexOf("Items"); 

//Everything before separator is the repo: 
var repo = string.Join("/", parts.Take(posOfItems)); 

//Everything after separator is the path: 
var path = string.Join("/", parts.Skip(posOfItems + 1)); 

//Now do something with repo/path variables 
+0

感謝您的回答。此解決方案對我無效,因爲我們需要保持與舊URL的兼容性。舊網址只支持回購(無模塊)。我只是需要,如果你添加一個模塊回購(回購/模塊)它必須以同樣的方式工作。我打破了我的頭腦,想出如何去做。 –

+0

如果您有單個控制器,並且解決方案中可能存在衝突的其他任何內容,那麼您或許能夠脫身。 – DavidG

+0

或者至少您確信回購路徑不會與控制器名稱衝突。 – DavidG

2

看起來像一個定製的路線可能會削減芥菜:

public class MyRoute: Route 
{ 
    public MyRoute() 
     : base("{*catchall}", new MvcRouteHandler()) 
    { 
    } 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     var rd = base.GetRouteData(httpContext); 
     if (rd == null) 
     { 
      // we do not have a match for {*catchall}, although this is very 
      // unlikely to ever happen :-) 
      return null; 
     } 

     var segments = httpContext.Request.Url.AbsolutePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); 
     if (segments.Length < 4) 
     { 
      // we do not have the minimum number of segments 
      // in the url to have a match 
      return null; 
     } 

     if (!string.Equals("items", segments[1], StringComparison.InvariantCultureIgnoreCase) && 
      !string.Equals("items", segments[2], StringComparison.InvariantCultureIgnoreCase)) 
     { 
      // we couldn't find "items" at the expected position in the url 
      return null; 
     } 

     // at this stage we know that we have a match and can start processing 

     // Feel free to find a faster and more readable split here 
     string repository = string.Join("/", segments.TakeWhile(segment => !string.Equals("items", segment, StringComparison.InvariantCultureIgnoreCase))); 
     string path = string.Join("/", segments.Reverse().TakeWhile(segment => !string.Equals("items", segment, StringComparison.InvariantCultureIgnoreCase)).Reverse()); 

     rd.Values["controller"] = "items"; 
     rd.Values["action"] = "index"; 
     rd.Values["repository"] = repository; 
     rd.Values["path"] = path; 
     return rd; 
    } 
} 

它可以在標準的路由前進行註冊:

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

    routes.Add("myRoute", new MyRoute()); 

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

如果你打算把在任意的字符串你希望你知道Zombie Operating Systems這可能會讓你感到驚訝。

+0

那麼「myRoute」不會成爲默認路由嗎?它會捕獲每一個URL。 –

+0

啊。抱歉。我只是注意到了'return null',如果它確定它不匹配你想要解析的所有路由。 –

+0

@Darin Dimitrov:謝謝你的回答。我懷疑這個解決方案不是通用的。對於任何控制器和任何操作都可以實現這一點嗎? –

相關問題