Использование Windows Azure Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider
в качестве поставщика outputCache для приложения MVC3. метод действия:
[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], представление кэшируется, как и ожидалось. Означает ли это, что я не могу включить [OutputCache] метод действия, который должен иметь [Authorize]? Или мне нужно переопределить AuthorizeAttribute с помощью настраиваемой реализации, которая использует метод обратного вызова статической проверки для кеша?
Обновление 1
После ответа Эвана я протестировал указанное выше действие в IIS Express (за пределами Azure). Вот мое переопределение для свойства VaryByCustom = "User" в атрибуте OutputCache:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
return "User".Equals(custom, StringComparison.OrdinalIgnoreCase)
? Thread.CurrentPrincipal.Identity.Name
: base.GetVaryByCustomString(context, custom);
}
Когда я посещаю образец кэшированной страницы как (скрытый), вывод страницы кэшируется, и в представлении отображается «Эта страница была кэширована 31.12.2011, 11:06: 12 AM (UTC) ". Если я затем выйду из системы и войду как (скрытый) и перейду на страницу, появится сообщение «Эта страница была кэширована 31.12.2011, 11:06: 38 AM (UTC)». При повторном входе в систему как (скрытом) и повторном посещении страницы в кеше снова отображается сообщение «Эта страница была кэширована 31.12.2011, 11:06: 12 AM (UTC)».Дальнейшие попытки входа / выхода показывают, что различные выходные данные кэшируются и возвращаются в зависимости от пользователя.
Это наводит меня на мысль, что вывод кэшируется отдельно в зависимости от пользователя, что является намерением с моей настройкой и переопределением VaryByCustom = "User". Проблема в том, что он не работает с поставщиком распределенного кеша Azure. Эван, вы отвечаете, что кэширование только общедоступного контента все еще в силе?
Обновление 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
делегирует проверку кеша защищенному виртуальному HttpValidationStatus OnCacheAuthorization (HttpContextBase)
, который, конечно, не статичен. Одна из причин, по которой он не статичен, заключается в том, что, как отмечено в ВАЖНОМ комментарии выше, он вызывает защищенный виртуальный логический объект AuthorizeCore (HttpContextBase)
.
Для выполнения любой логики AuthorizeCore из метода обратного вызова проверки статического кэша необходимо знать свойства Users и Roles экземпляра AuthorizeAttribute. Однако, похоже, нет простого способа подключения. Мне пришлось бы переопределить OnAuthorization, чтобы поместить эти 2 значения в HttpContext (коллекцию Items?), А затем переопределить OnCacheAuthorization, чтобы вернуть их. Но это грязно пахнет.
Если мы осторожно используем свойство VaryByCustom = "User" в атрибуте OutputCache, можем ли мы просто переопределить OnCacheAuthorization, чтобы всегда возвращать HttpValidationStatus.Действительный? Когда метод действия не имеет атрибута OutputCache, нам не нужно беспокоиться о том, что этот обратный вызов когда-либо будет вызван, верно? И если у нас есть атрибут OutputCache без VaryByCustom = "User", тогда должно быть очевидно, что страница может возвращать любую кешированную версию, независимо от того, какой пользовательский запрос создал кешированную копию. Насколько это рискованно?