Multiple levels in MVC custom routing

前端 未结 1 1644
挽巷
挽巷 2020-11-22 01:58

I am trying to build my own little cms. I created an abstract pageBase class that is inherited by Static, Reviews, Articles, News. Each having there own controller methods.<

相关标签:
1条回答
  • 2020-11-22 02:26

    You can make CMS-style routes seamlessly with a custom RouteBase subclass.

    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;
        }
    }
    

    You can register the route with MVC like this.

    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 }
    );
    

    The above assumes you have a CustomPageController with a Details action method.

    public class CustomPageController : Controller
    {
        public ActionResult Details(Guid id)
        {
            // Do something with id
    
            return View();
        }
    }
    

    You can change the route if you want it to go to a different controller action (or even make them constructor parameters).

    0 讨论(0)
提交回复
热议问题