在IIS杠杆浏览器缓存(谷歌的PageSpeed​​问题) [英] Leverage browser caching in IIS (google pagespeed issue)

查看:560
本文介绍了在IIS杠杆浏览器缓存(谷歌的PageSpeed​​问题)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有关于利用浏览器缓存的几个问题,但我没有发现任何有关如何做到这一点在ASP.NET应用程序非常有用。谷歌的的PageSpeed​​告诉这是性能最大的问题。
到目前为止,我在这做了我的的web.config 的:

There are several questions about leveraging browser caching but I didn't find anything useful for how to do this in an ASP.NET application. Google's Pagespeed tells this is performance biggest problem. So far I did this in my web.config:

<system.webServer>
  <staticContent>
    <!--<clientCache cacheControlMode="UseExpires"
            httpExpires="Fri, 24 Jan 2014 03:14:07 GMT" /> -->
    <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.24:00:00" />
  </staticContent>
</system.webServer>

评论code ++工程。我可以设置过期头是在未来的某个特定的时间,但我不能够设置 cacheControlMaxAge 设置多少天从现在静态内容将被缓存。这是行不通的。我的问题是:

Commented code works. I can set expire header to be some particular time in future but I was not able to set cacheControlMaxAge to set how many days from now static content would be cached. It does not work. My questions is:

我怎样才能做到这一点?
我知道这是可能只为这将是很好的解决特定的文件夹设置缓存,但它是不是也工作。应用程序在Windows Server 2012上承载,在IIS8,应用程序池设置为经典。

How can I do that? I know it is possible to set caching only for specific folder which would be good solution, but it isn't working also. Application is hosted on Windows Server 2012,on IIS8, application pool is set to classic.

在我设置web配置此code我得到的PageSpeed​​ 72(前71)。 50文件没有缓存。 (现在49)我不知道为什么,我只是意识到了一个文件,实际上缓存(SVG文件)。不幸的是PNG和JPG文件则没有。
这是我的的web.config

