来自数据库的ASP.NET MVC CMS动态路由


69

基本上,我有一个使用ASP.NET MVC构建的CMS后端,现在我要转到前端站点,并且需要能够根据输入的路由从cms数据库加载页面。

因此,如果用户输入domain.com/students/information,则MVC将在pages表中查找是否存在一个具有与学生/信息匹配的永久链接的页面,如果是这样,它将重定向到页面控制器,然后加载该页面数据库中的数据并将其返回到视图以显示。

到目前为止,我已经尝试了一条通吃的路线,但它仅适用于两个URL段,因此/ students / information无效,但对/ students / information / fall无效。我在网上找不到有关如何完成此操作的任何内容,因此尽管我会在这里问,但是在找到并开放源代码的ASP.NET MVC cms并剖析代码之前,我会在这里问。

这是我到目前为止的路由配置,但是我觉得有更好的方法可以做到这一点。

 public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // Default route to handle core pages
        routes.MapRoute(null,"{controller}/{action}/{id}",
                        new { action = "Index", id = UrlParameter.Optional },                  
                        new { controller = "Index" }
        );

        // CMS route to handle routing to the PageController to check the database for the route.


        var db = new MvcCMS.Models.MvcCMSContext();
        //var page = db.CMSPages.Where(p => p.Permalink == )
        routes.MapRoute(
            null,
            "{*.}",
            new { controller = "Page", action = "Index" }
        );          
    }

如果有人能为我指明正确的方向,说明我如何从数据库中加载CMS页面(最多包含三个URL段),并且仍然能够加载具有预定义的控制器和操作的核心页面。

Answers:


127

您可以使用约束来决定是否覆盖默认路由逻辑。

public class CmsUrlConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        var db = new MvcCMS.Models.MvcCMSContext();
        if (values[parameterName] != null)
        {
            var permalink = values[parameterName].ToString();
            return db.CMSPages.Any(p => p.Permalink == permalink);
        }
        return false;
    }
}

在路线定义中使用它,例如

routes.MapRoute(
    name: "CmsRoute",
    url: "{*permalink}",
    defaults: new {controller = "Page", action = "Index"},
    constraints: new { permalink = new CmsUrlConstraint() }
);

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

现在,如果您在“页面”控制器中具有“索引”操作,

public ActionResult Index(string permalink)
{
    //load the content from db with permalink
    //show the content with view
}
  1. 所有网址将被第一个路由捕获,并由约束条件进行验证。
  2. 如果永久链接存在于db中,则URL将由Page控制器中的Index操作处理。
  3. 如果不是这样,约束将失败并且URL将回退到默认路由(我不知道您在项目中是否还有其他控制器以及如何决定404逻辑)。

编辑

为了避免在Index操作中重新查询cms页面Page控制器,可以使用HttpContext.Items字典,例如

在约束中

var db = new MvcCMS.Models.MvcCMSContext();
if (values[parameterName] != null)
{
    var permalink = values[parameterName].ToString();
    var page =  db.CMSPages.Where(p => p.Permalink == permalink).FirstOrDefault();
    if(page != null)
    {
        HttpContext.Items["cmspage"] = page;
        return true;
    }
    return false;
}
return false;

然后在行动中

public ActionResult Index(string permalink)
{
    var page = HttpContext.Items["cmspage"] as CMSPage;
    //show the content with view
}

希望这可以帮助。


非常感谢您,我将尝试尝试并将其标记为答案(如果可行)。:)
卡尔·威斯

3
很棒,效果很好,只需要添加一个if(values [parameterName]!= null),否则就完美了!谢谢:)
卡尔·威斯

您好@shakib,如果有50K项,性能如何?据我所知有一个路由表缓存或类似的东西,但无论如何它从数据库检查。
Barbaros Alp 2014年

正如您所说的“ @BarbarosAlp”,“无论如何它都会从数据库中检查”,我能想到的最好的办法就是优化数据库查询。
shakib 2014年

1
@jallen已经有一段时间了,但是您可以使用HttpContext.Items字典来避免在操作中重新查询cms页面。
shakib 2015年

0

我使用更简单的方法,不需要任何自定义路由器处理。只需创建一个处理一些可选参数的单个/全局控制器,然后根据需要处理这些参数:

//Route all traffic through this controller with the base URL being the domain 
[Route("")]
[ApiController]
public class ValuesController : ControllerBase
{
    //GET api/values
    [HttpGet("{a1?}/{a2?}/{a3?}/{a4?}/{a5?}")]
    public ActionResult<IEnumerable<string>> Get(string a1 = "", string a2 = "", string a3 = "", string a4 = "", string a5 = "")
    {
        //Custom logic processing each of the route values
        return new string[] { a1, a2, a3, a4, a5 };
    }
}

在domain.com/test1/test2/test3上输出的示例

["test1","test2","test3","",""]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.