net mvc5 mvc asp asp.net-mvc asp.net-mvc-routing

mvc5 - Barra inclinada en una ruta ASP.NET MVC



razor partial (7)

En la última vista previa de MVC, estoy usando esta ruta para una URL heredada:

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

El problema es que ambas URL funcionan: http://example.com/Firefox-Extension http://example.com/Firefox-Extension/

Solo quiero que el segundo trabaje (para SEO). Además, cuando creo un enlace a esa página, el motor de enrutamiento me devuelve una URL sin una barra al final.

Este es el código que estoy usando para generar el enlace:

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

Creo que puede solucionar el primer problema utilizando un controlador HTTP para hacer una redirección 301 a la URL con la barra al final. Sin embargo, quiero hacer un enlace a la URL con la barra al final, y espero no tener que codificar la versión con la barra inclinada.

¿Alguien sabe cómo forzar la ruta para utilizar una barra al final?


Cuando escribe sus enlaces, siempre debe incluir la barra final. No sé si esto se aplica al marco mvc (o al enrutamiento de URL en general), pero sé que para los recursos estáticos, si no coloca la barra inclinada, agregue una ligera sobrecarga, ya que la solicitud se realiza dos veces.

La barra inmediatamente identifica la url como apuntando a un directorio. No es necesario analizar los archivos.

De nuevo, no creo que esto se aplique cuando usa enrutamiento de URL, pero no lo he investigado.

Revisa AQUÍ para ver un artículo sobre la barra final

editar: Al pensar en esto ... creo que es mejor dejar la barra, en lugar de tratar de incluirla. Cuando usa enrutamiento url, está usando la URL para enrutar directamente a un recurso. A diferencia de señalar un directorio con un index.html o default.aspx, está apuntando a un archivo específico.

Sé que la diferencia es sutil, pero puede ser mejor seguir con el no corte para las URL enrutadas, en lugar de luchar con el marco.

Use una barra inclinada estrictamente cuando en realidad está apuntando a un directorio. Pensé que supongo que podrías agregar una barra al final cada vez si realmente no te gusta.


Si tiene un contenedor sobre RouteLink entonces hay una solución fácil del problema. Por ejemplo, tenía un método envoltorio 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); //--- }

Como ve, utilicé parámetros para generar URL primero. Luego agregué "/" al final de la URL. y luego genere un enlace completo usando esas URL.


Aquí una sobrecarga para RouteLinkEx (HtmlHelper, cadena, cadena, objeto)

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


Pasé por esta publicación del blog:

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

esta mañana antes de toparse con esta pregunta en . Esa publicación del blog (del autor de esta pregunta) tiene un trackback a esta publicación del blog de Scott Hanselman con una respuesta a esta pregunta:

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

Me sorprendió no encontrar ningún enlace de aquí para allá, así que simplemente lo agregué. :)

La respuesta de Scott sugiere usar la reescritura de URL.


Aquí está mi versión para 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)); }


Creo que estás resolviendo el problema desde el ángulo equivocado. La razón dada para querer forzar la url única es para SEO. Creo que esto se refiere a obtener una penalización por contenido duplicado porque los motores de búsqueda consideran estas dos URL con el mismo contenido.

Otra solución a este problema es agregar una etiqueta CANONICAL a su página que indique a los motores de búsqueda cuál es la url "oficial" para la página. Una vez que lo haga, ya no necesitará forzar las URL y los motores de búsqueda no lo penalizarán y enrutarán los resultados de búsqueda a su url oficial.

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


MVC 5 y 6 tiene la opción de generar URL en minúsculas para sus rutas. Mi configuración de ruta se muestra a continuación:

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

Con este código, ya no necesitará la canonización de las URL ya que esto se hace por usted. Un problema que puede ocurrir si está utilizando URLs HTTP y HTTPS y quiere una URL canónica para esto. En este caso, es bastante fácil usar los enfoques anteriores y reemplazar HTTP con HTTPS o viceversa.

Otro problema es que los sitios web externos que enlazan con su sitio pueden omitir la barra inclinada o agregar caracteres en mayúscula y para esto debe realizar una redirección permanente 301 a la URL correcta con la barra al final. Para el uso completo y el código fuente, consulte la publicación de mi blog y el filtro 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 }

Ejemplo de uso para asegurar que todas las solicitudes sean redireccionadas 301 a la URL canónica correcta:

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