RouteHandler与ControllerFactory

时间:2011-06-21 01:30:47

标签: asp.net-mvc-3

asp.net mvc的新手(使用v3 + razor),我想知道如何最好地解决基于数据库创建动态路由的问题。基本上,主站点导航将输入到数据库中,我想将它们作为路径加载。 ie - 从数据库加载类别列表,然后在可能的情况下将路由附加到路由引擎......

mysite.com/cars mysite.com/televisions mysite.com/computers

等...

斜杠之后的每个类别都来自db,但是,有一些常规条目,比如/ about和/ contactus,它们不会在数据库中,并且已经静态输入到global.asax中...我的问题是:< / p>

对于动态数据库URL,我应该使用自定义RouteHandler或pehaps创建一个ControllerFactory,它将匹配并处理从数据库加载的条目的请求。如果我的RouteHandler或CustomControllerFactory没有从数据库中找到列表中的路由,是否可以让DefaultControllerFactory处理路由?感谢您提供任何帮助,这是第一个项目,所以我不确定最佳路线是什么;)没有双关语......

更新

尝试使用从数据库中提取的路由约束,但它现在与默认路由冲突...这是我的自定义约束和路由:

public class CategoryListConstraint : IRouteConstraint
{
    public CategoryListConstraint()
    {
        var repo = new Repository<Topic>();
        var cats = repo.All();
        var values = new List<string>();
        foreach (var c in cats)
        {
            values.Add(c.URI.Replace("/", "").Replace("?", ""));
        }
        this._values = values.ToArray<string>();
    }

    private string[] _values;

    public bool Match(HttpContextBase httpContext,
      Route route,
      string parameterName,
      RouteValueDictionary values,
      RouteDirection routeDirection)
    {
        // Get the value called "parameterName" from the 
        // RouteValueDictionary called "value"
        string value = values[parameterName].ToString();

        // Return true is the list of allowed values contains 
        // this value.
        return _values.Contains(value);
    }
}

以下是路线:

            Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                 "Categories",
                 "{category}/{*values}",
                 new { controller = "Category", action = "List" },
                 new CategoryListConstraint()
             );

            Routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

主页www.mysite.com使用默认路线加载。匹配约束列表的所有URL都按类别路由加载...但如果我有www.mysite.com/admin或www.mysite.com/aboutus,即使这些值被类别路由选中在约束列表中。困惑...

2 个答案:

答案 0 :(得分:1)

这样的事情怎么样?

类别控制器:

public ActionResult List(string category)
{
    var products = _repo.Get(category); // however you are getting your data
    return View(products);
}

<强>路线

routers.MapRoute(
    "About",
    "About",
    new { controller = "Home", action = "About" });

//... other static routes

routes.MapRoute(
    "CategoriesList",
    "{id}",
    new { controller = "Categories", action = "List" },
    new { id = @"\w+" });
  

针对每个Route规则测试传入的URL以查看它是否匹配 - 如果Route规则匹配,则该规则(及其关联的RouteHandler)是用于处理请求的规则(并忽略所有后续规则) )。这意味着您通常希望以“最具体到最不具体”的顺序构建路由规则

source

答案 1 :(得分:1)

找到我正在寻找的确切解决方案。代码如下。我设法避免使用Controller Factories或通过使用扩展RouteBase类来实现自定义IRouteHandler,该类工作得很好并且允许我将控制权传递给默认的mvc路由是特定的东西没有被击中。 BTW - 由于打破了与默认路由关联的控制器(尽管默认路由被击中),但约束最终无法正常工作

public class CustomRoutingEngine : RouteBase 
{
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {                       
        var routeHandler = new MvcRouteHandler();
        var currentRoute = new Route("{controller}/{*URI}", routeHandler);
        var routeData = new RouteData(currentRoute, routeHandler);

        // implement caching here
        var list = GetConstraintList();

        // set your values dynamically here
        routeData.Values["controller"] = "Category";
        // or
        routeData.Values.Add("action", "List");

        // return the route, or null to have it passed to the next routing engine in the list
        var url = Util.StripSlashOnFrontAndBack(httpContext.Request.Path.ToLower()).Split('/')[0];
        if (list.Contains(url))
            return routeData;
        return null;  // have another route handle the routing
    }


    protected List<string> GetConstraintList()
    {
        using (var repo = new RavenRepository<Topic>())
        {
            var tops = repo.Query().Where(x => x.Hidden == false).ToList()
                .Select(x=>x.Name.ToLower());
            List<string> list = new List<string>();
            list.AddRange(tops);
            repo.Dispose();
            return list ?? new List<string>();
        }
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        //implement this to return url's for routes, or null to just pass it on
        return null;
    }
}

然后我的寄存器路由方法如下:

            Routes.Clear();

            // Set Defaults
            Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            AreaRegistration.RegisterAllAreas();                

            routes.Add(new App.Helpers.CustomRoutingEngine());

            Routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );