MVC 之 缓存机制(一)

  一、概述

  缓存是将信息(数据或页面)放在内存中以避免频繁的数据库存储或执行整个页面的生命周期,直到缓存的信息过期或依赖变更才再次从数据库中读取数据或重新执行页面的生命周期。在系统优化过程中,缓存是比较普遍的优化做法和见效比较快的做法。
  MVC缓存本质上还是.NET的一套缓存体系,只不过该缓存体系应用在了MVC框架上。

  缓存是一种以空间换时间的技术, 比如, CPU的二级缓存,Windows的文件缓存。减少服务器的负荷,默认存放在内存里面,不过是可以修改的。缓存存在失效的情况。Asp.net 缓存主要分为页面缓存,数据源缓存,数据缓存。

  缓存代码的位置:

  [1] 页面缓存: <%@OutPutCache Duration="15" VaryByParam="none" %>

  [2] Contorller 缓存: [OutputCache(Duration = 10)]

  [3] Action 缓存: [OutputCache(Duration = 10)]

 

  缓存的常用场景:

  数据被频繁的使用,并且很少发生变化或对即时性的要求不高。

  二、Control 缓存

  Control 缓存即是把缓存应用到整个Control 上,该Control 下的所有Action都会被缓存起来。Control 缓存的粒度比较粗,应用也比较少些。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcCache.Control.Controllers
{
    [OutputCache(Duration = 10)]
    public class ControlController : Controller
    {
        // GET: /Home/
        public ActionResult Index()
        {
            ViewBag.CurrentTime = System.DateTime.Now;
            return View();
        }

        public ActionResult Index1()
        {
            ViewBag.CurrentTime = System.DateTime.Now;
            return View();
        }
    }
}

  在名为Control的Control中加入了OutputCache,并设置持续时间为10秒(Duration=10),即每10秒后过期当再次触发时更新缓存。

  三、Action 缓存

  即把缓存用到Action上,Action缓存为比较常用的缓存方式,该方式粒度细一些。使用方法类似Control缓存。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcCache.Control.Controllers
{
    //Control不加缓存
    public class ActionController : Controller
    {
        //该Index的Action加缓存
        [OutputCache(Duration = 10)]
        public ActionResult Index()
        {
            ViewBag.CurrentTime = System.DateTime.Now;
            return View();
        }

        //该Action不加缓存
        public ActionResult Index1()
        {
            ViewBag.CurrentTime = System.DateTime.Now;
            return View();
        }
} }

  四、使用配置文件

  当我们需要将N个Control或Action加入缓存,并且缓存的参数是一致的情况下,我们可以把相关的设置放到Web.config中,并在程序中加入相应的配置。同时,在配置文件中配置缓存参数将更加灵活方便。

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <!--配置缓存-->
    <caching>
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="TestConfigCache" duration="10"/>
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>
    <!--配置缓存-->
    <httpRuntime targetFramework="4.5" />
    <compilation debug="true" targetFramework="4.5" />
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />

    <handlers>
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>

</configuration>

  配置缓存节只需要将其放在system.web节下即可,以下是使用的方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcCache.Control.Controllers
{
    public class ConfigController : Controller
    {
        //TestConfigCache为在配置文件中配置的缓存节
        [OutputCache(CacheProfile = "TestConfigCache")]
        public ActionResult Index()
        {
            ViewBag.CurrentTime = System.DateTime.Now;
            return View();
        }
}
}

  注:当Control与Action都应用了缓存时,以Action的缓存为主。

  五、OutputCache 常用参数

  下面代码为mvc4的OutputCache的定义,由于使用的是英文版本IDE和框架,因此注释全部为英文。后面的讲解主要讲解常用的属性,此处不包含对于缓存依赖的讲解使用方法。

  各个属性的详细说明及使用请查阅MSDN,链接地址如下:https://msdn.microsoft.com/zh-cn/library/system.web.mvc.outputcacheattribute.aspx

using System;
using System.Web.UI;

