2012-03-08 14 views
11

ho notato sul violinista che [RequireHttps] fa il codice di stato 302 reindirizza al posto di 301. Non sono sicuro di come questo ha un senso ...Non dovrebbe [RequireHttps] in MVC eseguire un reindirizzamento permanente 301? Perché si fa un 302 (male per SEO?)

Se sei dicendo che un controller [RequireHttps], allora non vorrete mai che le persone visitino la versione Http di quella pagina. Quindi, perché non è un reindirizzamento permanente ... che dice ai motori di ricerca "per favore aggiorna i tuoi link in modo permanente alla versione https di questa pagina".

Se questo ha senso, e ho ragione, c'è un modo per cambiarlo in reindirizzamento 301?

+1

http://webmasters.stackexchange.com/questions/22268/when-redirecting-from-http-to-https-in-a-shop-site-which-status-code-should-iu – Hupperware

risposta

7

Sembra che la scelta di andare con 302 su 301 fosse un po 'arbitraria per cominciare. Tuttavia, non necessariamente segue che ogni URL sta per "avere" per utilizzare lo schema HTTPS. Molto bene potrebbe essere una pagina che consente l'accesso sia da HTTP che da HTTPS anche se potrebbe incoraggiare quest'ultimo. Un'implementazione in cui ciò potrebbe accadere potrebbe avere un po 'di codice cablato per determinare se utilizzare o meno HTTPS in base ad alcuni criteri speciali.

Come scenario, dai un'occhiata a Gmail. All'interno delle impostazioni, uno è in grado di consentire o disabilitare il protocollo HTTPS su ampie porzioni dell'applicazione. Quale codice dovrebbe essere restituito allora? 301 non sarebbe preciso, in quanto non è "permanente" ... solo un cambiamento su richiesta dell'utente. Purtroppo, il 302 non è del tutto preciso perché un errore 302 implica che ci sia l'intento di cambiare il link in un momento successivo (riferimento correlato http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html).

Concesso, Gmail è un esempio approssimativo perché le parti del sito che consentono tale opzione non sono in genere indicizzate da un motore di ricerca, ma la possibilità esiste ancora.

E per rispondere alla domanda finale, se si desidera un codice di stato diverso in ASP.NET MVC (che presumo si stia utilizzando dall'esempio di sintassi piccola), è possibile modificare con un semplice attributo personalizzato:

public class MyRequireHttpsAttribute : RequireHttpsAttribute 
{ 
    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     if (!filterContext.HttpContext.Request.IsSecureConnection) 
      filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.MovedPermanently; 
    } 
} 

Ora tutte le azioni che implementano l'attributo dovrebbe restituire un codice 301 di stato quando si accede tramite il protocollo HTTP.

+3

L'attributo RequireHttps non è un attributo che lascia una "scelta" nella questione. In pratica sta dicendo * DEVE ESSERE HTTPS - L'HTTP È SBAGLIATO * perché è impossibile arrivare al sito tramite HTTP. Questo è il motivo per cui sento che avrebbe dovuto essere il 301 per cominciare ... ma il tuo codice è azzeccato e apprezzo la discussione. Farò qualche ricerca in più prima di decidere di passare a 301 ... Sono un po 'nuovo al SEO. Grazie! –

+1

Giusto, e sono d'accordo con quel punto sull'attributo RequireHttps in particolare. Stavo cercando di ottenere un quadro più ampio di come non sia esplicitamente necessario in una particolare applicazione al di fuori del contesto di un particolare framework. Sono contento che il codice sia stato d'aiuto! – Dulan

+4

Sfortunatamente questo non ha funzionato ... lo fa ancora un 302. Ho provato a giocarci tutto il resto ... ma non potevo ancora scavalcare RequireHttps e farlo fare un 301. Invece, ho appena creato il mio [RequireHttpsPerm] e sto facendo il reindirizzamento me stesso. –

4

La soluzione di Dulan mi ha messo sulla strada giusta, ma l'esempio di codice non ha fermato il reindirizzamento 302 dall'implementazione core RequireHttpsAttribute. Così, ho cercato il codice di RequireHttpsAttribute e l'ho hackerato. Ecco quello che mi si avvicinò con: risposta

using System.Net; 
using System.Web.Mvc; 
using System; 
using System.Diagnostics.CodeAnalysis; 

[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "Unsealed because type contains virtual extensibility points.")] 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public class RequireHttps301Attribute : FilterAttribute, IAuthorizationFilter 
{ 

    public virtual void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) { 
      throw new ArgumentNullException("filterContext"); 
     } 

     if (!filterContext.HttpContext.Request.IsSecureConnection) { 
      HandleNonHttpsRequest(filterContext); 
     } 
    } 

    protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext) 
    { 
     // only redirect for GET requests, otherwise the browser might not propagate the verb and request 
     // body correctly. 

     if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { 
      throw new InvalidOperationException("Only redirect for GET requests, otherwise the browser might not propagate the verb and request body correctly."); 
     } 

     // redirect to HTTPS version of page 
     string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; 
     //what mvc did to redirect as a 302 
     //filterContext.Result = new RedirectResult(url); 

     //what I did to redirect as a 301 
     filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.MovedPermanently; 
     filterContext.HttpContext.Response.RedirectLocation = url; 
    } 
} 
+1