After I set this code in web config I got pagespeed of 72 (before was 71). 50 files were not cached. (Now 49) I was wondering why and I just realized that one file was actually cached (svg file). Unfortunately png and jpg file were not. This is my web.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>
  <configSections>
    <section name="exceptionManagement" type="Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManagerSectionHandler,Microsoft.ApplicationBlocks.ExceptionManagement" />
    <section name="jsonSerialization"     type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions,   Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E34" requirePermission="false" allowDefinition="Everywhere" />
    <sectionGroup name="elmah">
      <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"    />
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah"    />
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
      <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
    </sectionGroup>
  </configSections>

  <exceptionManagement mode="off">
    <publisher mode="off" assembly="Exception"  type="blabla.ExceptionHandler.ExceptionDBPublisher"  connString="server=188......;database=blabla;uid=blabla;pwd=blabla; " />
  </exceptionManagement>
  <location path="." inheritInChildApplications="false">
    <system.web>
      <httpHandlers>
        <add verb="GET,HEAD" path="ScriptResource.axd"  type="System.Web.Handlers.ScriptResourceHandler,System.Web.Extensions, Version=1.0.61025.0,  Culture=neutral, PublicKeyToken=31bf3856ad364e34" validate="false" />
        <add verb="GET" path="Image.ashx" type="blabla.WebComponents.ImageHandler, blabla/>"
        <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
        <add verb="*" path="*.jpg" type="System.Web.StaticFileHandler" />
        <add verb="GET" path="*.js" type="System.Web.StaticFileHandler" />
        <add verb="*" path="*.gif" type="System.Web.StaticFileHandler" />
        <add verb="GET" path="*.css" type="System.Web.StaticFileHandler" />
      </httpHandlers>
      <compilation defaultLanguage="c#" targetFramework="4.5.1" />
      <trace enabled="false" requestLimit="100" pageOutput="true" traceMode="SortByTime" localOnly="true"/>
      <authentication mode="Forms">
        <forms loginUrl="~/user/login.aspx">
          <credentials passwordFormat="Clear">
            <user name="blabla" password="blabla" />
          </credentials>
        </forms>
      </authentication>
      <authorization>
        <allow users="*" />
      </authorization>
      <sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" timeout="20" />
      <globalization requestEncoding="utf-8" responseEncoding="utf-8" culture="en-GB" uiCulture="en-GB" />
      <xhtmlConformance mode="Transitional" />
      <pages controlRenderingCompatibilityVersion="4.5" clientIDMode="AutoID">
        <namespaces>

        </namespaces>
        <controls>
          <add assembly="Microsoft.AspNet.Web.Optimization.WebForms" namespace="Microsoft.AspNet.Web.Optimization.WebForms" tagPrefix="webopt" />
        </controls>
      </pages>
      <webServices>
        <protocols>
          <add name="HttpGet" />
          <add name="HttpPost" />
        </protocols>
      </webServices>
    </system.web>
  </location>
  <appSettings>

  </appSettings>
  <connectionStrings>

  </connectionStrings>
  <system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization maxJsonLength="200000" />
      </webServices>
    </scripting>
  </system.web.extensions>
  <startup>
    <supportedRuntime version="v2.0.50727" />
    <supportedRuntime version="v1.1.4122" />
    <supportedRuntime version="v1.0.3705" />
  </startup>
  <system.webServer>


    <rewrite>
      <providers>
        <provider name="ReplacingProvider" type="ReplacingProvider, ReplacingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5ab632b1f332b247">
          <settings>
            <add key="OldChar" value="_" />
            <add key="NewChar" value="-" />
          </settings>
        </provider>
        <provider name="FileMap" type="DbProvider, Microsoft.Web.Iis.Rewrite.Providers, Version=7.1.761.0, Culture=neutral, PublicKeyToken=0525b0627da60a5e">
          <settings>
            <add key="ConnectionString" value="server=;database=blabla;uid=blabla;pwd=blabla;App=blabla"/>
            <add key="StoredProcedure" value="Search.GetRewriteUrl"/>
            <add key="CacheMinutesInterval" value="0"/>
          </settings>
        </provider>
      </providers>
      <rewriteMaps configSource="rewritemaps.config" />
      <rules configSource="rewriterules.config" />
    </rewrite>
    <modules>
      <remove name="ScriptModule" />
      <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3456AD264E35" />
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
    </modules>
    <handlers>
      <add name="Web-JPG" path="*.jpg" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
      <add name="Web-CSS" path="*.css" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
      <add name="Web-GIF" path="*.gif" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
      <add name="Web-JS" path="*.js" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
    </handlers>
    <validation validateIntegratedModeConfiguration="false" />
    <httpErrors errorMode="DetailedLocalOnly" existingResponse="Auto">
      <remove statusCode="404" subStatusCode="-1"/>
      <remove statusCode="500" subStatusCode="-1"/>
      <error statusCode="404" path="error404.htm" responseMode="File"/>
      <error statusCode="500" path="error.htm" responseMode="File"/>
    </httpErrors>
  </system.webServer>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="soapBinding_AdriagateService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true" messageEncoding="Text">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
          <security mode="None" />
        </binding>
      </basicHttpBinding>
      <netTcpBinding>
        <binding name="NetTcpBinding_ITravellerService" closeTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="blabla" bindingConfiguration="soapBinding_blabla" contract="" Address="blabla" name="blabla" />
        <endpoint address="blabla" binding="basicHttpBinding" bindingConfiguration="soapBinding_IImagesService"
          contract="ImagesService.IImagesService" name="soapBinding_IImagesService"/>
        <identity>
          <servicePrincipalName value="blabla"/>
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <system.web>
    <httpModules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
    </httpModules>
  </system.web>
  <elmah>
    <security allowRemoteAccess="false" />
  </elmah>
  <location path="elmah.axd" inheritInChildApplications="false">
    <system.web>
      <httpHandlers>
        <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
      </httpHandlers>

    </system.web>
    <system.webServer>
      <handlers>
        <add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
      </handlers>
    </system.webServer>
  </location>
</configuration>

编辑:
如果我设定确切的截止日期,缓存是工作,而不是JPG,GIF ....只为PNG

If I set exact expiry date, caching is working, but not for jpg,gif....only for png

EDIT2:
如果我设置 cacheControlCustom =公喜欢这里:

<clientCache cacheControlCustom="public" 
cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" /> 

缓存工作,但还是不是为JPEG图片和GIF;它仅适用于svgs和PNG格式。

caching is working but still not for jpegs and gifs; it only works for svgs and pngs.

推荐答案

大多数浏览器缓存问题,可以通过查看响应头(可在谷歌Chrome开发者工具来完成)。解决

Most of the browser caching issues can be resolved by viewing the response headers (can be done in Google chrome developer tools).

现在你的的web.config 文件中的 clientCache 部分应该你的输出缓存设置为最大年龄为你看到下面的图片已经设置了最大年龄 86400 为1天秒。

Now the clientCache section of your web.config file should set your output caching to a maximum age as you see in the image below has set the max-age to 86400 which is 1 day in seconds.

下面是此设置的Web.config片段。

Here is the web.config snippet for this setup.

<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00" />

