2008-09-30 2 views
15

В последнем MVC предварительного просмотра, я использую этот маршрут для унаследованного URL:Скользящего слэш на пути ASP.NET MVC

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

Проблема заключается в том, что обе работе этих URL в: 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

редактировать: После думать об этом ... Я думаю, что это, вероятно, лучше оставить от косой черты, вместо того, чтобы пытаться включить его. Когда вы используете маршрутизацию URL-адресов, вы используете URL-адрес для прямого перехода на ресурс. В отличие от указания каталога с индексом index.html или default.aspx, вы указываете на определенный файл.

Я знаю, что разница тонкая, но может быть лучше придерживаться не-косая черта для маршрутизированных городов, а не бороться с каркасом.

Используйте конечную черту строго, когда вы на самом деле указываете на каталог. Думал, я думаю, вы могли бы просто добавить косую черту до конца каждый раз, если вам это действительно не понравилось.

+1

Я согласен, для новых сайтов вот как я это сделаю. Проблема в том, что у меня есть существующий сайт, который я пытаюсь преобразовать. – 2009-02-25 15:11:15

+5

Я, вероятно, не потратил бы на это слишком много времени. Я предполагаю, что Google достаточно умный в эти дни, чтобы не дать вам успех в SEO. – 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

Я думаю, что вы решаете проблему с неправильным углом. Причина, по которой вы хотите заставить единственный URL-адрес для SEO. Я считаю, что это относится к получению дублирования контента, потому что поисковые системы рассматривают эти два URL-адреса с одним и тем же контентом.

Другим решением этой проблемы является добавление к вашей странице тега CANONICAL, который сообщает поисковым системам, что является «официальным» URL-адресом для страницы. Как только вы это сделаете, вам больше не нужно принуждать URL-адреса, а поисковые системы не будут наказывать вас и направят результаты поиска на ваш официальный URL-адрес.

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, как это делается для вас. Одна из проблем, которая может возникнуть, если вы используете URL-адрес HTTP и HTTPS и хотите использовать для этого канонический URL-адрес. В этом случае довольно просто использовать вышеупомянутые подходы и заменить HTTP HTTPS или наоборот.

Другая проблема заключается в том, что внешние сайты, которые ссылаются на ваш сайт, могут опускать конечную косую черту или добавлять символы верхнего регистра, и для этого вы должны перенести 301 постоянную переадресацию на правильный URL-адрес с завершающим косой чертой. Для полного использования и исходного кода, обратитесь к моим blog post и на RedirectToCanonicalUrlAttribute фильтр: пример

/// <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 перенаправлен на правильный канонический URL:

filters.Add(new RedirectToCanonicalUrlAttribute(
    RouteTable.Routes.AppendTrailingSlash, 
    RouteTable.Routes.LowercaseUrls)); 
Смежные вопросы