使用 Windows AzureMicrosoft.Web.DistributedCache.DistributedCacheOutputCacheProvider
作为 MVC3 应用程序的 outputCache 提供程序。下面是相关的操作方法:
[ActionName("sample-cached-page")]
[OutputCache(Duration = 300, VaryByCustom = "User",
Location = OutputCacheLocation.Server)]
[Authorize(Users = "me@mydomain.tld,another@otherdomain.tld")]
public virtual ActionResult SampleCachedPage()
{
return View();
}
从 Web 浏览器加载此视图时出现以下异常:
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?
更新 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);
}
当我以 me@mydomain.tld 身份访问示例缓存页面时,页面的输出被缓存,并且视图显示“此页面已缓存于 12/31/2011 11:06:12 AM (UTC)”。如果我随后注销并以 another@otherdomain.tld 身份登录并访问该页面,它会显示“此页面已缓存于 12/31/2011 11:06:38 AM (UTC)”。以 me@mydomain.tld 身份重新登录并重新访问该页面会导致缓存再次显示“此页面已缓存于 12/31/2011 11:06:12 AM (UTC)”。进一步的登录/注销尝试表明,根据用户的不同,正在缓存和返回不同的输出。
这让我相信输出是根据用户单独缓存的,这是我的 VaryByCustom = "User" 设置和覆盖的意图。问题是它不适用于 Azure 的分布式缓存提供程序。Evan,您是否回答仅缓存公共内容仍然有效?
更新 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 实例的用户和角色属性。但是,似乎没有一种简单的插入方法。我必须重写 OnAuthorization 以将这两个值放入 HttpContext(Items 集合?),然后重写 OnCacheAuthorization 以将它们取出。但这闻起来很脏。
如果我们小心地在 OutputCache 属性中使用 VaryByCustom = "User" 属性,我们是否可以重写 OnCacheAuthorization 以始终返回 HttpValidationStatus.Valid?当 action 方法没有 OutputCache 属性时,我们不需要担心这个回调会被调用,对吗?如果我们确实有一个没有 VaryByCustom = "User" 的 OutputCache 属性,那么很明显页面可以返回任何缓存版本,而不管哪个用户请求创建了缓存副本。这有多大风险?