2012-07-15 33 views
6

Tôi muốn tạo các sên tùy chỉnh cho các trang trong CMS của mình, để người dùng có thể tạo các url SEO của riêng mình (như Wordpress).ASP.NET MVC: Định tuyến sên tùy chỉnh mà không ảnh hưởng đến hiệu suất

Tôi đã từng làm điều này trong Ruby on Rails và khung công tác PHP bằng cách "lạm dụng" tuyến 404. Tuyến đường này được gọi khi không tìm thấy bộ điều khiển yêu cầu, cho phép tôi chuyển người dùng đến bộ điều khiển trang động động để phân tích cú pháp (Từ nơi tôi chuyển hướng đến 404 thực nếu không tìm thấy trang nào). Bằng cách này, cơ sở dữ liệu chỉ được truy vấn để kiểm tra slug yêu cầu.

Tuy nhiên, trong MVC tuyến bắt tất cả chỉ được gọi khi tuyến đường không phù hợp với tuyến đường mặc định là /{controller}/{action}/{id}.

Để vẫn có thể phân tích sên tùy chỉnh Tôi đổi 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(); 
    } 
} 

Điều này giải quyết vấn đề của tôi, nhưng đòi hỏi sự Pages bảng để được truy vấn đầy đủ cho mọi yêu cầu. Vì phương thức hiển thị quá tải (public ViewResult Show(Page p)) không hoạt động nên tôi cũng phải truy xuất trang lần thứ hai vì tôi chỉ có thể chuyển ID trang.

  1. Có cách nào tốt hơn để giải quyết vấn đề của tôi không?
  2. Có thể chuyển đối tượng Trang sang phương thức Hiển thị của tôi thay vì ID trang không?
+2

Không phải nó chỉ được khởi tạo khi khởi động ứng dụng? Chỉ trên một mặt lưu ý: 'db.Dispose();'? Edit: Xin lỗi tôi đã không đọc câu hỏi của bạn rất tốt. Có lẽ bạn có thể đặt các trang trong bộ đệm ẩn toàn cục? – Silvermind

+0

Cảm ơn bạn đã chỉ đúng hướng! Chức năng này thực sự chỉ được gọi khi khởi động. Tôi đoán tôi đã nhìn vào nó như thể nó là một ngôn ngữ diễn giải (như PHP). Xem xét đây là mã chỉ được thực thi khi khởi động tôi đoán tác động hiệu suất là không đáng kể. Tuy nhiên, tôi vẫn không chắc chắn nếu đây là cách để đi, hoặc nếu điều này đã đạt được bằng cách sử dụng chức năng dựng sẵn. Tôi cũng vẫn tự hỏi liệu có thể chuyển Mô hình thay vì ID (Câu hỏi số 2) hay không. – christiaanderidder

Trả lời

2

Thậm chí nếu mã số đăng ký tuyến đường của bạn hoạt động như là, vấn đề sẽ là các tuyến đường đã được đăng ký tĩnh chỉ khi khởi động. Điều gì xảy ra khi một bài đăng mới được thêm vào - bạn có phải khởi động lại nhóm ứng dụng không?

Bạn có thể đăng ký tuyến đường chứa phần slug SEO của URL của bạn và sau đó sử dụng thanh trượt trong tra cứu.

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: Mã không được biên dịch cũng không được thử nghiệm - đã không có môi trường dev của tôi có sẵn ngay bây giờ đối xử với nó như là giả;.)

Triển khai này sẽ có một tìm kiếm cho slug cho mỗi máy chủ khởi động lại. Bạn cũng có thể điền trước bộ nhớ cache slug-to-id key-value khi khởi động, vì vậy tất cả các tra cứu trang hiện có sẽ rẻ.

+0

Giải pháp hay, nhưng tôi đang cố gắng loại bỏ/Trang/phần. Tôi có nên định tuyến tất cả yêu cầu tới một bộ điều khiển và kiểm tra xem tên yêu cầu đã tồn tại như một bộ điều khiển ở đó chưa, tôi đã cố gắng tránh cách này bởi vì nó không sử dụng bất kỳ định tuyến dựng sẵn nào cho bộ điều khiển. Tuy nhiên, nếu đây là cách duy nhất để đạt được viết lại của tôi, thì MVC có cung cấp cách tìm kiếm các bộ điều khiển hiện tại không? – christiaanderidder

+0

@christiaanderidder: nếu bạn thêm tuyến cuối cùng (có, sắp xếp các vấn đề), dưới dạng 'url:" {slug} "', cơ bản nó sẽ hoạt động như một bản hack 404 thông thường. –

+1

@christiaanderidder: Nếu bạn thực hiện một tùy chỉnh ['IRouteConstraint'] (http://msdn.microsoft.com/en-us/library/system.web.routing.irouteconstraint.aspx) thay vào đó, bạn có thể thực hiện tra cứu slug và chỉ 'return false' nếu nó không' .Match (...) '. Tôi thích [triển khai trình xác thực hợp lệ có thể thử nghiệm này] (http://stackoverflow.com/a/9019603/). –

0

Tôi đã chỉnh sửa câu trả lời của tôi để đưa ra một câu trả lời hoàn chỉnh hơn cho những câu hỏi của bạn:

trả lời Trả lời câu số 1:

tuyến Đăng ký được khởi tạo khi khởi động. (Có lẽ cũng là khi tái chế Application Pool, rất có thể xảy ra.) Tôi cũng nghĩ rằng không có gì sai với cách tiếp cận của bạn vì nó chỉ xảy ra một lần. Tôi làm điều tương tự truy vấn tất cả các ngôn ngữ được hỗ trợ từ cơ sở dữ liệu để đăng ký chúng dưới dạng/TwoLetterISOLanguageName (/ nl,/en,/de, v.v.).

trả lời Trả lời câu số 2:

này nên làm việc thông qua một mô hình: Đặt nó trước khi con đường Default!

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

Các ContactController:

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

Model:

public class MyModel 
{ 
    public string Name { get; set; } 
} 
Các vấn đề liên quan