2008-09-30 17 views
15

Trong preview MVC mới nhất, tôi đang sử dụng tuyến đường này cho một URL cũ:Trailing dấu gạch chéo trên một tuyến đường ASP.NET MVC

routes.MapRoute(
"Legacy-Firefox", // Route name 
"Firefox-Extension/", // URL with parameters 
new { controller = "Home", action = "Firefox", id = "" } // Parameter defaults 
); 

Vấn đề là cả hai công việc của những URL: http://example.com/Firefox-Extension http://example.com/Firefox-Extension/

Tôi chỉ muốn thứ hai hoạt động (đối với SEO). Ngoài ra, khi tôi tạo một liên kết đến trang đó, công cụ định tuyến cung cấp cho tôi một URL mà không có dấu gạch chéo.

Đây là mã tôi đang sử dụng để tạo ra các liên kết:

<%= Html.ActionLink("Firefox Extension", "Firefox", "Home")%> 

tôi tin rằng có thể khắc phục vấn đề đầu tiên bằng cách sử dụng một trình xử lý HTTP để làm một 301 redirect đến URL với dấu gạch chéo. Tuy nhiên, tôi muốn liên kết tới URL có dấu gạch chéo và tôi hy vọng không phải mã hóa cứng phiên bản bằng dấu gạch chéo.

Bất kỳ ai biết cách buộc tuyến đường sử dụng dấu gạch chéo?

Trả lời

2

Khi bạn viết liên kết, bạn nên luôn bao gồm dấu gạch chéo cuối cùng. Tôi không biết nếu điều này áp dụng cho khuôn khổ mvc (hoặc định tuyến URL nói chung), nhưng tôi biết rằng đối với tài nguyên tĩnh, nếu bạn không đặt dấu gạch chéo trong bạn thêm một chi phí nhỏ khi yêu cầu được thực hiện hai lần.

Dấu gạch chéo ngay lập tức nhận dạng url khi trỏ đến một thư mục. Không cần phân tích cú pháp tệp.

Một lần nữa, tôi không tin điều này áp dụng khi bạn sử dụng định tuyến URL, nhưng tôi chưa xem xét nó.

Kiểm tra HERE for an article about the trailing slash

chỉnh sửa: Khi nghĩ về điều này ... Tôi nghĩ rằng nó có thể là tốt hơn để rời khỏi dấu gạch chéo, thay vì cố gắng bao gồm nó. Khi bạn đang sử dụng định tuyến url, bạn đang sử dụng URL để định tuyến trực tiếp đến tài nguyên. Trái với việc trỏ đến một thư mục có index.html hoặc default.aspx, bạn đang trỏ đến một tệp cụ thể.

Tôi biết sự khác biệt là tinh tế, nhưng nó có thể tốt hơn để dính vào không gạch chéo cho Url định tuyến, thay vì chiến đấu với khuôn khổ.

Sử dụng dấu gạch chéo sau khi bạn thực sự trỏ đến một thư mục. Nghĩ rằng tôi đoán bạn chỉ có thể thêm một dấu gạch chéo vào cuối mỗi lần nếu bạn thực sự không thích nó.

+1

Tôi đồng ý, cho các trang web mới đó là cách tôi sẽ làm điều đó. Vấn đề là, tôi có một trang web hiện tại tôi đang cố gắng chuyển đổi. –

+5

Tôi có lẽ sẽ không dành quá nhiều thời gian cho nó. Tôi đoán là Google đủ thông minh trong những ngày này để không cho bạn một hit SEO. – Armstrongest

4

Nếu bạn có trình bao bọc qua RouteLink hơn là giải pháp dễ dàng. Ví dụ, tôi đã có một phương pháp wrapper RouteLinkEx:

