Невозможно добавить заголовок 'Vary' к ответу - PullRequest
10 голосов
/ 21 октября 2011

Я пытаюсь добавить заголовок Vary: Accept-Encoding к ответу на файлы, которые я сжимаю, , как рекомендовано ранее .

Однако по какой-то причине это невозможно - либос тестового сервера Visual Studio или сервера IIS.

У меня есть следующий код:

if (url.Contains(".js") || url.Contains(".aspx") || url.Contains(".css"))
{
    app.Response.AppendHeader("Vary", "Accept-Encoding");
    app.Response.AppendHeader("Varye", "Accept-Encoding"); // for testing
    app.Response.AppendHeader("Varye", "Accept-Things");   // for testing
    app.Response.AppendHeader("Vary", "Accept-Stuff");     // for testing
    app.Response.AppendHeader("Var", "Accept-Items");      // for testing

    encodings = encodings.ToLower();

    if (encodings.Contains("gzip") || encodings == "*")
    {
        app.Response.Filter = new GZipStream(baseStream, CompressionMode.Compress);
        app.Response.AppendHeader("Content-Encoding", "gzip");

    }
}

, в результате получается следующий заголовок ответа:

Status=OK - 200
Server=ASP.NET Development Server/10.0.0.0
Date=Fri, 21 Oct 2011 12:24:11 GMT
X-AspNet-Version=4.0.30319
Varye=Accept-Encoding, Accept-Things
Var=Accept-Items
Content-Encoding=gzip
Cache-Control=public
Etag="1CC8F2E9D772300"
Content-Type=text/css
Content-Length=16200
Connection=Close

Как видите, заголовок Vary отсутствует.Существуют бессмысленные заголовки с похожим синтаксисом, поэтому где-то должно быть что-то, удаляющее заголовок Vary до его отправки.

Я не знаю, уместно ли это, но здесь я определяю свое сжатиемодуль в web.config:

<httpModules>
    <add name="CompressionModule" type="Utility.HttpCompressionModule"/>
</httpModules>

(где Utility.HttpCompressionModule - это класс, к которому относится приведенный выше фрагмент кода.)

Почему я не могу добавитьVary header?

EDIT: Решение Эрика Си оставило меня с кодом, подобным этому:

