MVC 自定义路由中的多个级别

Posted

技术标签:

【中文标题】MVC 自定义路由中的多个级别【英文标题】:Multiple levels in MVC custom routing 【发布时间】:2015-11-03 05:29:02 【问题描述】:

我正在尝试构建自己的小 cms。我创建了一个抽象的 pageBase 类,它由 Static、Reviews、Articles、News 继承。每个都有自己的控制器方法。

我的问题是我需要允许管理员定义他自己的自定义路径级别。例如。 news\local\mynewdogArticles\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 自定义路由中的多个级别的主要内容,如果未能解决你的问题,请参考以下文章

请教一个 MVC 自定义路由的问题

.net mvc4如何设置自定义路由

ASP.NET MVC:在不影响性能的情况下路由自定义 slug

spring-cloud中zuul自定义service级别,api级别的路由白名单

Asp.net MVC 自定义路由

Spring MVC 中的自定义授权