namespace System.Web.Mvc
{
    // Summary:
    //     Represents an attribute that is used to mark an action method whose output
    //     will be cached.
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class OutputCacheAttribute : ActionFilterAttribute, IExceptionFilter
    {
        // Summary:
        //     Initializes a new instance of the System.Web.Mvc.OutputCacheAttribute class.
        public OutputCacheAttribute();

        // Summary:
        //     Gets or sets the cache profile name.
        //
        // Returns:
        //     The cache profile name.
        public string CacheProfile { get; set; }
        //
        // Summary:
        //     Gets or sets the child action cache.
        //
        // Returns:
        //     The child action cache.
        public static System.Runtime.Caching.ObjectCache ChildActionCache { get; set; }
        //
        // Summary:
        //     Gets or sets the cache duration, in seconds.
        //
        // Returns:
        //     The cache duration.
        public int Duration { get; set; }
        //
        // Summary:
        //     Gets or sets the location.
        //
        // Returns:
        //     The location.
        public OutputCacheLocation Location { get; set; }
        //
        // Summary:
        //     Gets or sets a value that indicates whether to store the cache.
        //
        // Returns:
        //     true if the cache should be stored; otherwise, false.
        public bool NoStore { get; set; }
        //
        // Summary:
        //     Gets or sets the SQL dependency.
        //
        // Returns:
        //     The SQL dependency.
        public string SqlDependency { get; set; }
        //
        // Summary:
        //     Gets or sets the vary-by-content encoding.
        //
        // Returns:
        //     The vary-by-content encoding.
        public string VaryByContentEncoding { get; set; }
        //
        // Summary:
        //     Gets or sets the vary-by-custom value.
        //
        // Returns:
        //     The vary-by-custom value.
        public string VaryByCustom { get; set; }
        //
        // Summary:
        //     Gets or sets the vary-by-header value.
        //
        // Returns:
        //     The vary-by-header value.
        public string VaryByHeader { get; set; }
        //
        // Summary:
        //     Gets or sets the vary-by-param value.
        //
        // Returns:
        //     The vary-by-param value.
        public string VaryByParam { get; set; }

        // Summary:
        //     Returns a value that indicates whether a child action cache is active.
        //
        // Parameters:
        //   controllerContext:
        //     The controller context.
        //
        // Returns:
        //     true if the child action cache is active; otherwise, false.
        public static bool IsChildActionCacheActive(ControllerContext controllerContext);
        //
        // Summary:
        //     This method is an implementation of System.Web.Mvc.IActionFilter.OnActionExecuted(System.Web.Mvc.ActionExecutedContext)
        //     and supports the ASP.NET MVC infrastructure. It is not intended to be used
        //     directly from your code.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        public override void OnActionExecuted(ActionExecutedContext filterContext);
        //
        // Summary:
        //     This method is an implementation of System.Web.Mvc.IActionFilter.OnActionExecuting(System.Web.Mvc.ActionExecutingContext)
        //     and supports the ASP.NET MVC infrastructure. It is not intended to be used
        //     directly from your code.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        public override void OnActionExecuting(ActionExecutingContext filterContext);
        //
        // Summary:
        //     This method is an implementation of System.Web.Mvc.IExceptionFilter.OnException(System.Web.Mvc.ExceptionContext)
        //     and supports the ASP.NET MVC infrastructure. It is not intended to be used
        //     directly from your code.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        public void OnException(ExceptionContext filterContext);
        //
        // Summary:
        //     This method is an implementation of System.Web.Mvc.IResultFilter.OnResultExecuted(System.Web.Mvc.ResultExecutedContext)
        //     and supports the ASP.NET MVC infrastructure. It is not intended to be used
        //     directly from your code.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        public override void OnResultExecuted(ResultExecutedContext filterContext);
        //
        // Summary:
        //     Called before the action result executes.
        //
        // Parameters:
        //   filterContext:
        //     The filter context, which encapsulates information for using System.Web.Mvc.AuthorizeAttribute.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     The filterContext parameter is null.
        public override void OnResultExecuting(ResultExecutingContext filterContext);
    }
}
OutputCache

  常用属性:

  (1) AllowMultiple:获取或设置一个值,该值指示是否可指定筛选器特性的多个实例。
  (2) CacheProfile:缓存使用的配置文件的缓存名称,可选属性,默认值为空字符("")

  注意:包含在用户控件(.ascx 文件)中的 @ OutputCache 指令不支持此属性。在页中指定此属性时,属性值必须与 outputCacheSettings 节下面的 outputCacheProfiles 元素中的一个可用项的名称匹配。如果此名称与配置文件项不匹配,将引发异常。

  如果每个页面的缓存时间相同,则不需要每个页面设置,而是通过统一一个地方控制,这样就可以更好的统一控制所有页面的缓存时间。如果想改变缓存时间,只需要改一下web.config的配置信息即可,而不用每个页面去修改。 

  (3) ChildActionCache:获取或设置子操作缓存。

  (4) Duration:缓存时间,以秒为单位,这个除非你的Location=None,可以不添加此属性,其余时候都是必须的。

  (5) OutputCacheLocation:枚举类型,缓存的位置。当设置成None时,所有缓存将失效,默认为Any。

    [1] Any:页面被缓存在浏览器、代理服务器端和web服务器端;

    [2] Client:缓存在浏览器;

    [3] DownStream:页面被缓存在浏览器和任何的代理服务器端;

    [4] Server:页面被缓存在Web服务器端;

    [5] None:页面不缓存;

    [6] ServerAndClient:页面被缓存在浏览器和web服务器端;

  (6) VaryByContentEncoding:获取或设置基于内容变化的编码。一般设置为Accept-Encoding里面可能的Encoding名称。

  (7) VaryByCustom:获取或设置基于自定义项变化的值。一个完全可以定制的设置,例如我们可能需要根据用户角色来决定不同的缓存版本,或者根据浏览器的一些小版本号来区分不同的缓存版本,我们可以这样设置:VaryByCustom=”Role,BrowserVersion”,这些名称是自己定义的,光这样写当然是没有用的,我们还需要在Global.asax文件中,添加一个特殊的方法,来针对这种特殊的需求进行处理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
 
