Разве [RequireHttps] в MVC не должен делать перманентное перенаправление 301?Почему он делает 302 (плохо для SEO?) - PullRequest
12 голосов
/ 09 марта 2012

Я заметил на Fiddler, что [RequireHttps] делает код состояния 302 перенаправляет вместо 301. Я не уверен, как это имеет смысл ...

Если вы говорите, что контроллер [RequireHttps], тоВы никогда не хотите, чтобы люди посещали Http-версию этой страницы.Так почему же не постоянное перенаправление ... сообщая поисковым системам "пожалуйста, обновите ваши ссылки постоянно на https версию этой страницы".

Если это имеет смысл, и я прав, есть лиспособ изменить его на 301 редирект?

Ответы [ 4 ]

9 голосов
/ 22 октября 2013

Ответ Дулана близок, но он не работает, по крайней мере, с нашим решением MVC 4+. Но после некоторой проб и ошибок мы заставили наших работать с 301 вместо 302. Вот новый класс:

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

Причина, по которой ответ Дулана не сработал, заключается в том, что свойство Permanent для filterContext.Result доступно только для чтения и может быть установлено только при вызове RedirectResult(), а проблема в том, что вызывается RedirectResult() в методе base.OnAuthorization(). Поэтому просто вызовите базовый метод, а затем переопределите filterContext.Result ниже вторым параметром true, чтобы сделать результат постоянным. После этого мы стали видеть 301 код в Fiddler2.

7 голосов
/ 10 марта 2012

Кажется, что выбор 302 вместо 301 был немного произвольным для начала.Однако из этого не обязательно следует, что каждый URL-адрес будет «иметь» использование схемы HTTPS.Там вполне может быть страница, которая разрешает доступ как по HTTP, так и по HTTPS, даже если это может поощрять последнее.Реализация, в которой это может произойти, может иметь некоторый код, предназначенный для определения того, использовать ли HTTPS или нет на основе некоторых специальных критериев.

В качестве сценария, приведенного выше, взгляните на Gmail.В настройках можно разрешить или запретить протокол HTTPS для больших частей приложения.Какой код должен быть возвращен тогда?301 не будет точным, так как он не «постоянный» ... только изменение по указанию пользователя.К сожалению, 302 также не совсем точен, потому что ошибка 302 подразумевает, что в какой-то момент в будущем есть намерение изменить ссылку (соответствующая ссылка http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html).

Конечно, Gmail - грубый пример, потому чточасти сайта, которые разрешают эту опцию, обычно не индексируются поисковой системой, но возможность все еще существует.

И чтобы ответить на ваш последний вопрос, если вам нужен другой код состояния в ASP.NET MVC (которыйЯ предполагаю, что вы используете из небольшого примера синтаксиса), это можно изменить с помощью простого пользовательского атрибута:

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

Теперь все действия, которые реализуют атрибут, должны возвращать код состояния 301 при доступе черезпротокол HTTP.

4 голосов
/ 24 августа 2012

Решение Дулана поставило меня на правильный путь, но пример кода не остановил перенаправление 302 из базовой реализации RequireHttpsAttribute.Итак, я посмотрел код RequireHttpsAttribute и взломал его.Вот что я придумал:

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 голос
/ 17 апреля 2015

Просто быстрое замечание, что RequireHttpsAttribute также генерирует исключение InvalidOperationException, если запрос не является GET-запросом.Это лучше сделать, возвращая метод 405. Not Allowed, что является гораздо более подходящей ошибкой.

В моей реализации ниже я также даю пользователю атрибута возможность того, хотят ли они перенаправить навсегда(301) или временно (302).Как сказал @Dulan, вы должны выполнить постоянное перенаправление 301, если доступ к странице возможен только через HTTPS, и временное перенаправление 302, если доступ к странице возможен через HTTP или 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...