现在那大,响应报头有一个最大年龄财产上的的Cache-Control 头设置。所以浏览器的的被缓存的内容。嗯,这主要是真实的,但一些浏览器需要另一个标志被设置。特别是公共标志的缓存控制头设置。这可以通过在的web.config 使用 cacheControlCustom 属性很容易地添加。下面是一个例子。

Now thats great, the response header has a max-age property set on the Cache-Control header. So the browser should be caching the content. Well this is mostly true but some browsers require another flag to be set. Specifically the public flag set for the cache control header. This can be easily added by using the cacheControlCustom attribute in the web.config. Here is an example.

<clientCache cacheControlCustom="public" cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00" />

现在,当我们重试页面,检查头。

Now when we retry the page and inspect the headers.

现在,你可以从图像见上我们现在有值公开,最大年龄= 86400 。因此,我们的浏览器拥有所有需要缓存的资源。现在,检查头和谷歌浏览器的网络选项卡将帮助我们。

Now as you can see from the image above we now have the value public, max-age=86400. So our browser has all it needs to cache the resources. Now examining the headers and the network tab of google chrome will help us.

下面是该文件的第一个请求。注意该文件没有缓存...

Here is the first request to the file.. Note the file is not cached...

现在让导航回到此页面(注意:不要刷新页面,我们会谈论在第二个)。您将看到现在从缓存中返回响应(如圈)。

Now lets navigate back to this page (NOTE: do not refresh the page, we will talk about that in a second). You will see the response in now returning from cache (as circled).

如果我使用任何<大骨节病> F5 或使用浏览器刷新功能刷新页面现在会发生什么。等待..哪里的(从缓存)去。

Now what happens if I refresh the page using either F5 or using the browser refresh feature. Wait.. where did the (from cache) go.

那么使用刷新按钮会不管缓存头的重新下载静态资源谷歌浏览器(不知道其他浏览器)(在此处插入说明,请的)。这意味着资源已被重新取回,最大年龄头送了过来。

Well in Google Chrome (not sure about other browsers) using the refresh button will re-download the static resources regardless of the cache header (insert clarification here please). That means that the resources has been re-retrieved and the max age header sent over.

现在最重要的解释后,一定要测试如何您正在监视的缓存头。

Now after all the explanation above, be sure to test how you are monitoring the cache headers.

更新

键入图像/ JPG格式的。现在,你可能期望的默认行为将缓存此处理程序。但是看到IIS扩展 ashx的(正确)作为一个动态脚本,不受缓存没有明确设定在$ C $的缓存头C本身。

Based on your comments you stated you have a generic handler (IHttpHandler) named Image.ashx with the content type of image/jpg. Now you may expect the default behaviour would be to cache this handler. However IIS sees the extension .ashx (correctly) as a dynamic script and is not subject to caching without explicitly setting the cache headers in the code itself.

现在这是你需要小心,因为通常 IHttpHandlers 应该INFACT不被缓存,因为他们通常提供动态内容。现在,如果内容是不太可能改变,你可以直接在code设置缓存头。这里是 IHttpHandlers 使用响应背景设置缓存头的一个例子。

Now this is where you need to be careful, as typically IHttpHandlers should infact not be cached as they usually deliver dynamic content. Now if that content is unlikely to change you could set your cache headers directly in the code. Here is an example of setting cache headers in IHttpHandlers using the Response context.

context.Response.ContentType = "image/jpg";

context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1));
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetSlidingExpiration(true);

context.Response.TransmitFile(context.Server.MapPath("~/out.jpg"));

现在看code我们设置在的 Cache属性的一些属性。为了得到我所设置的属性所需的响应。

Now looking at the code we are setting a few properties on the Cache property. To get the desired response I have set the properties.


  • context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); 讲述了把高速缓存设置最大年龄= 的Cache-Control 头的一部分是 1 有朝一日(86400秒)。

  • context.Response.Cache.SetCacheability(HttpCacheability.Public); 讲述了把高速缓存设置的Cache-Control 公共。这是很重要的,因为它告诉浏览器缓存对象。

  • context.Response.Cache.SetSlidingExpiration(真); 告诉输出缓存,以确保它设置最大年龄= 的Cache-Control 头正常。如果不设置滑动期满时,IIS OUT输出缓存会忽略最大年龄头。把此一并给了我这样的结果。

  • context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); tells the out put cache to set the max-age= part of the Cache-Control header to be 1 day in the future (86400 seconds).
  • context.Response.Cache.SetCacheability(HttpCacheability.Public); tells the out put cache to set the Cache-Control header to public. This is quite important as it tells the browser to cache to object.
  • context.Response.Cache.SetSlidingExpiration(true); tells the output cache to ensure it is setting the max-age= part of the Cache-Control header properly. Without setting the sliding expiration, the IIS out put caching will ignore the max age header. Putting this together gives me this result.

