ASP.NET MVC:路由定制蛞蝓,而不会影响性能 [英] ASP.NET MVC: Routing custom slugs without affecting performance

查看:142
本文介绍了ASP.NET MVC:路由定制蛞蝓,而不会影响性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个在我的CMS的网页自定义的蛞蝓,这样用户就可以创建自己的搜索引擎优化的URL(如Word preSS)。

我以前做的Ruby on Rails和PHP框架的滥用404路线。这条路线被称为当请求控制器无法找到,使我忒路由用户到我的动态页面控制器解析蛞蝓(从我他们重定向到真正的404,如果没有页面被发现)。这样数据库只查询,以检查请求的蛞蝓。

不过,在MVC中的包罗万象的路线,只有当路线不适合 / {控制器} / {行动} / {ID} 的默认路由称为

要仍然能够解析定制蛞蝓我修改了 RouteConfig.cs 文件:

 公共类RouteConfig
{
    公共静态无效的RegisterRoutes(RouteCollection路线)
    {
        routes.IgnoreRoute({}资源个.axd / {*} PATHINFO);        routes.MapHttpRoute(
            名称:DefaultApi
            routeTemplate:API / {}控制器/(编号),
            默认:新{ID = RouteParameter.Optional}
        );        RegisterCustomRoutes(路线);        routes.MapRoute(
            名称:默认,
            网址:{控制器} / {行动} / {ID}
            默认:新{控制器=页面,行动=索引,ID = UrlParameter.Optional}
        );
    }    公共静态无效RegisterCustomRoutes(RouteCollection路线)
    {
        CMSContext DB =新CMSContext();
        清单<页面和GT;页= db.Pages.ToList();
        的foreach(在页页P)
        {
            routes.MapRoute(
                名称:p.Title,
                网址:p.Slug,
                默认:新{控制器=页面,行动=显示,ID = p.ID}
            );
        }
        db.Dispose();
    }
}

这解决了我的问题,但要求表进行查询完全为每个请求。由于重载show方法(公开的ViewResult显示(页P))没有工作,我也有检索页面第二次,因为我只能通过网页ID。


  1. 是否有更好的方法来解决我的问题?

  2. 是否有可能Page对象传递给我的Show方法而不是页面的ID?


解决方案

即使你的路线报名code的工作原理是,这个问题将是该路由注册静态的启动时。当添加一个新职位,会发生什么? - 你将不得不重新启动应用程序池

您可以注册包含您的URL的SEO蛞蝓部分航线,然后用塞在一个查找。

RouteConfig.cs

routes.MapRoute(
    名称:SeoSlugPageLookup
    网址:页/ {}塞,
    默认:新{控制器=页面,
                    行动=SlugLookup
                  });

PageController.cs

公众的ActionResult SlugLookup(字符串蛞蝓)
{
    // TODO:检查这里空/空塞。    诠释? ID = GetPageId(塞);    如果(ID!= NULL){
        返回视图(显示,新的{ID});
    }    // TODO:在回退应通过搜索网站蛞蝓帮助用户。
    抛出新的HttpException(404,NOTFOUND);
}私人诠释? GetPageId(字符串蛞蝓)
{
    诠释? ID = GetPageIdFromCache(塞);    如果(ID == NULL){
        ID = GetPageIdFromDatabase(塞);        如果(ID!= NULL){
            SetPageIdInCache(塞,身份证);
        }
    }    返回ID;
}私人诠释? GetPageIdFromCache(字符串蛞蝓)
{
    //还有比如许多缓存技术:
    // http://msdn.microsoft.com/en-us/library/dd287191.aspx
    // HTTP://alandjackson.word$p$pss.com/2012/04/17/key-based-cache-in-mvc3-5/
    //根据您希望如何先进的CMS是,
    //缓存可以在一个服务层来完成。
    返回slugToPageIdCache.ContainsKey(塞)? slugToPageIdCache [塞]:空;
}私人诠释? SetPageIdInCache(字符串塞,INT ID)
{
    返回slugToPageIdCache.GetOrAdd(塞,身份证);
}私人诠释? GetPageIdFromDatabase(字符串蛞蝓)
{
    使用(CMSContext DB =新CMSContext()){
        //假设独特的蛞蝓。
        一页一页= db.Pages.Where(P => p.Slug == requestContext.Url).SingleOrDefault();        如果(页面!= NULL){
            返回page.Id;
        }
    }    返回null;
}公众的ActionResult展(INT ID)
{
    //你现有的实现。
}

(FYI:code编译没有经过测试也没有 - 没有得到我提供的开发环境,现在把它看作伪code;)

本实施将对每服务器重启蛞蝓一个搜索。你也可以$ P $在启动时对填充键值蛞蝓到ID缓存,因此所有现有的网页搜索的时候会很便宜。

I would like to create custom slugs for pages in my CMS, so users can create their own SEO-urls (like Wordpress).

I used to do this in Ruby on Rails and PHP frameworks by "abusing" the 404 route. This route was called when the requested controller could not be found, enabling me te route the user to my dynamic pages controller to parse the slug (From where I redirected them to the real 404 if no page was found). This way the database was only queried to check the requested slug.

However, in MVC the catch-all route is only called when the route does not fit the default route of /{controller}/{action}/{id}.

To still be able to parse custom slugs I modified the RouteConfig.cs file:

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

This solves my problem, but requires the Pages table to be fully queried for every request. Because a overloaded show method (public ViewResult Show(Page p)) did not work I also have to retrieve the page a second time because I can only pass the page ID.

  1. Is there a better way to solve my problem?
  2. Is it possible to pass the Page object to my Show method instead of the page ID?

解决方案

Even if your route registration code works as is, the problem will be that the routes are registered statically only on startup. What happens when a new post is added - would you have to restart the app pool?

You could register a route that contains the SEO slug part of your URL, and then use the slug in a lookup.

RouteConfig.cs

routes.MapRoute(
    name: "SeoSlugPageLookup",
    url: "Page/{slug}",
    defaults: new { controller = "Page", 
                    action = "SlugLookup",
                  });

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

(FYI: Code not compiled nor tested - haven't got my dev environment available right now. Treat it as pseudocode ;)

This implementation will have one search for the slug per server restart. You could also pre-populate the key-value slug-to-id cache at startup, so all existing page lookups will be cheap.

这篇关于ASP.NET MVC:路由定制蛞蝓,而不会影响性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