Конечная косая черта на маршруте ASP.NET MVC - PullRequest
15 голосов
/ 30 сентября 2008

В последнем предварительном просмотре MVC я использую этот маршрут для устаревшего URL:

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 через косую черту, и надеюсь, что нет необходимости жестко кодировать версию с косой чертой.

Кто-нибудь знает, как заставить маршрут использовать косую черту?

Ответы [ 7 ]

3 голосов
/ 06 мая 2010

Я наткнулся на это сообщение в блоге:

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

сегодня утром, прежде чем наткнуться на этот вопрос в StackOverflow. Этот пост в блоге (от автора этого вопроса) содержит обратную ссылку на этот пост от Скотта Хансельмана с ответом на этот вопрос:

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

Я был удивлен, когда не нашел здесь никакой ссылки, поэтому я просто добавил ее. :)

Ответ Скотта предполагает использование перезаписи URL.

3 голосов
/ 05 июня 2009

Если у вас есть обертка над 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.

2 голосов
/ 08 ноября 2008

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

Косая черта сразу идентифицирует URL как указывающий на каталог. Нет необходимости разбирать файлы.

Опять же, я не верю, что это применимо, когда вы используете маршрутизацию URL, но я не рассматривал это.

Проверьте ЗДЕСЬ для статьи о конце слеш

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

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

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

1 голос
/ 08 февраля 2015

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

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

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

1 голос
/ 23 августа 2010

Вот моя версия для 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 голос
/ 01 февраля 2010

Здесь перегрузка для RouteLinkEx (HtmlHelper, string, string, object)

        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);
        //---   
    }
0 голосов
/ 23 июля 2015

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

Другая проблема заключается в том, что внешние веб-сайты, которые ссылаются на ваш сайт, могут пропускать косую черту или добавлять символы в верхнем регистре, и для этого вам следует выполнить перенаправление 301 на правильный URL-адрес с косой чертой. Полное использование и исходный код см. В моем блоге и фильтре 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));
...