從mvc(2)用戶控件中,我想遍歷所有路由值。如何循環遍歷所有路線?
所以,如果我有這樣的控制器:
UserController
AccountController
我所需要的值的集合,它會出現在類似的網址:
/user/...
/account/...
即值用戶帳戶。
我怎樣才能得到這個?
我試過RouteTables,但無法弄清楚。
從mvc(2)用戶控件中,我想遍歷所有路由值。如何循環遍歷所有路線?
所以,如果我有這樣的控制器:
UserController
AccountController
我所需要的值的集合,它會出現在類似的網址:
/user/...
/account/...
即值用戶帳戶。
我怎樣才能得到這個?
我試過RouteTables,但無法弄清楚。
哦,真的是一個很好的問題,讓我的自我忙一小時。 爲了實現所需的功能,我們需要掛接到MVC源代碼和一點點反射。
缺省路由的名稱不可用,所以我們需要寫一個路由集合擴展名來保存路由名稱中的RouteData令牌。
public static Route MapRouteWithName(this RouteCollection routes,string name, string url, object defaults=null, object constraints=null)
{
Route route = routes.MapRoute(name, url, defaults, constraints);
route.DataTokens = new RouteValueDictionary();
route.DataTokens.Add("RouteName", name);
return route;
}
修改在Global.asax圖路線要進行的調用以前的擴展
routes.MapRouteWithName(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
修改了MVC PathHelper一點點。(包含此幫手項目)
using System;
using System.Collections.Specialized;
using System.Web;
public static class PathHelpers
{
// this method can accept an app-relative path or an absolute path for contentPath
public static string GenerateClientUrl(HttpContextBase httpContext, string contentPath)
{
if (String.IsNullOrEmpty(contentPath))
{
return contentPath;
}
// many of the methods we call internally can't handle query strings properly, so just strip it out for
// the time being
string query;
contentPath = StripQuery(contentPath, out query);
return GenerateClientUrlInternal(httpContext, contentPath) + query;
}
private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath)
{
if (String.IsNullOrEmpty(contentPath))
{
return contentPath;
}
// can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
bool isAppRelative = contentPath[0] == '~';
if (isAppRelative)
{
string absoluteContentPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
string modifiedAbsoluteContentPath = httpContext.Response.ApplyAppPathModifier(absoluteContentPath);
return GenerateClientUrlInternal(httpContext, modifiedAbsoluteContentPath);
}
string relativeUrlToDestination = MakeRelative(httpContext.Request.Path, contentPath);
string absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);
return absoluteUrlToDestination;
}
public static string MakeAbsolute(string basePath, string relativePath)
{
// The Combine() method can't handle query strings on the base path, so we trim it off.
string query;
basePath = StripQuery(basePath, out query);
return VirtualPathUtility.Combine(basePath, relativePath);
}
public static string MakeRelative(string fromPath, string toPath)
{
string relativeUrl = VirtualPathUtility.MakeRelative(fromPath, toPath);
if (String.IsNullOrEmpty(relativeUrl) || relativeUrl[0] == '?')
{
// Sometimes VirtualPathUtility.MakeRelative() will return an empty string when it meant to return '.',
// but links to {empty string} are browser dependent. We replace it with an explicit path to force
// consistency across browsers.
relativeUrl = "./" + relativeUrl;
}
return relativeUrl;
}
private static string StripQuery(string path, out string query)
{
int queryIndex = path.IndexOf('?');
if (queryIndex >= 0)
{
query = path.Substring(queryIndex);
return path.Substring(0, queryIndex);
}
else
{
query = null;
return path;
}
}
}
在控制器中添加少量幫手方法
public static string GenerateUrl(string routeName, string actionName, string controllerName, RouteCollection routeCollection, RequestContext requestContext)
{
RouteValueDictionary mergedRouteValues = MergeRouteValues(actionName, controllerName);
VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues);
if (vpd == null)
{
return null;
}
string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath);
return modifiedUrl;
}
public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName)
{
// Create a new dictionary containing implicit and auto-generated values
RouteValueDictionary mergedRouteValues = new RouteValueDictionary();
// Merge explicit parameters when not null
if (actionName != null)
{
mergedRouteValues["action"] = actionName;
}
if (controllerName != null)
{
mergedRouteValues["controller"] = controllerName;
}
return mergedRouteValues;
}
現在我們可以編寫一些反射邏輯來讀取控制器,動作和routenames。
Dictionary<string, List<string>> controllersAndActions = new Dictionary<string, List<string>>();
// Get all the controllers
var controllers = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(Controller).IsAssignableFrom(t));
foreach (var controller in controllers)
{
List<string> actions = new List<string>();
//Get all methods without HttpPost and with return type action result
var methods = controller.GetMethods().Where(m => typeof(ActionResult).IsAssignableFrom(m.ReturnType)).Where(a=>!a.GetCustomAttributes(typeof(HttpPostAttribute),true).Any());
methods.ToList().ForEach(a => {
actions.Add(a.Name);
});
var controllerName = controller.Name;
if (controllerName.EndsWith("Controller"))
{
var nameLength = controllerName.Length - "Controller".Length;
controllerName = controllerName.Substring(0, nameLength);
}
controllersAndActions.Add(controllerName, actions);
}
List<string> allowedRoutes = new List<string>();
var routeNames = RouteTable.Routes.Where(o=>o.GetRouteData(this.HttpContext)!=null).Select(r=>r.GetRouteData(this.HttpContext).DataTokens["RouteName"].ToString());
foreach (var cName in controllersAndActions)
{
foreach (var aName in cName.Value)
{
foreach (var item in routeNames)
{
allowedRoutes.Add(GenerateUrl(item, aName, cName.Key, RouteTable.Routes, this.Request.RequestContext));
}
}
}
要記住的要點:如果在路由已定義的任何默認參數,那麼URL爲那些控制器和操作將是空的。例如在上面的例子中 「/首頁/索引」 將顯示爲 「/」
下載示例應用程序Link To Download
你的意思是根值..? – MethodMan 2012-02-10 16:59:40
你有樹/圖形的種類? – Adrian 2012-02-10 17:19:07
RouteTable確實是一個可以看的地方...也就是說,我們用相反的方式來做:我們使用屬性來通告路由,並通過反射來建立路由表 - 我們當然可以在奇想中運行反射列出路線 – 2012-02-10 17:19:51