ASP.NET MVC: Routing Custom Pools That Do Not Affect Performance

I would like to create custom bullets for pages in my CMS so that users can create their own SEO URLs (e.g. Wordpress).

I used this in Ruby on Rails and PHP frameworks, β€œabusing” route 404. This route is called when the requested controller cannot be found, which allows me to redirect the user to my dynamic page controller to parse the bullet (from where I redirected them to the real 404 if the page was not found). Thus, the database was requested only to validate the requested pool.

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

To still be able to parse custom bullets, 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 requested for each query. Since the overloaded show method ( public ViewResult Show(Page p) ) did not work, I also need to get the page a second time, because I can only pass the page identifier.

  • Is there a better way to solve my problem?
  • Is it possible to pass the page object to my Show method instead of the page id?
+6
source share
2 answers

Even if your route registration code works as it is, the problem will be that routes are registered statically only at startup. What happens when you add a new message - do you need to restart the application pool?

You can register a route containing a portion of the ad for your URL, and then use the pool in the search.

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 that is not compiled or tested does not have access to my dev development environment). Treat it like a pseudo code;)

This implementation will have one search for the pool to restart the server. You can also pre-populate the cache key with the key-key at startup, so all existing page searches will be cheap.

+2
source

I edited my answer to give a more complete answer to your questions:

Answer to question 1:

Registration starts at startup. (It is also possible that it is very likely when the Application Pool recycled.) I also think that there is nothing wrong with your approach, since it happens only once. I do the same by querying all supported languages ​​from the database to register them as / TwoLetterISOLanguageName (/ nl, / en, / de, etc.).

Answer to question 2:

This should work when passing the model: Put it in front of the Default route!

 routes.MapRoute( name: "Contact", url: "contact/{action}", defaults: new { controller = "Contact", action = "Index", MyModel = new MyModel { Name = "hello" } }); 

ContactController:

 public ActionResult Index(MyModel mymodel) { return Content(mymodel.Name); } 

Model:

 public class MyModel { public string Name { get; set; } } 
0
source

Source: https://habr.com/ru/post/920529/


All Articles