namespace MvcApplicationCacheSample
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
 
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );
 
        }
 
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
 
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }
 
        public override string GetVaryByCustomString(HttpContext context, string custom)
        {
            switch(custom)
            {
                case "Role":
                    {
                        return string.Join(",", Roles.GetRolesForUser());
                    }
                case "BrowserVersion":
                    {
                        return context.Request.Browser.Type;
                    }
 
                default:
                    break;
            }
            return string.Empty;
        }
    }
}

  (8) VaryByHeader:获取或设置基于标头变化的值。可以根据用户请求中所提供的一些Header信息不同而决定是否读取缓存。

    例如根据不同的语言,我们显然是有不同的版本的。或者根据用户浏览器不同,也可以缓存不同的版本。可以通过这样设置:

    VaryByHeader=”Accept-Language,User-Agent”

  (9) VaryByParam:用于多个输出缓存的字符串列表,并以“分号”进行分隔。默认时,该字符串与GET方法传递的参数或与POST方法传递的变量相对应。当被设置为多个参数时,输出缓存将会为每个参数都准备一个与之相对应的文档版本。可能值包括none,*,以及任何有效的查询串或POST参数名称。如果不想要为不同的已缓存内容指定参数,可以将其设置为none。如果想要指定所有的已缓存内容参数,可以设置为*。

<%@ OutputCache Duration="#ofseconds"
   Location="Any | Client | Downstream | Server | None | ServerAndClient "
   Shared="True | False"
   VaryByControl="controlname"
   VaryByCustom="browser | customstring"
   VaryByHeader="headers"
   VaryByParam="parametername"
   VaryByContentEncoding="encodings"
   CacheProfile="cache profile name | ''"
   NoStore="true | false"
   SqlDependency="database/table name pair | CommandNotification"
