Come posso evitare contenuti duplicati in ASP.NET MVC a causa di URL e valori predefiniti senza distinzione tra maiuscole e minuscole?
-
05-07-2019 - |
Domanda
Modifica : ora devo risolvere davvero questo problema, ho fatto un po 'di più indagine e venne fuori con a numero di cose per ridurre i duplicati soddisfare. Ho pubblicato un codice dettagliato esempi sul mio blog: Riduzione Contenuto duplicato con ASP.NET MVC
Primo post: vai facile se l'ho contrassegnato come sbagliato o se l'ho taggato male: P
Nel nuovo framework ASP.NET MVC di Microsoft sembra che ci siano due cose che potrebbero far sì che i tuoi contenuti vengano pubblicati su più URL (qualcosa che Google penalizza e causerà la divisione del PageRank tra di loro):
- URL senza distinzione tra maiuscole e minuscole
- URL predefinito
Puoi impostare il controller / azione predefinito da pubblicare per le richieste alla radice del tuo dominio. Diciamo che scegliamo HomeController / Index. Finiamo con i seguenti URL che offrono lo stesso contenuto:
- mydomain.com /
- mydomain.com/Home/Index
Ora, se le persone iniziano a collegarsi a entrambi, il PageRank verrebbe diviso. Google considererebbe anche i suoi contenuti duplicati e penalizzerebbe uno di loro per evitare duplicati nei loro risultati.
Inoltre, gli URL non fanno distinzione tra maiuscole e minuscole, quindi in realtà otteniamo lo stesso contenuto anche per questi URL:
- mydomain.com/Home/Index
- mydomain.com/home/index
- mydomain.com/Home/index
- mydomain.com/home/Index
- (l'elenco continua)
Quindi, la domanda ... Come posso evitare queste penalità? Vorrei:
- Tutte le richieste di reindirizzamento dell'azione predefinita (stato 301) allo stesso URL
- Tutti gli URL devono essere case sensitive
Possibile?
Soluzione
Bump!
MVC 5 Ora supporta la produzione di solo URL minuscoli e criteri comuni per la barra rovesciata.
public static void RegisterRoutes(RouteCollection routes)
{
routes.LowercaseUrls = true;
routes.AppendTrailingSlash = false;
}
Anche sulla mia applicazione per evitare contenuti duplicati su diversi domini / involucri Ip / Letter ecc ...
Tendo a produrre URL canonici basati su un PrimaryDomain - Protocollo - Controller - Lingua - Azione
public static String GetCanonicalUrl(RouteData route,String host,string protocol)
{
//These rely on the convention that all your links will be lowercase!
string actionName = route.Values["action"].ToString().ToLower();
string controllerName = route.Values["controller"].ToString().ToLower();
//If your app is multilanguage and your route contains a language parameter then lowercase it also to prevent EN/en/ etc....
//string language = route.Values["language"].ToString().ToLower();
return String.Format("{0}://{1}/{2}/{3}/{4}", protocol, host, language, controllerName, actionName);
}
Quindi puoi utilizzare la risposta di @Gabe Sumner per reindirizzare all'URL canonico della tua azione se l'URL della richiesta corrente non corrisponde.
Altri suggerimenti
Ci stavo lavorando anche su questo. Ovviamente, rimanderò a ScottGu. Offro umilmente anche la mia soluzione a questo problema.
Aggiungi il seguente codice a global.asax :
protected void Application_BeginRequest(Object sender, EventArgs e)
{
// If upper case letters are found in the URL, redirect to lower case URL.
if (Regex.IsMatch(HttpContext.Current.Request.Url.ToString(), @"[A-Z]") == true)
{
string LowercaseURL = HttpContext.Current.Request.Url.ToString().ToLower();
Response.Clear();
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location",LowercaseURL);
Response.End();
}
}
Un'ottima domanda!
Oltre a pubblicare qui, ho inviato un'e-mail a ScottGu per vedere se aveva una buona risposta. Ha fornito un esempio per l'aggiunta di vincoli ai percorsi, in modo da poter rispondere solo agli URL minuscoli:
public class LowercaseConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route,
string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
string value = (string)values[parameterName];
return Equals(value, value.ToLower());
}
E nel metodo delle rotte di registro:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "home", action = "index", id = "" },
new { controller = new LowercaseConstraint(), action = new LowercaseConstraint() }
);
}
È un inizio, ma vorrei poter cambiare la generazione di collegamenti da metodi come Html.ActionLink e RedirectToAction in modo che corrispondano.
Credo che ci sia una risposta migliore a questo. Se inserisci un link canonico nella testa della tua pagina come:
<link rel="canonical" href="http://mydomain.com/Home/Index"/>
Quindi google mostra solo la pagina canonica nei loro risultati e, soprattutto, tutta la bontà di google va a quella pagina senza penalità.
Come te, ho avuto la stessa domanda ; tranne che non ero disposto ad accontentarmi di una limitazione dell'URL tutto in minuscolo, e non mi piaceva neanche l'approccio canonico
(beh, è ??buono ma non da solo).
Non sono riuscito a trovare una soluzione, quindi abbiamo scritto e aperto a classe di reindirizzamento .
Usarlo è abbastanza semplice: ogni metodo GET nelle classi del controller deve aggiungere solo questa riga all'inizio:
Seo.SeoRedirect(this);
La classe di riscrittura SEO utilizza automaticamente gli attributi Informazioni sul chiamante di C # 5.0 per eseguire il lavoro pesante, rendendo il codice sopra rigorosamente copia-e-incolla.
Come menziono nel SO Q & amp; A collegato, sto lavorando a un modo per convertirlo in un attributo, ma per ora, fa il lavoro.
Il codice forzerà un caso per l'URL. Il caso sarà lo stesso del nome del metodo del controller: scegli se vuoi tutti i limiti, tutti più bassi o un mix di entrambi (CamelCase è buono per gli URL). Emetterà 301 reindirizzamenti per corrispondenze senza distinzione tra maiuscole e minuscole e memorizza nella cache i risultati per ottenere le migliori prestazioni. Reindirizzerà anche le barre rovesciate finali (applicate per elenchi di indici, applicate diversamente) e rimuoverà i contenuti duplicati a cui si accede tramite il nome del metodo predefinito ( Index
in un'app ASP.NET MVC stock).
Non so davvero come ti sentirai dopo 8 anni, ma ora ASP MVC 5 supporta il routing degli attributi per percorsi facili da ricordare e la risoluzione di problemi di contenuti duplicati per i siti SEO Friendly
basta aggiungere routes.MapMvcAttributeRoutes (); nel tuo RouteConfig e poi definisci un unico percorso per ogni azione come
[Route("~/")]
public ActionResult Index(int? page)
{
var query = from p in db.Posts orderby p.post_date descending select p;
var pageNumber = page ?? 1;
ViewData["Posts"] = query.ToPagedList(pageNumber, 7);
return View();
}
[Route("about")]
public ActionResult About()
{
return View();
}
[Route("contact")]
public ActionResult Contact()
{
return View();
}
[Route("team")]
public ActionResult Team()
{
return View();
}
[Route("services")]
public ActionResult Services()
{
return View();
}
Basato sulla risposta di Gabe Sumner, ma senza reindirizzamenti per JS, immagini e altri contenuti. Funziona solo su azioni del controller. L'idea è di effettuare il reindirizzamento più avanti nella pipeline quando sappiamo già che è un percorso. Per questo possiamo usare un ActionFilter.
public class RedirectFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var url = filterContext.HttpContext.Request.Url;
var urlWithoutQuery = url.GetLeftPart(UriPartial.Path);
if (Regex.IsMatch(urlWithoutQuery, @"[A-Z]"))
{
string lowercaseURL = urlWithoutQuery.ToString().ToLower() + url.Query;
filterContext.Result = new RedirectResult(lowercaseURL, permanent: true);
}
base.OnActionExecuting(filterContext);
}
}
Nota che il filtro sopra non reindirizza o modifica il case per la stringa di query.
Quindi associa ActionFilter a livello globale a tutte le azioni aggiungendolo a GlobalFilterCollection.
filters.Add(new RedirectFilterAttribute());
È una buona idea impostare ancora la proprietà LowercaseUrls su true su RouteCollection.