MVC 自定义路由中的多个级别
Posted
技术标签:
【中文标题】MVC 自定义路由中的多个级别【英文标题】:Multiple levels in MVC custom routing 【发布时间】:2015-11-03 05:29:02 【问题描述】:我正在尝试构建自己的小 cms。我创建了一个抽象的 pageBase 类,它由 Static、Reviews、Articles、News 继承。每个都有自己的控制器方法。
我的问题是我需要允许管理员定义他自己的自定义路径级别。例如。 news\local\mynewdog
或 Articles\events\conventions\mycon
。所以我想要一种传递字符串数组并设置自定义路由的方法。
【问题讨论】:
认为可能与您正在寻找的内容很接近***.com/questions/16026441/… ***.com/questions/42250306/… 【参考方案1】:您可以使用自定义 RouteBase
子类无缝地创建 CMS 样式的路由。
public class PageInfo
// VirtualPath should not have a leading slash
// example: events/conventions/mycon
public string VirtualPath get; set;
public Guid Id get; set;
public class CustomPageRoute
: RouteBase
private object synclock = new object();
public override RouteData GetRouteData(HttpContextBase httpContext)
RouteData result = null;
// Trim the leading slash
var path = httpContext.Request.Path.Substring(1);
// Get the page that matches.
var page = GetPageList(httpContext)
.Where(x => x.VirtualPath.Equals(path))
.FirstOrDefault();
if (page != null)
result = new RouteData(this, new MvcRouteHandler());
// Optional - make query string values into route values.
this.AddQueryStringParametersToRouteData(result, httpContext);
// TODO: You might want to use the page object (from the database) to
// get both the controller and action, and possibly even an area.
// Alternatively, you could create a route for each table and hard-code
// this information.
result.Values["controller"] = "CustomPage";
result.Values["action"] = "Details";
// This will be the primary key of the database row.
// It might be an integer or a GUID.
result.Values["id"] = page.Id;
// IMPORTANT: Always return null if there is no match.
// This tells .NET routing to check the next route that is registered.
return result;
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
VirtualPathData result = null;
PageInfo page = null;
// Get all of the pages from the cache.
var pages = GetPageList(requestContext.HttpContext);
if (TryFindMatch(pages, values, out page))
if (!string.IsNullOrEmpty(page.VirtualPath))
result = new VirtualPathData(this, page.VirtualPath);
// IMPORTANT: Always return null if there is no match.
// This tells .NET routing to check the next route that is registered.
return result;
private bool TryFindMatch(IEnumerable<PageInfo> pages, RouteValueDictionary values, out PageInfo page)
page = null;
Guid id = Guid.Empty;
// This example uses a GUID for an id. If it cannot be parsed,
// we just skip it.
if (!Guid.TryParse(Convert.ToString(values["id"]), out id))
return false;
var controller = Convert.ToString(values["controller"]);
var action = Convert.ToString(values["action"]);
// The logic here should be the inverse of the logic in
// GetRouteData(). So, we match the same controller, action, and id.
// If we had additional route values there, we would take them all
// into consideration during this step.
if (action == "Details" && controller == "CustomPage")
page = pages
.Where(x => x.Id.Equals(id))
.FirstOrDefault();
if (page != null)
return true;
return false;
private void AddQueryStringParametersToRouteData(RouteData routeData, HttpContextBase httpContext)
var queryString = httpContext.Request.QueryString;
if (queryString.Keys.Count > 0)
foreach (var key in queryString.AllKeys)
routeData.Values[key] = queryString[key];
private IEnumerable<PageInfo> GetPageList(HttpContextBase httpContext)
string key = "__CustomPageList";
var pages = httpContext.Cache[key];
if (pages == null)
lock(synclock)
pages = httpContext.Cache[key];
if (pages == null)
// TODO: Retrieve the list of PageInfo objects from the database here.
pages = new List<PageInfo>()
new PageInfo()
Id = new Guid("cfea37e8-657a-43ff-b73c-5df191bad7c9"),
VirtualPath = "somecategory/somesubcategory/content1"
,
new PageInfo()
Id = new Guid("9a19078b-2d7e-4fc6-ae1d-3e76f8be46e5"),
VirtualPath = "somecategory/somesubcategory/content2"
,
new PageInfo()
Id = new Guid("31d4ea88-aff3-452d-b1c0-fa5e139dcce5"),
VirtualPath = "somecategory/somesubcategory/content3"
;
httpContext.Cache.Insert(
key: key,
value: pages,
dependencies: null,
absoluteExpiration: System.Web.Caching.Cache.NoAbsoluteExpiration,
slidingExpiration: TimeSpan.FromMinutes(15),
priority: System.Web.Caching.CacheItemPriority.NotRemovable,
onRemoveCallback: null);
return (IEnumerable<PageInfo>)pages;
你可以像这样用 MVC 注册路由。
routes.IgnoreRoute("resource.axd/*pathInfo");
// Case sensitive lowercase URLs are faster.
// If you want to use case insensitive URLs, you need to
// adjust the matching code in the `Equals` method of the CustomPageRoute.
routes.LowercaseUrls = true;
routes.Add(
name: "CustomPage",
item: new CustomPageRoute());
routes.MapRoute(
name: "Default",
url: "controller/action/id",
defaults: new controller = "Home", action = "Index", id = UrlParameter.Optional
);
以上假设您有一个CustomPageController
和一个Details
操作方法。
public class CustomPageController : Controller
public ActionResult Details(Guid id)
// Do something with id
return View();
如果您希望路由转到不同的控制器操作(甚至将它们设为构造函数参数),您可以更改路由。
【讨论】:
以上是关于MVC 自定义路由中的多个级别的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET MVC:在不影响性能的情况下路由自定义 slug