chr*_*der 6 c# asp.net-mvc asp.net-mvc-routing asp.net-mvc-4
我想在我的CMS中为页面创建自定义slug,因此用户可以创建自己的SEO-urls(如Wordpress).
我以前通过"滥用"404路由在Ruby on Rails和PHP框架中执行此操作.无法找到请求的控制器时调用此路由,使我能够将用户路由到我的动态页面控制器以解析slug(如果没有找到页面,则从我将其重定向到真实404).这样,仅查询数据库以检查所请求的slug.
但是,在MVC中,仅当路由不适合默认路由时才会调用catch-all路由/{controller}/{action}/{id}.
为了仍然能够解析自定义slugs我修改了RouteConfig.cs文件:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
RegisterCustomRoutes(routes);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { Controller = "Pages", Action = "Index", id = UrlParameter.Optional }
);
}
public static void RegisterCustomRoutes(RouteCollection routes)
{
CMSContext db = new CMSContext();
List<Page> pages = db.Pages.ToList();
foreach (Page p in pages)
{
routes.MapRoute(
name: p.Title,
url: p.Slug,
defaults: new { Controller = "Pages", Action = "Show", id = p.ID }
);
}
db.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
这解决了我的问题,但要求Pages为每个请求完全查询表.因为重载的show method(public ViewResult Show(Page p))不起作用,我还必须第二次检索页面,因为我只能传递页面ID.
即使您的路由注册代码按原样工作,问题也将是路由仅在启动时静态注册。添加新帖子时会发生什么 - 您是否需要重新启动应用程序池?
您可以注册包含 URL 的 SEO slug 部分的路由,然后在查找中使用该 slug。
RouteConfig.cs
routes.MapRoute(
name: "SeoSlugPageLookup",
url: "Page/{slug}",
defaults: new { controller = "Page",
action = "SlugLookup",
});
Run Code Online (Sandbox Code Playgroud)
PageController.cs
public ActionResult SlugLookup (string slug)
{
// TODO: Check for null/empty slug here.
int? id = GetPageId (slug);
if (id != null) {
return View ("Show", new { id });
}
// TODO: The fallback should help the user by searching your site for the slug.
throw new HttpException (404, "NotFound");
}
private int? GetPageId (string slug)
{
int? id = GetPageIdFromCache (slug);
if (id == null) {
id = GetPageIdFromDatabase (slug);
if (id != null) {
SetPageIdInCache (slug, id);
}
}
return id;
}
private int? GetPageIdFromCache (string slug)
{
// There are many caching techniques for example:
// http://msdn.microsoft.com/en-us/library/dd287191.aspx
// http://alandjackson.wordpress.com/2012/04/17/key-based-cache-in-mvc3-5/
// Depending on how advanced you want your CMS to be,
// caching could be done in a service layer.
return slugToPageIdCache.ContainsKey (slug) ? slugToPageIdCache [slug] : null;
}
private int? SetPageIdInCache (string slug, int id)
{
return slugToPageIdCache.GetOrAdd (slug, id);
}
private int? GetPageIdFromDatabase (string slug)
{
using (CMSContext db = new CMSContext()) {
// Assumes unique slugs.
Page page = db.Pages.Where (p => p.Slug == requestContext.Url).SingleOrDefault ();
if (page != null) {
return page.Id;
}
}
return null;
}
public ActionResult Show (int id)
{
// Your existing implementation.
}
Run Code Online (Sandbox Code Playgroud)
(仅供参考:代码未编译或测试 - 目前还没有可用的开发环境。将其视为伪代码;)
此实现将在每次服务器重新启动时搜索一次 slug。您还可以在启动时预先填充键值 slug-to-id 缓存,这样所有现有的页面查找都会很便宜。