[翻译]  Unable to append 'Vary' header to response

[CHINESE]  无法在响应中添加“Vary”标头


I'm trying to add a Vary: Accept-Encoding header to the response to files that I compress, as advised earlier.

我正在尝试添加一个Vary:Accept-Encoding标头来响应我压缩的文件,如前所述。

However, for some reason this is not possible - either from the Visual Studio test server, or an IIS server.

但是,由于某种原因,无法从Visual Studio测试服务器或IIS服务器进行此操作。

I have the following code:

我有以下代码:

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

    }
}

this results in the following response header:

这导致以下响应头:

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

As you can see, the Vary header is not present. Meaningless headers with similar syntax are present, so there must be something, somewhere, taking out the Vary header before it is sent.

如您所见,Vary标头不存在。存在具有类似语法的无意义标头,因此在发送之前必须有某些东西取出Vary标头。

I don't know if it is relevant, but here is where I define my compression module in web.config:

我不知道它是否相关,但这里是我在web.config中定义压缩模块的地方:

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

(Where Utility.HttpCompressionModule is the class which the code excerpt I provided above belongs to.)

(其中Utility.HttpCompressionModule是我上面提供的代码摘录所属的类。)

Why is it that I can't add the Vary header?

为什么我不能添加Vary标头?

EDIT: Eric C's solution has left me with code like this:

编辑:Eric C的解决方案给我留下了这样的代码:

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

    }

But, the headers look like this:

但是,标题看起来像这样:

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

(No idea why this is application/x-javascript as its set in the HTML as text/javascript, but this is irrelevant.)

(不知道为什么这是application / x-javascript,因为它在HTML中设置为text / javascript,但这是无关紧要的。)

As you can see, I now have a vary header, but it is set as Vary=* rather than Vary=Accept-Encoding, as you would expect from my code in the compression module.

正如您所看到的,我现在有一个vary头,但它设置为Vary = *而不是Vary = Accept-Encoding,正如您对压缩模块中的代码所期望的那样。

What is going on here? How do I get the Vary header set correctly?

这里发生了什么?如何正确设置Vary标头?

Second Edit: I'm going to paste the source code for the whole class. There isn't much more to it than I have already posted, but it might help to give a grasp of exactly what I'm doing:

第二次编辑:我要粘贴整个类的源代码。它没有比我已发布的更多,但它可能有助于准确掌握我正在做的事情:

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

Further, here is the System.Web section of my web.config file:

此外,这是我的web.config文件的System.Web部分:

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

There isn't much more to say. There is no other non-standard stuff that we are doing with the site, that I know about. Any ideas?

没有更多的话要说。我所知道的,我们没有其他非标准的东西与网站有关。有任何想法吗?

6 个解决方案

#1


1  

As mentioned by others, this is a known issue with the IIS compression module overwriting the Vary header specifically and has been addressed by hotfix.

正如其他人所提到的,这是一个已知的问题,IIS压缩模块专门覆盖了Vary头,并且已由修补程序解决。

If you can't ensure the hotfix is installed then another workaround is to append the header using IIS URL Rewrite in your Web.config:

如果您无法确保安装了此修补程序,则另一种解决方法是在Web.config中使用IIS URL Rewrite附加标头:

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

#2


10  

As of IIS 7.5, the Vary header is overwritten by the gzip IIS filter (gzip.dll), that implements the DyanamicCompressionModule. The filter always sets the header to "Vary: Accept-Encoding", regardless of changes made in ASP.NET code. As of today, the only workaround is to disable compression for dynamic content and then implement it in code. Here's how:

从IIS 7.5开始,Vary头被gzip IIS过滤器(gzip.dll)覆盖,它实现了DyanamicCompressionModule。无论ASP.NET代码中的更改如何,过滤器始终将标头设置为“Vary:Accept-Encoding”。截至今天,唯一的解决方法是禁用动态内容的压缩,然后在代码中实现它。就是这样:

  1. Remove the following line from Web.config:

    从Web.config中删除以下行:

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

  2. Then go to IIS management console and make sure compression is not enabled for dynamic content.

    然后转到IIS管理控制台并确保未对动态内容启用压缩。

  3. Implement compression manually in Global.asax.cs, method HttpApplication.Application_BeginRequest:

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