if (url.Contains(".js") || url.Contains(".aspx") || url.Contains(".css"))
{
    app.Response.Cache.SetVaryByCustom("Accept-Encoding");

    encodings = encodings.ToLower();

    if (encodings.Contains("gzip") || encodings == "*")
    {
        app.Response.Filter = new GZipStream(baseStream, CompressionMode.Compress);
        app.Response.AppendHeader("Content-Encoding", "gzip");

    }

Но заголовки выглядят так:

Status=OK - 200
Server=ASP.NET Development Server/10.0.0.0
Date=Mon, 24 Oct 2011 09:26:37 GMT
Content-Encoding=gzip
Cache-Control=public
Etag="1CC7A09FDE77300"
Vary=*
Content-Type=application/x-javascript
Content-Length=44447
Connection=Close

(Понятия не имею, почему это application/x-javascript, поскольку оно установлено в HTML как text/javascript, но это не имеет значения.)

Как видите, у меня теперь есть заголовок var,но он установлен как Vary=*, а не Vary=Accept-Encoding, как и следовало ожидать от моего кода в модуле сжатия.

Что здесь происходит?Как правильно установить заголовок Vary?

Второе редактирование: Я собираюсь вставить исходный код для всего класса.Это не намного больше, чем я уже писал, но это может помочь понять, что именно я делаю:

public class HttpCompressionModule : IHttpModule
{
    /// <summary>
    /// Initializes a new instance of the <see cref="AjaxHttpCompressionModule"/> class.
    /// </summary>
    public HttpCompressionModule()
    {
    }

    #region IHttpModule Members

    /// <summary>
    /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
    /// </summary>
    void IHttpModule.Dispose()
    {

    }

    /// <summary>
    /// Initializes a module and prepares it to handle requests.
    /// </summary>
    /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
    void IHttpModule.Init(HttpApplication context)
    {
        context.BeginRequest += (new EventHandler(this.context_BeginRequest));
    }

    #endregion

    /// <summary>
    /// Handles the BeginRequest event of the context control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    void context_BeginRequest(object sender, EventArgs e)
    {            
        HttpApplication app = (HttpApplication)sender;
        string encodings = app.Request.Headers.Get("Accept-Encoding");
        Stream baseStream = app.Response.Filter;


        if (string.IsNullOrEmpty(encodings))
            return;


        string url = app.Request.RawUrl.ToLower();

        if (url.Contains(".js") || url.Contains(".css") || url.Contains("ajax.ashx"))
        {
            app.Response.Cache.SetVaryByCustom("Accept-Encoding");

            encodings = encodings.ToLower();

            if (encodings.Contains("gzip") || encodings == "*")
            {
                app.Response.Filter = new GZipStream(baseStream, CompressionMode.Compress);
                app.Response.AppendHeader("Content-Encoding", "gzip");

            }
            else if (encodings.Contains("deflate"))
            {
                app.Response.Filter = new DeflateStream(baseStream, CompressionMode.Compress);
                app.Response.AppendHeader("Content-Encoding", "deflate");
            }
        }
    }
}

Далее, вот раздел System.Web моегоФайл web.config:

<system.web>
    <!--<compilation debug="true"></compilation>-->
    <trace enabled="true" traceMode="SortByTime"/>
    <httpRuntime executionTimeout="180"/>
    <globalization culture="en-GB" uiCulture="en-GB"/>
    <!-- custom errors-->
    <customErrors mode="Off">
    </customErrors>
    <!-- Membership -->
    <membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
        <providers>
            <clear/>
            <add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SQLServerAuth" applicationName="mycompany" minRequiredPasswordLength="4" minRequiredNonalphanumericCharacters="0" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed" maxInvalidPasswordAttempts="1024"/>
        </providers>
    </membership>
    <!-- Roles -->
    <roleManager enabled="true" cacheRolesInCookie="true" defaultProvider="SqlProvider">
        <providers>
            <clear/>
            <add connectionStringName="SQLServerAuth" applicationName="mycompany" name="SqlProvider" type="System.Web.Security.SqlRoleProvider"/>
        </providers>
    </roleManager>
    <!-- Authentication -->
    <anonymousIdentification enabled="false"/>
    <authentication mode="Forms">
        <forms name=".AUTH" protection="All" timeout="2" path="/">
        </forms>
    </authentication>
    <httpModules>
        <add name="CompressionModule" type="Utility.HttpCompressionModule"/>
    </httpModules>
</system.web>

Больше сказать нечего.Нет других нестандартных вещей, которые мы делаем с сайтом, о которых я знаю.Есть идеи?

Ответы [ 6 ]

10 голосов
/ 31 августа 2012

Начиная с IIS 7.5, заголовок Vary перезаписывается фильтром IIS gzip (gzip.dll), который реализует модуль DyanamicCompressionModule.Фильтр всегда устанавливает заголовок «Vary: Accept-Encoding», независимо от изменений, внесенных в код ASP.NET.На сегодняшний день единственный обходной путь - отключить сжатие для динамического содержимого и затем реализовать его в коде.Вот как:

  1. Удалите следующую строку из Web.config:

    <add name="CompressionModule" type="Utility.HttpCompressionModule"/>

  2. Затем перейдите к управлению IISконсоль и убедитесь, что сжатие не включено для динамического содержимого.

  3. Реализация сжатия вручную в Global.asax.cs, метод HttpApplication.Application_BeginRequest:

   protected void Application_BeginRequest(object sender, EventArgs e)
   {
       HttpContext context = HttpContext.Current;
       context.Response.Filter 
          = new GZipStream(context.Response.Filter, CompressionMode.Compress);
       context.Response.AppendHeader("Content-Encoding", "gzip");
       context.Response.Cache.VaryByHeaders["Accept-Encoding"] = true;

       // We can now set additional Vary headers...
       context.Response.Cache.VaryByHeaders.UserAgent = true;
       context.Response.Cache.VaryByHeaders["X-Requested-With"] = true;
   }

Эта проблема была недавно сообщили в Microsoft .

4 голосов
/ 24 октября 2011

Попробуйте:

Response.Cache.SetVaryByCustom("Accept-Encoding");
Response.Cache.SetOmitVaryStar(true);

Поскольку в приведенном выше тексте что-то убедило код в том, что * здесь уместно, и вы уверены, что это не так.

Обратите внимание, что в IE есть недостаток в механизме кэширования (частично исправлен, IIRC в IE9, но не раньше), когда требуется такой заголовок варьирования, чтобы указать полную неспособность кешировать (это допустимо, но неоптимальное поведение). По этой причине некоторые люди предпочитают варьироваться в зависимости от User-Agent, поскольку Accept-Encoding является технически правильным значением для отправки:

  1. IE будет кешировать это правильно.
  2. Пользовательский агент, который изменяется в том, какой заголовок Accept-Encoding отправляет, возможен, но маловероятен в том, что встретится в поле.

Примечание: Вы также должны убедиться, что отправляете разные E-теги с разными версиями (g-zip, дефлированные и несжатые) в качестве объектов меток E-Tags (набор байтов, включая любое кодирование контента, но не включая кодирование транспорта), а не ресурсы, и не изменение E-тега может привести к тому же виду SNAFU, который вы устанавливаете Vary: Accept-Encoding, чтобы избежать.

Если вы не можете изменить E-Tag, вам лучше не указывать его и использовать только последний измененный для условных GET.


Кстати, ответ на интересующий вас вопрос о том, почему установлен application / x-javascript, заключается в том, что это тип контента, который сервер ассоциирует с «.js». Подсказка в HTML не повлияет на это, так как это не то, что сервер видит для этого запроса, хотя он может использоваться для переопределения типа контента, если что-то поддельное, как text / plain, было отправлено сервером (стандарты неясны, и поведение агента пользователя может отличаться, оставаясь в пределах буквы стандарта). Поскольку все браузеры обычно рассматривают application / javascript application / x-javascript и text / javascript как означающие javascript, здесь это не имеет большого значения.

2 голосов
/ 02 мая 2012

Вам нужно вручную добавить значение заголовка, чтобы увидеть Vary: Accept-Encoding.

response.Cache.SetOmitVaryStar(true);
response.AddHeader("Vary", "Accept-Encoding");
2 голосов
/ 21 октября 2011

Попробуйте это:

app.Response.Cache.SetVaryByCustom("Accept-Encoding");
1 голос
/ 29 октября 2014

Как уже упоминалось, это известная проблема с модулем сжатия IIS, перезаписывающим заголовок Vary специально, и устранена hotfix .

Если вы не можете убедиться, что исправление установлено, тогда еще один обходной путь - добавить заголовок, используя IIS URL Rewrite в вашем файле Web.config:

<configuration>
  <system.webServer>
    <rewrite>
      <outboundRules>
        <rule name="Append 'Vary: X-Requested-With' header" patternSyntax="ECMAScript">
          <match serverVariable="RESPONSE_VARY" pattern=".+" />
          <action type="Rewrite" value="{R:0}, X-Requested-With" replace="true" />
        </rule>
        <rule name="Set 'Vary: X-Requested-With' header if no others" patternSyntax="ECMAScript">
          <match serverVariable="RESPONSE_VARY" pattern=".+" negate="true" />
          <action type="Rewrite" value="X-Requested-With" />
        </rule>
      </outboundRules>
    </rewrite>
  </system.webServer>
</configuration>
1 голос
/ 20 августа 2013

Я тоже пробовал эти

Response.Cache.SetOmitVaryStar(true);
Response.AddHeader("Vary", "Accept-Encoding");

, но не сработало .. Однако это сработало:

Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...