当呼叫到达缓存时,OutputCache发送错误的Vary标头

vto*_*ola 11 asp.net caching outputcache http asp.net-mvc-3

我有一个我想要缓存的动作方法:

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

用这种方法:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我们的想法是为未经过身份验证的用户保留页面的缓存版本,但避免对经过身份验证的用户进行缓存.

我以为它总会返回Vary:CookieHTTP标头,但事实并非如此.使用Fiddler进行测试并发出两次相同的请求,在第一次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
Run Code Online (Sandbox Code Playgroud)

但在第二个中,它会覆盖标题:

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
Run Code Online (Sandbox Code Playgroud)

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

问候.

更新:

以类似的方式,当我发送两个相同的经过身份验证的请求时,第一个调用获取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
Run Code Online (Sandbox Code Playgroud)

但第二个获得与未经过身份验证的请求相同的响应:

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
Run Code Online (Sandbox Code Playgroud)

我上传了一个显示问题测试项目,所以可能你想尝试一下.

请注意,IHttpModule根据请求是否有cookie ,有一个设置是否经过身份验证的请求,这不是"现实生活"方法,仅用于测试目的.

该项目仅包含一个带有自身链接的网页,一个登录您的链接以及另一个将您注销的链接:

  • LogIn:HTTP 302再次将cookie 重定向到主页.
  • LogOut:HTTP 302再次将重定向的过期cookie发送到主页.

预期/理想的行为将是:

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

但到目前为止这是行为:

  1. 用户访问索引,并从服务器获取页面.页面显示日期"A".
  2. 用户再次访问索引,浏览器显示缓存版本.页面显示日期"A".
  3. 清理浏览器缓存.
  4. 用户访问索引,浏览器显示服务器缓存版本.页面显示日期"A".
  5. 用户点击登录,broswer获得一个新页面,显示日期"B".
  6. 用户单击注销,浏览器应获取服务器缓存页面,但事实并非如此.页面再次从浏览器缓存中显示日期"B".这是因为Vary在经过身份验证的响应中缺少标头.

我不知道我是否对缓存有什么不妥,只是遗漏了一些细节或者OutputCache工作不是很好,但我会很感激任何指导.

干杯.

更新2:

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

  1. 允许浏览器和代理缓存页面的"公共"版本.
  2. 允许浏览器为其用户缓存页面的"已验证"版本.

如果我更改OutputCache声明仅在服务器上执行缓存并阻止下游和客户端缓存:

[OutputCache(Duration=60*5, Location=OutputCacheLocation.Server, VaryByCustom="index")]
Run Code Online (Sandbox Code Playgroud)

它的行为与预期的一样,但是防止了下游和客户端缓存,这不是我想要的.

Pau*_*yng 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);
}
Run Code Online (Sandbox Code Playgroud)

然后在以下位置使用用户文字VaryByCustom

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

所以基本上这会导致为匿名(假设匿名身份是空字符串或其他东西)和服务器上的每个用户构建一个缓存,并发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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用它标记你的操作方法:

[HttpCacheUnauthenticated]
public ActionResult Index()
{
    return View();
}
Run Code Online (Sandbox Code Playgroud)