使用 Windows Azure 的Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider
作为 MVC3 应用程序的 outputCache 提供程序。下面是相关的操作方法:
[ActionName("sample-cached-page")]
[OutputCache(Duration = 300, VaryByCustom = "User",
Location = OutputCacheLocation.Server)]
[Authorize(Users = "[email protected] /cdn-cgi/l/email-protection,[email protected] /cdn-cgi/l/email-protection")]
public virtual ActionResult SampleCachedPage()
{
return View();
}
从网络浏览器加载此视图时出现以下异常:
System.Configuration.Provider.ProviderException: When using a custom output cache provider like 'DistributedCache', only the following expiration policies and cache features are supported: file dependencies, absolute expirations, static validation callbacks and static substitution callbacks.
System.Configuration.Provider.ProviderException: When using a custom output cache provider like 'DistributedCache', only the following expiration policies and cache features are supported: file dependencies, absolute expirations, static validation callbacks and static substitution callbacks.
at System.Web.Caching.OutputCache.InsertResponse(String cachedVaryKey, CachedVary cachedVary, String rawResponseKey, CachedRawResponse rawResponse, CacheDependency dependencies, DateTime absExp, TimeSpan slidingExp)
at System.Web.Caching.OutputCacheModule.OnLeave(Object source, EventArgs eventArgs)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
如果我删除 [Authorize] 属性,视图将按预期进行缓存。这是否意味着我无法将 [OutputCache] 放在必须具有 [Authorize] 的操作方法上?或者,我是否需要使用对缓存使用静态验证回调方法的自定义实现来重写 AuthorizeAttribute?
Update 1
在Evan回答之后,我在IIS Express(Azure之外)中测试了上述操作方法。以下是我对 OutputCache 属性上的 VaryByCustom = "User" 属性的覆盖:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
return "User".Equals(custom, StringComparison.OrdinalIgnoreCase)
? Thread.CurrentPrincipal.Identity.Name
: base.GetVaryByCustomString(context, custom);
}
当我访问示例缓存页面时[电子邮件受保护] /cdn-cgi/l/email-protection,页面的输出被缓存,并且视图显示“此页面于 12/31/2011 11:06 被缓存:12AM (UTC)”。如果我随后注销并以身份登录[电子邮件受保护] /cdn-cgi/l/email-protection访问该页面,显示“此页面已于 12/31/2011 11:06 缓存:38AM (UTC)”。重新登录为[电子邮件受保护] /cdn-cgi/l/email-protection重新访问该页面会导致缓存显示“此页面已于 12/31/2011 11:06 缓存:12再次尝试“AM (UTC)”。进一步的登录/注销尝试显示,根据用户的不同,正在缓存和返回不同的输出。
这让我相信输出是根据用户单独缓存的,这就是我的 VaryByCustom =“User”设置和覆盖的意图。问题是它不能与 Azure 的分布式缓存提供程序一起使用。埃文,您关于仅缓存公共内容的回答仍然有效吗?
Update 2
我挖掘了源代码,发现开箱即用的 AuthorizeAttribute 实际上确实有一个非静态验证回调。这是摘录自OnAuthorization
:
if (AuthorizeCore(filterContext.HttpContext)) {
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else {
HandleUnauthorizedRequest(filterContext);
}
CacheValidationHandler
将缓存验证委托给protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase)
,这当然不是静态的。它不是静态的原因之一是,正如上面重要注释中所述,它调用protected virtual bool AuthorizeCore(HttpContextBase)
.
为了通过静态缓存验证回调方法执行任何 AuthorizeCore 逻辑,需要了解 AuthorizeAttribute 实例的 Users 和 Roles 属性。然而,似乎没有一种简单的插入方法。我必须重写 OnAuthorization 将这两个值放入 HttpContext (Items 集合?),然后重写 OnCacheAuthorization 将它们取出。但那味道很脏。
如果我们小心地在 OutputCache 属性中使用 VaryByCustom = "User" 属性,我们是否可以重写 OnCacheAuthorization 以始终返回 HttpValidationStatus.Valid?当操作方法没有 OutputCache 属性时,我们不需要担心调用此回调,对吗?如果我们确实有一个没有 VaryByCustom = "User" 的 OutputCache 属性,那么很明显,无论哪个用户请求创建了缓存副本,页面都可以返回任何缓存版本。这有多危险?