我正在尝试建立自己的小cms。我创建了一个抽象的pageBase类,该类由“静态”,“评论”,“文章”,“新闻”继承。每个都有自己的控制器方法。
我的问题是我需要允许管理员定义自己的自定义路径级别。例如news\local\mynewdog或Articles\events\conventions\mycon。所以我想要一种传递字符串数组并设置自定义路由的方法。
news\local\mynewdog
Articles\events\conventions\mycon
您可以使用自定义RouteBase子类无缝进行CMS样式的路由。
RouteBase
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带动作的方法。
CustomPageController
Details
public class CustomPageController : Controller { public ActionResult Details(Guid id) { // Do something with id return View(); } }
如果希望路由转到其他控制器操作(甚至使它们成为构造函数参数),则可以更改路由。