2012-02-09 33 views
11

我有,我想缓存的操作方法:的OutputCache正在发送错误Vary标头时,电话打缓存

[OutputCache(Duration=60*5, Location=OutputCacheLocation.Any, VaryByCustom="index")] 
public ActionResult Index() 
{ 
    return View(); 
} 

采用这种方法:

public override string GetVaryByCustomString(HttpContext context, string custom) 
{ 
    context.Response.Cache.SetOmitVaryStar(true); 
    context.Response.Cache.VaryByHeaders["Cookie"] = true; 

    if (User.Identity.IsAuthenticated) 
    { 
     Debug.Print("Authenticated"); 
     context.Response.Cache.SetNoServerCaching(); 
     context.Response.Cache.SetCacheability(HttpCacheability.Private); 
     return null; 
    } 
    else 
    { 
     Debug.Print("Non authenticated"); 
     return custom; 
    } 
} 

当时的想法是keep a cached version of the page for non-authenticated users, but avoid caching for authenticated ones

我以为它总是会返回一个Vary:Cookie HTTP标头,但事实并非如此。 做与小提琴手测试并发出两次相同的请求,在第一HTTP调用它去好:

HTTP/1.1 200 OK 
Cache-Control: public, max-age=300 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 10:53:36 GMT 
Last-Modified: Thu, 09 Feb 2012 10:48:36 GMT 
Vary: Cookie 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 10:48:37 GMT 
Content-Length: 441 

但在第二个,它覆盖了头:

HTTP/1.1 200 OK 
Cache-Control: public, max-age=297 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 10:53:36 GMT 
Last-Modified: Thu, 09 Feb 2012 10:48:36 GMT 
Vary: * 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 10:48:39 GMT 
Content-Length: 441 

所以,据我所知,即使浏览器是公开的,浏览器也不会缓存请求,因为Vary:*意味着请求是使用不在URL中或HTTP头中的参数生成的。有没有办法来解决这个问题?

问候。

UPDATE:

以类似的方式,当我把两个相同的身份验证请求,第一次调用得到private修改,但不是Vary头:

HTTP/1.1 200 OK 
Cache-Control: private, max-age=300 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 12:43:14 GMT 
Last-Modified: Thu, 09 Feb 2012 12:38:14 GMT 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 12:38:14 GMT 
Content-Length: 443 

但第二个获得与未经认证的请求相同的响应:

HTTP/1.1 200 OK 
Cache-Control: public, max-age=298 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 12:44:32 GMT 
Last-Modified: Thu, 09 Feb 2012 12:39:32 GMT 
Vary: * 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 12:39:33 GMT 
Content-Length: 443 

我已经上传了一个test project showing the issue所以可能你想试​​试看。

请注意,有一个IHttpModule会将请求设置为已验证或未验证,具体取决于请求是否包含cookie,但这不是“真实生活”方法,仅用于测试目的。

该项目包含一个链接到自己,在您登录的链接,而另一个链接,将您注销只有一个网页:

  • 登录:发送一个cookie在HTTP 302重定向到家庭页面。
  • LogOut:将HTTP 302重定向中的过期cookie再次发送到主页。

预期/理想行为将是:

  1. 用户访问索引,并从服务器获取页面。页面显示日期“A”。
  2. 用户访问再次索引,浏览器显示缓存版本。页面显示日期“A”。
  3. 清理浏览器缓存。
  4. 用户访问再次索引,浏览器显示服务器缓存版本。页面显示日期“A”。
  5. 用户单击登录,并且浏览器会获得一个显示日期“B”的新页面。
  6. 用户单击注销,浏览器获取服务器缓存页面。该页面再次显示日期“A”。

但是,这是迄今为止行为:

  1. 用户访问索引,并从服务器获取页面。页面显示日期“A”。
  2. 用户访问再次索引,浏览器显示缓存版本。页面显示日期“A”。
  3. 清理浏览器缓存。
  4. 用户访问再次索引,浏览器显示服务器缓存版本。页面显示日期“A”。
  5. 用户单击登录,并且浏览器会获得一个显示日期“B”的新页面。
  6. 用户单击退出,浏览器应该得到服务器缓存页面,但不是。该页面再次从浏览器缓存中显示日期“B”。这是因为经过身份验证的响应中缺少Vary标头。

我不知道,如果我得到的东西错了缓存,只是缺少一些细节或者OutputCache不能很好地工作,但我希望任何指导。

干杯。

