2008-09-30 96 views
15

在最新的MVC預覽上尾部的斜槓,我使用這條路線的傳統網址:的ASP.NET MVC路線

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

的問題是,這兩個網址的工作: http://example.com/Firefox-Extension http://example.com/Firefox-Extension/

我只想要第二個工作(對於SEO)。另外,當我創建一個指向該頁面的鏈接時,路由引擎會給我一個沒有斜線的URL。

這是我使用生成的鏈接代碼:

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

我相信可以通過使用HTTP處理程序做301重定向到與結尾的斜線的URL解決的第一個問題。但是,我想鏈接到帶有斜線的URL,我希望不必使用斜槓對版本進行硬編碼。

任何人都知道如何強制路線使用尾部斜槓?

回答

2

當您編寫鏈接時,您應該始終包含最終的斜槓。我不知道這是否適用於mvc框架(或一般的URL路由),但是我知道對於靜態資源,如果您沒有在其中添加斜線,則會在請求完成兩次時添加一個小的開銷。

斜槓立即將URL標識爲指向目錄。無需解析文件。

同樣,我不相信這適用於您使用URL路由時,但我沒有看過它。

檢查HERE for an article about the trailing slash

編輯: 在思考這個...我認爲這可能會更好過離開的斜線,而不是試圖將其列入。當您使用網址路由時,您正在使用網址直接路由到資源。與指向具有index.html或default.aspx的目錄相反,您指向特定的文件。

我知道這種差異很微妙,但是堅持使用Routes Urls的非斜線可能會更好,而不是與框架打架。

嚴格地說,當您實際指向目錄時,請使用尾部斜線。我想如果你真的不喜歡它的話,你可以每次都附加斜線到最後。

+1

我同意,新的網站我就是這麼做的。問題是,我有一個現有的網站,我試圖轉換。 – 2009-02-25 15:11:15

+5

我可能不會在這上面花太多時間。我的猜測是,谷歌現在很聰明,不會給你一個搜索引擎優化。 – Armstrongest 2009-02-25 21:32:26

4

如果你有一個RouteLink封裝,那麼這個問題很簡單。 例如,我有一個包裝方法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); 
     //--- 
     } 

正如你看到我使用的參數首先生成URL。然後我在URL的末尾添加「/」。然後我使用這些URL生成完整的鏈接。

1

這裏RouteLinkEx(的HtmlHelper,字符串,字符串對象)

 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

我正好碰到這個博客帖子超載:

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

今天上午運行到StackOverflow上這個問題之前, 。這博客文章(從這個問題的作者)的引用,以從斯科特Hanselman的這個博客帖子有這個問題的答案:

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

我很驚訝地發現從這裏到那裏沒有聯繫呢,所以我只是加了它。 :)

斯科特的答案建議使用URL重寫。

1

這裏是我的版本的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

我認爲你是從錯誤的角度解決問題。爲了強制單個網址給出的理由是SEO。我認爲這是因爲搜索引擎認爲這兩個網址具有相同的內容而導致重複的內容處罰。

另一種解決這個問題,那麼是一個規範的標籤添加到您的網頁,其中告訴搜索引擎這對於頁面中的「官方」的網址。一旦你這樣做,你不再需要強制的網址和搜索引擎不會懲罰你,將路徑搜索結果的官方網址。

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

0

MVC 5,6具有產生小寫的URL爲你的路由的選擇。我的路由配置如下所示:

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

使用此代碼,您不再需要規範化URL,因爲這是爲您完成的。如果您使用HTTP和HTTPS URL並希望爲此使用規範網址,則可能會出現一個問題。在這種情況下,使用上述方法很容易,並用HTTPS代替HTTP,反之亦然。

的另一個問題是外部網站鏈接到你的網站可以省略結尾的斜線或添加大寫字符和這個你應該與結尾的斜線正確的URL進行301永久重定向。爲了充分利用和源代碼,請參閱我的blog postRedirectToCanonicalUrlAttribute過濾器:

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

用例,以確保所有的請求都301重定向到正確的規範網址:

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