This is an issue that was recently reported to Microsoft.

这是最近向Microsoft报告的问题。

#3


4  

Try:

尝试:

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

Since it seems in the above that something has convinced the code that * is appropriate here, and you're certain it isn't.

因为在上面看来某些东西使代码确信*在这里是合适的,而你确定它不是。

Note that with IE there's a flaw in the caching mechanism (partially fixed, IIRC with IE9 but not before) where it takes such a vary header to indicate total inability to cache (this is valid, but sub-optimal behaviour). For this reason, some people prefer to vary by User-Agent because while Accept-Encoding is the technically correct value to send:

请注意,使用IE时,缓存机制中存在一个缺陷(部分修复,IIRC与IE9但之前没有),它需要这样一个变化的标头来表示完全无法缓存(这是有效的,但是次优的行为)。出于这个原因,有些人更喜欢因User-Agent而异,因为Accept-Encoding是技术上正确的发送值:

  1. IE will cache this correctly.
  2. IE将正确缓存。
  3. A User-Agent that varies in what Accept-Encoding header it sends is possible, but unlikely in what will be encountered in the field.
  4. User-Agent在它发送的Accept-Encoding标头中有所不同,但在该字段中遇到的内容不太可能。

Note: You must also make sure that you send different E-Tags with the different versions (g-zipped, deflated and uncompressed) as E-Tags label entities (the bunch of bytes sent, including any Content-Encoding but not including any Transport-Encoding) rather than resources, and not varying the E-Tag can result in much the same sort of SNAFU that you are setting Vary: Accept-Encoding to avoid.

注意:您还必须确保使用不同版本(g-zipped,deflated和uncompressed)发送不同的E-Tags作为E-Tags标签实体(发送的一堆字节,包括任何Content-Encoding但不包括任何Transport -Encoding)而不是资源,而不是改变E-Tag可能会导致你设置的SNAFU与Vary相同:接受编码以避免。

If you can't make it vary the E-Tag, then you'd be better off omitting it and using last-modified alone for conditional GETs.

如果你不能让它改变电子标签,那么你最好省略它并使用最后修改单独用于条件GET。


Incidentally, the answer to your wondering about why application/x-javascript is set, is that that is the content-type the server is associating with ".js". The hint in the HTML won't affect that as it isn't something the server sees for this request, though it could be used to over-ride the content-type if something bogus like text/plain was sent by the server (the standards are unclear and user-agent behaviour could vary while remaining within the letter of the standard). Since browsers generally all consider application/javascript application/x-javascript and text/javascript to mean javascript, it matters little here.

顺便提一下,你对设置application / x-javascript的原因感到疑惑的答案就是服务器与“.js”关联的内容类型。 HTML中的提示不会影响它,因为它不是服务器对此请求所看到的内容,但如果服务器发送了像text / plain这样的虚假内容,它可以用来覆盖内容类型(标准不清楚,用户代理行为可能会有所不同,同时保持在标准的字母内)。由于浏览器通常都认为应用程序/ javascript应用程序/ x-javascript和text / javascript意味着javascript,因此这里很重要。

#4


2  

Try this:

尝试这个:

app.Response.Cache.SetVaryByCustom("Accept-Encoding");

#5


2  

You need to manually add the header value to see Vary: Accept-Encoding.

您需要手动添加标头值以查看Vary:Accept-Encoding。

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

#6


1  

I also tried these

我也试过这些

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

, but wouldn't work.. This, however, did work:

,但是不行。但是,这确实有效:

Response.Cache.VaryByHeaders["Accept-Encoding"] = true;

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2018 ITdaan.com 粤ICP备14056181号