public static string RouteLinkEx(this HtmlHelper helper,string text,string routeName,RouteValueDictionary rvd,object htmlAttributes) 
     { 

     UrlHelper uh = new UrlHelper(helper.ViewContext.RequestContext,helper.RouteCollection); 
     // Add trailing slash to the url of the link 
     string url = uh.RouteUrl(routeName,rvd) + "/"; 
     TagBuilder builder = new TagBuilder("a") 
     { 
     InnerHtml = !string.IsNullOrEmpty(text) ? HttpUtility.HtmlEncode(text) : string.Empty 
     }; 
     builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); 
     builder.MergeAttribute("href",url); 
     return builder.ToString(TagRenderMode.Normal); 
     //--- 
     } 

Như bạn thấy tôi đã sử dụng các thông số để tạo URL đầu tiên. Sau đó, tôi đã thêm "/" ở cuối URL. và sau đó tôi tạo liên kết hoàn chỉnh bằng cách sử dụng các URL đó.

1

Dưới đây là một tình trạng quá tải cho RouteLinkEx (HtmlHelper, string, string, đối tượng)

 public static string RouteLinkEx(this HtmlHelper helper, string text, string routeName, object routeValues) 
    { 

     UrlHelper uh = new UrlHelper(helper.ViewContext.RequestContext); 

     // Add trailing slash to the url of the link 
     string url = uh.RouteUrl(routeName, routeValues) + "/"; 
     TagBuilder builder = new TagBuilder("a") 
     { 
      InnerHtml = !string.IsNullOrEmpty(text) ? HttpUtility.HtmlEncode(text) : string.Empty 
     }; 
     //builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); 
     builder.MergeAttribute("href", url); 
     return builder.ToString(TagRenderMode.Normal); 
     //--- 
    } 
3

tôi tình cờ xem qua bài đăng trên blog này:

http://www.ytechie.com/2008/10/aspnet-mvc-what-about-seo.html

sáng nay trước khi chạy vào câu hỏi này trên StackOverflow . Đó là bài viết trên blog (từ tác giả của câu hỏi này) có trackback bài blog này từ Scott Hanselman với một câu trả lời cho câu hỏi này:

http://www.hanselman.com/blog/ASPNETMVCAndTheNewIIS7RewriteModule.aspx

Tôi rất ngạc nhiên khi tìm thấy không có liên kết từ chỗ này đến chỗ nào, vì vậy tôi chỉ cần thêm nó. :)

Câu trả lời của Scott đề xuất sử dụng Viết lại URL.

+0

+1 đóng góp tốt – Maslow

1

Đây là phiên bản của tôi cho ASP.NET MVC 2

public static MvcHtmlString RouteLinkEx(this HtmlHelper helper, string text, RouteValueDictionary routeValues) 
    { 
     return RouteLinkEx(helper, text, null, routeValues, null); 
    } 

    public static MvcHtmlString RouteLinkEx(this HtmlHelper htmlHelper, string text, string routeName, RouteValueDictionary routeValues, object htmlAttributes) 
    { 
     string url = UrlHelper.GenerateUrl(routeName, null, null, null, null, null, routeValues, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, false); 

     var builder = new TagBuilder("a") 
     { 
      InnerHtml = !string.IsNullOrEmpty(text) ? HttpUtility.HtmlEncode(text) : string.Empty 
     }; 
     builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); 
     // Add trailing slash to the url of the link 
     builder.MergeAttribute("href", url + "/"); 
     return MvcHtmlString.Create(builder.ToString(TagRenderMode.Normal)); 
    } 
1

Tôi nghĩ rằng bạn đang giải quyết vấn đề từ góc độ sai. Lý do được đưa ra vì muốn ép buộc url duy nhất là dành cho SEO. Tôi tin rằng điều này đề cập đến việc bị phạt nội dung trùng lặp vì công cụ tìm kiếm xem xét hai URL này có cùng nội dung.