更新2:

我的目的是使用HTTP缓存语义:

  1. 允许浏览器和proxys缓存页的 “公共” 版本。
  2. 允许浏览器为其用户缓存页面的“已认证”版本。

如果我改变的OutputCache声明只做服务器上的高速缓存和防止下游客户端缓存:

[OutputCache(Duration=60*5, Location=OutputCacheLocation.Server, VaryByCustom="index")] 

它的表现不如预期,但防止下游客户端缓存,和那不是我想要的。

+1

如果您还在方法的OutputCache属性上设置了其他VaryBy属性,会发生什么情况? – bzlm 2012-02-09 12:13:24

+0

我已经添加VaryByHeader =“Cookie”,它仍然发生,第二次调用获得Vary = *。 – vtortola 2012-02-09 12:18:14

+0

如果您尝试[在链接问题中链接到的方法]会怎样(http://visitmix.com/writings/using-varybycustom-with-outputcache-in-asp-net-mvc-to-support-caching-for-登录用户),没有改变cookie或修改'GetVaryByCustom'方法中的响应头? (从技术上讲,您不需要通过Cookie来实现仅针对匿名用户的缓存。) – bzlm 2012-02-09 13:13:22

回答

4

我不认为[OutputCache]属性是你想要什么,该VaryByCustom方法基本上是说,我想根据这些参数来缓存不同的版本,它并没有真正有选项不缓存和该属性中的大部分代码都是基于基于服务器的缓存构建的。

这样说自定义缓存上MSDN的文件似乎表明你需要返回一个字符串,基于认证状态变化:

public override string GetVaryByCustomString(HttpContext context, string custom) 
{ 
    if(custom == "user") return "User:" + context.Request.User.Identity.Name; 

    return base.GetVaryByCustomString(context, custom); 
} 

然后使用用户文字在VaryByCustom

[OutputCache(Duration=60*5, Location=OutputCacheLocation.Any, VaryByCustom="user")] 
public ActionResult Index() 
{ 
    return View(); 
} 

所以基本上这将导致在建匿名缓存(假定匿名身份是空字符串或东西)和每用户上我相信发送给客户端的服务器和Vary: *。显然不是你想要的东西。

如果你真的只想缓存使用HTTP缓存的未经身份验证的版本,我会建议不要使用OutputCacheAttribute并使用更自定义的东西。

你可以很容易地就在自己编写自定义的属性像你有什么对你的GetVaryByCustomString实现(这仅仅是一些伪代码,就需要比这更多):

public class HttpCacheUnauthenticatedAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     if(!filterContext.HttpContext.Request.IsAuthenticated) { 
      //TODO: set unauthenticated caching values and vary here 
     } 
    } 
} 

然后标记您的行动方法与它:

与自己相似的东西
[HttpCacheUnauthenticated] 
public ActionResult Index() 
{ 
    return View(); 
} 
+0

使用OutputCache的方法在第一次调用中按预期工作,这只是在后续调用中返回错误的Vary头,此时调用是OutputCache中的“缓存命中”。就这些。提供的功能,但不按预期工作。实现我自己的OutputCache操作过滤器不会解决任何问题。问题不在于OutputCacheAttribute本身,而在于将缓存API放在后面。 – vtortola 2012-02-21 10:59:13

+0

@vtortola这就是为什么我认为你应该绕过API并自己写头。你只是在寻找HTTP缓存,而不是服务器端缓存是否正确?就像我所说的,OutputCache属性不适用于任何其内容没有缓存在响应中的场景。 – 2012-02-21 11:47:28

+0

不,我正在寻找:D仔细阅读“预期/理想的行为将是:”部分。干杯。 – vtortola 2012-02-21 13:46:39

0

我使用自定义缓存提供商,在这种情况下,有这个一个简单的解决方案。 在的BeginRequest,根据用户认证状态,我们设定一个上下文信息不运行缓存:

HttpContext.Current.Items["NoCache"] = "1"; 

,然后在我们的GetVaryBy方法会返回null如果该信息被设置:

public override string GetVaryByCustomString(HttpContext context, string custom) 
{ 
    if (HttpContext.Current.Items["NoCache"] != null) 
     return null; 

    // remaining code here 
} 

然后在缓存方法上,我们可以测试一样。例如:

public override object Add(string key, object entry, DateTime utcExpiry) 
{ 
    if (HttpContext.Current.Items["NoCache"] != null) 
     return null; 

    // remaining code here 
}