%>

  六、缓存依赖

  SqlDependency:一个值,用于标识操作的输出缓存所依赖的一组数据库名称和表名称对。SqlCacheDependency 类在所有受支持的 SQL Server 版本 (7.0, 2000, 2005) 上监视特定的 SQL Server 数据库表,数据库表发生更改时,将自动删除缓存项,并向 Cache 中添加新版本的项。

  下面为应用实例。示例说明:数据库为本地数据库,库名:wcfDemo,监听表名:user。缓存时间为:3600秒即一小时。数据库依赖周期为500毫秒,即每0.5秒监听下数据库是否有变化,如果有变化则立即更新缓存。

  1、建立Control,测试代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcCache.Control.Controllers
{
    public class SqlDependencyController : Controller
    {
        [OutputCache(CacheProfile = "SqlDependencyCache")]
        public ActionResult Index()
        {
            ViewBag.CurrentTime = System.DateTime.Now;
            return View();
        }
    }
}

  2、在配置文件中配置CacheProfile为SqlDependencyCache的节,并且配置缓存对数据库的依赖。

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <!--数据库连接字符串-->
  <connectionStrings>
    <add name="Conn" connectionString="server=localhost;database=wcfDemo;uid=sa;pwd=123456;" providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <!--数据库连接字符串-->
  <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <!--配置缓存-->
    <caching>
    <!--缓存的数据库依赖节-->
      <sqlCacheDependency>
        <databases>
          <add name="UserCacheDependency" connectionStringName="Conn" pollTime="500"/><!--Conn:数据库连接字符串的名称,name随便启名,缓存节会用到-->
        </databases>
      </sqlCacheDependency>
    <!--缓存的数据库依赖节-->
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="SqlDependencyCache" duration="3600" sqlDependency="UserCacheDependency:user"/><!--UserCacheDependency:数据库依赖配置节的名称,user:数据库中需要监听的表名称-->
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>
    <!--配置缓存-->
    <httpRuntime targetFramework="4.5" />
    <compilation debug="true" targetFramework="4.5" />
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
</configuration>

  注意:
  (1) 由于是缓存对数据库的依赖,应用时必须包含connectionStrings的节点。

  (2) <add name="UserCacheDependency" connectionStringName="Conn" pollTime="500"/>。

       [1] connectionStringName:数据库连接字符串的名称;

       [2] pollTime:监听数据库变化的周期,以毫秒为单位。即每500毫秒查看下数据库是否有变化。

  (3) <add name="SqlDependencyCache" duration="3600" sqlDependency="UserCacheDependency:user"/>

        [1] sqlDependency:数据依赖的节的名称+冒号+数据表名称(小写)。如果这个依赖会用到多个表,则用“分号”间隔开,示例如下:UserCacheDependency:user;UserCacheDependency:user1

  3、启用该数据库表的缓存依赖通知功能

  打开vs命令工具行,输入:aspnet_regsql -S localhost -U sa -P 123456 -ed -d wcfDemo -et -t user

      -S localhost:数据库地址

      -U sa:数据库登录名

      -P 123456:数据库登录密码

      -d wcfDemo:数据库的名称

      -t user:表名称(小写)

  

  因为只是监听是否发生数据变化,因此表结构随意,下面的我的表结构:

  

  4、测试程序

  上面的例子只打印了当前时间,如果不加入缓存依赖的情况下,1小时之内都应该运行出的结果都是同一个时间,每次Ctrl+F5强制刷新浏览器时不发生任务变化。当加入缓存依赖后,只要对数据库的数据进行任意修改都会更新缓存的时间,即更改数据后再刷新浏览器时会看到时间在变化。

  七、Response类的Cache属性

  用于获取页面缓存策略。该方式的核心是调用System.Web.HttpCachePolicy。该类主要包含用于设置缓存特定的HTTP标头的方法和用于控制ASP.NET页面输出缓存的方法。与.NET Framework 1.x中的HttpCachePolicy类相比,.NET Framework 2.0中的HttpCachePolicy类得到了扩充和发展。主要是增加了一些重要方法,例如,SetOmitVarStar方法等。由于HttpCachePolicy类方法众多,下面简要说明几个常用方法。

  SetExpires方法:用于设置缓存过期的绝对时间。它的参数是一个DataTime类的实例,表示过期的绝对时间。

protectedvoid Page_Load(object sender, EventArgs e)
{
    // 通过API设置缓存
    //相当于@OutputCache指令中的Duration属性
    Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
    Response.Cache.SetExpires(DateTime.Parse("6:00:00PM"));
}

  如上代码,第一行代码表示输出缓存时间是60秒,并且页面不随任何GETPOST参数改变,等同于“<%@ OutputCache Duration="60" VaryByParam="none" %>”。第二行代码设置缓存过期的绝对时间是当日下午6时整。

  SetLastModified方法:用于设置页面的Last-Modified HTTP标头。Last-Modified HTTP标头表示页面上次修改时间,缓存将依靠它来进行计时。如果违反了缓存限制层次结构,此方法将失败。该方法的参数是一个DataTime类的实例。

  SetSlidingExpiration方法:该方法将缓存过期从绝对时间设置为可调时间。其参数是一个布尔值。当参数为true时,Cache-Control HTTP标头将随每个响应而更新。此过期模式与相对于当前时间将过期标头添加到所有输出集的IIS配置选项相同。当参数为False时,将保留该设置,且任何启用可调整过期的尝试都将静态失败。此方法不直接映射到HTTP标头。它由后续模块或辅助请求来设置源服务器缓存策略。

  SetOmitVaryStar方法:ASP.NET 2.0新增的方法。用于指定在按参数进行区分时,响应是否应该包含vary:*标头。方法参数是一个布尔值,若要指示HttpCachePolicy不对其VaryByHeaders属性使用*值,则为true;否则为false

  SetCacheability方法:用于设置页面的Cache-Control HTTP标头。该标头用于控制在网络上缓存文档的方式。该方法有两种重载方式,所不同的是参数。一种重载方法的参数是HttpCacheability枚举值,包括NoCachePrivatePublicServerServerAndNoCacheServerAndPrivate(有关这些枚举值的定义,可参考MSDN)。另一种方法的参数有两个,一个参数是HttpCacheability枚举值,另一个参数是字符串,表示添加到标头的缓存控制扩展。需要注意的是,仅当与PrivateNoCache指令一起使用时,字段扩展名才有效。如果组合不兼容的指令和扩展,则此方法将引发无效参数异常。

posted on 2017-03-14 15:07  Now,DayBreak  阅读(3587)  评论(0编辑  收藏  举报