Một giải pháp khác cho vấn đề này sau đó là thêm thẻ CANONICAL vào trang của bạn để thông báo cho các công cụ tìm kiếm là url "chính thức" của trang. Một khi bạn làm điều đó bạn không còn cần phải buộc các URL và công cụ tìm kiếm sẽ không phạt bạn và sẽ định tuyến kết quả tìm kiếm đến url chính thức của bạn.

https://support.google.com/webmasters/answer/139066?hl=en

0

MVC 5 và 6 có tùy chọn tạo URL trường hợp thấp hơn cho tuyến đường của bạn. Cấu hình tuyến đường của tôi được hiển thị bên dưới:

public static class RouteConfig 
{ 
    public static void RegisterRoutes(RouteCollection routes) 
    { 
     // Imprive SEO by stopping duplicate URL's due to case or trailing slashes. 
     routes.AppendTrailingSlash = true; 
     routes.LowercaseUrls = true; 

     routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

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

Với mã này, bạn sẽ không còn cần chuẩn hóa URL vì điều này được thực hiện cho bạn. Một vấn đề có thể xảy ra nếu bạn đang sử dụng URL HTTP và HTTPS và muốn có URL chuẩn cho việc này. Trong trường hợp này, nó khá dễ sử dụng các phương pháp trên và thay thế HTTP bằng HTTPS hoặc ngược lại.

Một vấn đề khác là các trang web bên ngoài liên kết đến trang web của bạn có thể bỏ qua dấu gạch chéo hoặc thêm ký tự chữ hoa và bạn nên thực hiện chuyển hướng vĩnh viễn 301 đến URL chính xác với dấu gạch chéo. Đối với việc sử dụng đầy đủ và mã nguồn, hãy tham khảo blog post tôi và RedirectToCanonicalUrlAttribute lọc: ví dụ

/// <summary> 
/// To improve Search Engine Optimization SEO, there should only be a single URL for each resource. Case 
/// differences and/or URL's with/without trailing slashes are treated as different URL's by search engines. This 
/// filter redirects all non-canonical URL's based on the settings specified to their canonical equivalent. 
/// Note: Non-canonical URL's are not generated by this site template, it is usually external sites which are 
/// linking to your site but have changed the URL case or added/removed trailing slashes. 
/// (See Google's comments at http://googlewebmastercentral.blogspot.co.uk/2010/04/to-slash-or-not-to-slash.html 
/// and Bing's at http://blogs.bing.com/webmaster/2012/01/26/moving-content-think-301-not-relcanonical). 
/// </summary> 
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)] 
public class RedirectToCanonicalUrlAttribute : FilterAttribute, IAuthorizationFilter 
{ 
    private readonly bool appendTrailingSlash; 
    private readonly bool lowercaseUrls; 

    #region Constructors 

    /// <summary> 
    /// Initializes a new instance of the <see cref="RedirectToCanonicalUrlAttribute" /> class. 
    /// </summary> 
    /// <param name="appendTrailingSlash">If set to <c>true</c> append trailing slashes, otherwise strip trailing 
    /// slashes.</param> 
    /// <param name="lowercaseUrls">If set to <c>true</c> lower-case all URL's.</param> 
    public RedirectToCanonicalUrlAttribute(
     bool appendTrailingSlash, 
     bool lowercaseUrls) 
    { 
     this.appendTrailingSlash = appendTrailingSlash; 
     this.lowercaseUrls = lowercaseUrls; 
    } 

    #endregion 

    #region Public Methods 

    /// <summary> 
    /// Determines whether the HTTP request contains a non-canonical URL using <see cref="TryGetCanonicalUrl"/>, 
    /// if it doesn't calls the <see cref="HandleNonCanonicalRequest"/> method. 
    /// </summary> 
    /// <param name="filterContext">An object that encapsulates information that is required in order to use the 
    /// <see cref="RedirectToCanonicalUrlAttribute"/> attribute.</param> 
    /// <exception cref="ArgumentNullException">The <paramref name="filterContext"/> parameter is <c>null</c>.</exception> 
    public virtual void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 

