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

时间:2011-10-21 12:29:31

标签: c# asp.net http-headers

我正在尝试为我压缩的文件as advised earlier添加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标题?

编辑: 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");

    }

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

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,但这是无关紧要的。)

正如您所看到的,我现在有一个不同的标头,但它设置为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");
            }
        }
    }
}

此外,这是我的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>

没有更多要说的了。我所知道的,没有其他非标准的东西我们正在使用该网站。有什么想法吗?

6 个答案:

答案 0 :(得分:10)

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

  1. 从Web.config中删除以下行:

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

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

  3. Global.asax.cs中手动实施压缩,方法HttpApplication.Application_BeginRequest

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

    这是最近reported to Microsoft的问题。

答案 1 :(得分:4)

尝试:

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

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

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

  1. IE会正确缓存此内容。
  2. 用户代理在发送的Accept-Encoding标头中有所不同,但在该字段中遇到的内容不太可能。
  3. 注意:您还必须确保使用不同版本(g-zipped,deflated和uncompressed)发送不同的E-Tags作为E-Tags标签实体(发送的字节串,包括任何内容编码但不包括任何传输编码而不是资源,并且不改变电子标签可能会导致您设置Vary: Accept-Encoding以避免使用相同类型的SNAFU。

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


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

答案 2 :(得分:2)

试试这个:

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

答案 3 :(得分:2)

您需要手动添加标头值才能看到Vary: Accept-Encoding

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

答案 4 :(得分:1)

我也试过这些

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

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

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

答案 5 :(得分:1)

正如其他人所提到的,这是一个known issue,其中IIS压缩模块专门覆盖了Vary标头,并由hotfix解决。

如果您无法确保安装了此修补程序,则另一种解决方法是在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>
相关问题