使用 Slug 的 C# Mvc 通用路由

C# Mvc Generic Route using Slug

我正在尝试创建一个通用路由来处理 slug,但我总是遇到错误

我的想法是,而不是 www.site。com/controller/action 我得到 url 一个友好的 www.site.com/{鼻涕虫}

例如www.site.com/Home/Open 将改为 www.site.com/open-your-company

错误

server error in '/' application The Resource cannot be found

在我的Global.asax我有

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

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

在我的 cshtml 之一中,我有以下 link (即使它被评论,仍然有同样的错误)。

@Html.ActionLink("Open your company", "DefaultSlug", new { controller = "Home", action = "Open", slug = "open-your-company" })

编辑:家庭控制器

public ActionResult Open() { 
    return View(new HomeModel()); 
}

在Global.asax中你slug不能为空,如果为空则url不会走默认路由

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

    routes.MapRoute(
        name: "DefaultSlug",
        url: "{slug}",
        defaults: new { controller = "Home", action = "Open" },
        constraints: new{ slug=".+"});

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

并更新 HomeController

public ActionResult Open(string slug) {
    HomeModel model = contentRepository.GetBySlug(slug);

    return View(model); 
}

测试路线link...

@Html.RouteLink("Open your company", routeName: "DefaultSlug", routeValues: new { controller = "Home", action = "Open", slug = "open-your-company" })

和行动link...

@Html.ActionLink("Open your company", "Open", routeValues: new { controller = "Home", action = "Open", slug = "open-your-company" })

两者都产生...

http://localhost:35979/open-your-company

这是我完成类似任务所采取的步骤。这依赖于模型上的自定义 Slug 字段来匹配路线。

  1. 设置您的控制器,例如Controllers\PagesController.cs:

    public class PagesController : Controller
    {
        // Regular ID-based routing
        [Route("pages/{id}")]
        public ActionResult Detail(int? id)
        {
            if(id == null)
            {
                return new HttpNotFoundResult();
            }
    
            var model = myContext.Pages.Single(x => x.Id == id);
            if(model == null)
            {
                return new HttpNotFoundResult();
            }
            ViewBag.Title = model.Title;
            return View(model);
        }
    
        // Slug-based routing - reuse View from above controller.
        public ActionResult DetailSlug (string slug)
        {
            var model = MyDbContext.Pages.SingleOrDefault(x => x.Slug == slug);
            if(model == null)
            {
                return new HttpNotFoundResult();
            }
            ViewBag.Title = model.Title;
            return View("Detail", model);
        }
    }
    
  2. 在App_Start\RouteConfig.cs中设置路由

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            // Existing route register code
    
            // Custom route - top priority
            routes.MapRoute(
                    name: "PageSlug", 
                    url: "{slug}", 
                    defaults: new { controller = "Pages", action = "DetailSlug" },
                    constraints: new {
                        slug = ".+", // Passthru for no slug (goes to home page)
                        slugMatch = new PageSlugMatch() // Custom constraint
                    }
                );
            }
    
            // Default MVC route setup & other custom routes
        }
    }
    
  3. 自定义 IRouteConstraint 实现,例如Utils\RouteConstraints.cs

    public class PageSlugMatch : IRouteConstraint
    {
        private readonly MyDbContext MyDbContext = new MyDbContext();
    
        public bool Match(
            HttpContextBase httpContext,
            Route route,
            string parameterName,
            RouteValueDictionary values,
            RouteDirection routeDirection
        )
        {
            var routeSlug = values.ContainsKey("slug") ? (string)values["slug"] : "";
            bool slugMatch = false;
            if (!string.IsNullOrEmpty(routeSlug))
            {
                slugMatch = MyDbContext.Pages.Where(x => x.Slug == routeSlug).Any();
            }
            return slugMatch;
        }
    }