     if (string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.Ordinal)) 
     { 
      string canonicalUrl; 
      if (!this.TryGetCanonicalUrl(filterContext, out canonicalUrl)) 
      { 
       this.HandleNonCanonicalRequest(filterContext, canonicalUrl); 
      } 
     } 
    } 

    #endregion 

    #region Protected Methods 

    /// <summary> 
    /// Determines whether the specified URl is canonical and if it is not, outputs the canonical URL. 
    /// </summary> 
    /// <param name="filterContext">An object that encapsulates information that is required in order to use the 
    /// <see cref="RedirectToCanonicalUrlAttribute" /> attribute.</param> 
    /// <param name="canonicalUrl">The canonical URL.</param> 
    /// <returns><c>true</c> if the URL is canonical, otherwise <c>false</c>.</returns> 
    protected virtual bool TryGetCanonicalUrl(AuthorizationContext filterContext, out string canonicalUrl) 
    { 
     bool isCanonical = true; 

     canonicalUrl = filterContext.HttpContext.Request.Url.ToString(); 
     int queryIndex = canonicalUrl.IndexOf(QueryCharacter); 

     if (queryIndex == -1) 
     { 
      bool hasTrailingSlash = canonicalUrl[canonicalUrl.Length - 1] == SlashCharacter; 

      if (this.appendTrailingSlash) 
      { 
       // Append a trailing slash to the end of the URL. 
       if (!hasTrailingSlash) 
       { 
        canonicalUrl += SlashCharacter; 
        isCanonical = false; 
       } 
      } 
      else 
      { 
       // Trim a trailing slash from the end of the URL. 
       if (hasTrailingSlash) 
       { 
        canonicalUrl = canonicalUrl.TrimEnd(SlashCharacter); 
        isCanonical = false; 
       } 
      } 
     } 
     else 
     { 
      bool hasTrailingSlash = canonicalUrl[queryIndex - 1] == SlashCharacter; 

      if (this.appendTrailingSlash) 
      { 
       // Append a trailing slash to the end of the URL but before the query string. 
       if (!hasTrailingSlash) 
       { 
        canonicalUrl = canonicalUrl.Insert(queryIndex, SlashCharacter.ToString()); 
        isCanonical = false; 
       } 
      } 
      else 
      { 
       // Trim a trailing slash to the end of the URL but before the query string. 
       if (hasTrailingSlash) 
       { 
        canonicalUrl = canonicalUrl.Remove(queryIndex - 1, 1); 
        isCanonical = false; 
       } 
      } 
     } 

     if (this.lowercaseUrls) 
     { 
      foreach (char character in canonicalUrl) 
      { 
       if (char.IsUpper(character)) 
       { 
        canonicalUrl = canonicalUrl.ToLower(); 
        isCanonical = false; 
        break; 
       } 
      } 
     } 

     return isCanonical; 
    } 

    /// <summary> 
    /// Handles HTTP requests for URL's that are not canonical. Performs a 301 Permanent Redirect to the canonical URL. 
    /// </summary> 
    /// <param name="filterContext">An object that encapsulates information that is required in order to use the 
    /// <see cref="RedirectToCanonicalUrlAttribute" /> attribute.</param> 
    /// <param name="canonicalUrl">The canonical URL.</param> 
    protected virtual void HandleNonCanonicalRequest(AuthorizationContext filterContext, string canonicalUrl) 
    { 
     filterContext.Result = new RedirectResult(canonicalUrl, true); 
    } 

    #endregion 
} 

Cách sử dụng để đảm bảo tất cả các yêu cầu được 301 chuyển hướng đến URL kinh điển đúng:

filters.Add(new RedirectToCanonicalUrlAttribute(
    RouteTable.Routes.AppendTrailingSlash, 
    RouteTable.Routes.LowercaseUrls));