在MVC3应用程序中使用Windows Azure的Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider
作为outputCache提供程序。下面是相关的Action方法:
[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();
}
我在从网页浏览器中加载此视图时遇到以下异常: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] 属性,视图会按照预期进行缓存。这是否意味着我不能在必须具有 [Authorize] 的操作方法上放置 [OutputCache]?还是说,我需要使用静态验证回调方法覆盖 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访问示例缓存页面时,页面的输出被缓存,视图显示“This page was cached at 12/31/2011 11:06:12 AM (UTC)”。如果我退出并登录其他用户another@otherdomain.tld并访问页面,则它会显示“This page was cached at 12/31/2011 11:06:38 AM (UTC)”。再次登录me@mydomain.tld并重新访问页面会导致缓存再次显示“This page was cached at 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实例的Users和Roles属性。但是似乎没有一个简单的插入方式。我必须覆盖OnAuthorization并将这两个值放入HttpContext(Items集合?),然后覆盖OnCacheAuthorization以将它们取回。但这听起来很糟糕。
如果我们小心使用OutputCache属性中的VaryByCustom = "User"属性,是否可以仅覆盖OnCacheAuthorization始终返回HttpValidationStatus.Valid呢?当操作方法没有OutputCache属性时,我们不需要担心此回调是否被调用,正确吗?如果我们有一个没有VaryByCustom = "User"的OutputCache属性,那么很明显页面可以返回任何缓存版本,而不管哪个用户请求创建了缓存副本。这有多冒险?