Credo che con MVC 4+ puoi usare il costruttore 'RedirectResult' che prende 2 argomenti e forza un 301:' filterContext.Result = new RedirectResult (url, true); ' –

8

di Dulan è vicino, ma non funziona, almeno con la nostra soluzione MVC 4+. Ma dopo alcuni tentativi ed errori, abbiamo fatto il nostro lavoro con 301 anziché 302. Ecco la nuova classe:

public class CustomRequireHttpsAttribute : RequireHttpsAttribute 
{ 
    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     #if !DEBUG 
     base.OnAuthorization(filterContext); 

     if (!filterContext.HttpContext.Request.IsSecureConnection) 
     { 
      string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; 
      filterContext.Result = new RedirectResult(url, true); 
     } 
     #endif 
    } 
} 

La ragione per cui la risposta di Dulan non ha funzionato sembra essere perché la proprietà del filterContext.Result Permanent è in sola lettura e può essere impostato solo quando il RedirectResult() si chiama, e il problema è che RedirectResult() viene chiamato nel metodo base.OnAuthorization(). Quindi basta chiamare il metodo base, quindi sovrascrivere lo filterContext.Result in basso con il secondo parametro di true per rendere permanente il risultato. Dopo averlo fatto, abbiamo iniziato a vedere 301 codici in Fiddler2.

1

Solo una breve nota che RequireHttpsAttribute genera anche un'eccezione InvalidOperationException se la richiesta è diversa da una richiesta GET. Questo è più utile restituendo un metodo 405 non consentito, che è un errore molto più appropriato.

Nella mia implementazione di seguito, conferisco all'utente anche l'attributo se desideri reindirizzare in modo permanente (301) o temporaneamente (302). Come affermato da @Dulan, è necessario eseguire un reindirizzamento permanente 301 se la pagina può essere sempre accessibile solo tramite HTTPS e un reindirizzamento temporaneo 302 se è possibile accedere alla pagina tramite HTTP o HTTPS.

/// <summary> 
/// Represents an attribute that forces an unsecured HTTP request to be re-sent over HTTPS. 
/// <see cref="System.Web.Mvc.RequireHttpsAttribute"/> performs a 302 Temporary redirect from a HTTP URL to a HTTPS URL. 
/// This filter gives you the option to perform a 301 Permanent redirect or a 302 temporary redirect. 
/// You should perform a 301 permanent redirect if the page can only ever be accessed by HTTPS and a 302 temporary redirect if 
/// the page can be accessed over HTTP or HTTPS. 
/// </summary> 
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)] 
public class RedirectToHttpsAttribute : FilterAttribute, IAuthorizationFilter 
{ 
    private readonly bool permanent; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="RedirectToHttpsAttribute"/> class. 
    /// </summary> 
    /// <param name="permanent">if set to <c>true</c> the redirection should be permanent; otherwise, <c>false</c>.</param> 
    public RedirectToHttpsAttribute(bool permanent) 
    { 
     this.permanent = permanent; 
    } 

    /// <summary> 
    /// Gets a value that indicates whether the redirection should be permanent. 
    /// </summary> 
    /// <value> 
    /// <c>true</c> if the redirection should be permanent; otherwise, <c>false</c>. 
    /// </value> 
    public bool Permanent 
    { 
     get { return this.permanent; } 
    } 

    /// <summary> 
    /// Determines whether a request is secured (HTTPS) and, if it is not, calls the <see cref="HandleNonHttpsRequest"/> method. 
    /// </summary> 
    /// <param name="filterContext">An object that encapsulates information that is required in order to use the <see cref="System.Web.Mvc.RequireHttpsAttribute"/> attribute.</param> 
    /// <exception cref="System.ArgumentNullException">The filterContext parameter is null.</exception> 
    public virtual void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 

     if (!filterContext.HttpContext.Request.IsSecureConnection) 
     { 
      this.HandleNonHttpsRequest(filterContext); 
     } 
    } 

    /// <summary> 
    /// Handles unsecured HTTP requests that are sent to the action method. 
    /// </summary> 
    /// <param name="filterContext">An object that encapsulates information that is required in order to use the <see cref="System.Web.Mvc.RequireHttpsAttribute"/> attribute.</param> 
    /// <exception cref="System.InvalidOperationException">The HTTP request contains an invalid transfer method override. All GET requests are considered invalid.</exception> 
    protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext) 
    { 
     // Only redirect for GET requests, otherwise the browser might not propagate the verb and request body correctly. 
     if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
     { 
      // The RequireHttpsAttribute throws an InvalidOperationException. Some bots and spiders make HEAD requests (to reduce bandwidth) 
      // and we don’t want them to see a 500-Internal Server Error. A 405 Method Not Allowed would be more appropriate. 
      throw new HttpException((int)HttpStatusCode.MethodNotAllowed, "Method Not Allowed"); 
     } 

     string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; 
     filterContext.Result = new RedirectResult(url, this.permanent); 
    } 
} 
Problemi correlati