我需要在ASP MVC應用程序中將兩個可能包含斜槓的變量映射到控制器。我們來看一個例子。MVC:如何在URL中管理slahses第一個路由參數
- 庫和路徑將被URL編碼參數。
- 存儲庫最多可以有0個斜線或1個斜線(代表或代表/模塊)
- 路徑可以有任意數量的斜線。
例如,這些都是有效的URL:
http://mysite/rep/Items
http://mysite/rep/module/Items/foo/bar/file.c
有人可能會提供有關如何定義這條路一些建議嗎?
我需要在ASP MVC應用程序中將兩個可能包含斜槓的變量映射到控制器。我們來看一個例子。MVC:如何在URL中管理slahses第一個路由參數
例如,這些都是有效的URL:
http://mysite/rep/Items
http://mysite/rep/module/Items/foo/bar/file.c
有人可能會提供有關如何定義這條路一些建議嗎?
最後,基於在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 = "/" }
));
該路由使用一個內部路由,它定義了一個模塊參數,如果找到了,我將它連接到存儲庫並將其刪除。因此,映射存儲庫或存儲庫/模塊是透明的。
由於此問題的性質,您無法正確執行此映射。 試着拿支鉛筆和地圖下面的URL庫和路徑:
http://mysite/rep/Items/Items/Items/Items/Items/Items/Items/Items/Items
有多個映射:
1) 庫=項目 路徑= /條目/項目/產品/項目/項目/商品
2) 庫=項/項 路徑=項/項目/項目/項目/項目/商品
等....
因此,無論你應該
Items是一個常量(是控制器名稱),因此您可以離散化:控制器名稱左側的所有塊都是存儲庫,並且所有控制器名稱右側的塊是路徑。 –
如果你不能忍受「老式」的風格參數和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
看起來像一個定製的路線可能會削減芥菜:
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
這可能會讓你感到驚訝。
那麼「myRoute」不會成爲默認路由嗎?它會捕獲每一個URL。 –
啊。抱歉。我只是注意到了'return null',如果它確定它不匹配你想要解析的所有路由。 –
@Darin Dimitrov:謝謝你的回答。我懷疑這個解決方案不是通用的。對於任何控制器和任何操作都可以實現這一點嗎? –
它必須是那個確切的URL,還是這些參數可以被URL編碼? (我猜前者,因爲後者很容易,但值得明確詢問) – David
@David:查看我的編輯 –
如果你不能進行URL編碼,你唯一能做的就是把整個URL作爲一個字符串並自己解析。 – DavidG