正如我上面说你可能不希望缓存 ashx的文件,因为它们通常提供动态内容。然而,如果动态内容不太可能在一定时期内改变你可以使用上述方法来提供你的 ashx的文件。

As I stated above you may not want to cache the .ashx files as they typically deliver dynamic content. However if that dynamic content is not likely to change in a given period you can use the methods above to deliver your .ashx file.

现在在上面,你也可以设置在 ETag的上市进程结合 (见高速缓存头维基)组件,这样浏览器就可以核实相关内容通过自定义字符串被交付。维基状态:

Now in conjunction with the process listed above you could also set the ETag (see wiki) component of the cache headers so the browser can verify the content being delivered by a custom string. The wiki states:

这是ETag的是由网络服务器分配给一个特定的不透明标识符
  资源的版本,在URL中找到。如果在该资源的内容
  网址不断变化,一个新的和不同的的ETag 分配。

An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. If the resource content at that URL ever changes, a new and different ETag is assigned.

所以这真的是某种类型的唯一标识为浏览器标识内容在响应交付。通过提供这个标题中的浏览器上的下一个重装会​​如果无 - 匹配头从传送过来与的ETag 最后的响应。我们可以修改我们的处理程序,以检测如果无 - 匹配头,并将其与我们自己产生的 Etag的。现在还没有确切的科学产生的ETag ,但一个好的经验法则是提供一个标识符,将最有可能只定义一个实体。在这种情况下,我喜欢使用连接在一起,如两个字符串。

So this is really some sort of unique identification for the browser to identify the content being delivered in the response. By providing this header the browser on the next reload will send over a If-None-Match header with the ETag from the last response. We can modify our handler to detect the If-None-Match header and compare it to our own generated Etag. Now there is no exact science to generating ETags but a good rule of thumb is to deliver an identifier that will most likely define only one entity. In this case I like to use two strings concatenated together such as.

System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/saveNew.png"));
string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString();

在上面,我们从我们的文件系统加载文件的片段(您可以从任何地方得到这个)。我然后使用 GetHash code()方法(在所有对象)来获取对象的整数哈希code。在这个例子中我Concat的文件名,那么最后写日期的哈希值。究其原因,最后写日期的情况下,该文件被改变了哈希code变更为好,从而使的指纹的不同。

In the snippet above we are loading a file from our file system (you could get this from anywhere). I am then using the GetHashCode() method (on all objects) to get the integer hash code of the object. In the example I concat the hash of the file name, then the last write date. The reason for the last write date is in case the file is changed the hash code is changed as well, thus making the finger prints different.

这将生成类似于 306894467-210133036 散code。

This will generate a hash code similar to 306894467-210133036.

那么,我们如何在我们的处理程序使用。下面是处理程序的最新修改版本。

So how do we use this in our handler. Below is the newly modified version of the handler.

System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/out.png"));
string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString();
var browserETag = context.Request.Headers["If-None-Match"];

context.Response.ClearHeaders();
if(browserETag == eTag)
{
    context.Response.Status = "304 Not Modified";
    context.Response.End();
    return;
}
context.Response.ContentType = "image/jpg";
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1));
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetSlidingExpiration(true);
context.Response.Cache.SetETag(eTag);
context.Response.TransmitFile(file.FullName);

正如你所看到的,我已经改变了不少很多的处理程序,但是你会发现,我们生成 Etag的散,检查传入如果 - 无 - 匹配头。如果ETAG哈希和头都相等,则我们告诉内容处理不当通过返回的状态code 304未修改更改了浏览器

As you can see, I have changed quite alot of the handler however you will notice that we generate the Etag hash, check for an incoming If-None-Match header. If the etag hash and the header are equal then we tell the browser that the content hasnt changed by returning the status code 304 Not Modified.

接下来是相同的处理,除了我们通过调用添加的ETag 标题:

Next was the same handler except we add the ETag header by calling:

context.Response.Cache.SetETag(eTag);

当我们在浏览器中运行这件事我们得到的。

When we run this up in the browser we get.

您会从图像看到(我没有更改文件名),我们现在有我们的缓存系统到位的所有组件。在的ETag 正在交付一个头,浏览器发送请求头如果无 - 匹配所以我们处理程序可以做出相应的反应来改变缓存文件。

You will see from the image (as i did change the file name) that we now have all the components of our cache system in place. The ETag is being delivered as a header, and the browser is sending the request header If-None-Match so our handler can respond accordingly to cache file changed.

这篇关于在IIS杠杆浏览器缓存(谷歌的PageSpeed​​问题)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