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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16026441/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 23:45:11  来源:igfitidea点击:

Dynamic Routes from database for ASP.NET MVC CMS

c#asp.net-mvc-4routes

提问by Carl Weis

Basically I have a CMS backend I built using ASP.NET MVC and now I'm moving on to the frontend site and need to be able to load pages from my cms database, based on the route entered.

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

So if the user enters domain.com/students/information, MVC would look in the pages table to see if a page exists that has a permalink that matches students/information, if so it would redirect to the page controller and then load the page data from the database and return it to the view for display.

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

So far I have tried to have a catch all route, but it only works for two URL segments, so /students/information, but not /students/information/fall. I can't find anything online on how to accomplish this, so I though I would ask here, before I find and open source ASP.NET MVC cms and dissect the code.

到目前为止,我已经尝试了一个包罗万象的路由,但它只适用于两个 URL 段,所以 /students/information,而不是 /students/information/fall。我在网上找不到任何关于如何实现这一点的信息,所以我想在这里问一下,然后我找到并开源 ASP.NET MVC cms 并剖析代码。

Here is the route configuration I have so far, but I feel there is a better way to do this.

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

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

If anybody can point me in the right direction on how I would go about loading CMS pages from the database, with up to three URL segments, and still be able to load core pages, that have a controller and action predefined.

如果有人能指出我将如何从数据库加载 CMS 页面的正确方向,最多三个 URL 段,并且仍然能够加载核心页面,这些页面具有预定义的控制器和操作。

采纳答案by shakib

You can use a constraint to decide whether to override the default routing logic.

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

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

use it in route definition like,

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

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

Now if you have an 'Index' action in 'Page' Controller like,

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

public ActionResult Index(string permalink)
{
    //load the content from db with permalink
    //show the content with view
}
  1. all urls will be caught by the first route and be verified by the constraint.
  2. if the permalink exists in db the url will be handled by Index action in Page controller.
  3. if not the constraint will fail and the url will fallback to default route(i dont know if you have any other controllers in the project and how you will decide your 404 logic).
  1. 所有 url 都将被第一个路由捕获并由约束验证。
  2. 如果数据库中存在永久链接,则 url 将由页面控制器中的索引操作处理。
  3. 如果不是,则约束将失败并且 url 将回退到默认路由(我不知道项目中是否还有其他控制器以及如何决定 404 逻辑)。

EDIT

编辑

To avoid re querying the cms page in the Indexaction in Pagecontroller, one can use the HttpContext.Itemsdictionary, like

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

in the constraint

在约束中

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;

then in the action,

然后在行动中,

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

hope this helps.

希望这可以帮助。

回答by Kon Rad

I use simpler approach that doesn't require any custom router handling. Simply create a single/global Controller that handles a few optional parameters, then process those parameters as you like:

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

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

Sample output at domain.com/test1/test2/test3

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

["test1","test2","test3","",""]