Использование Windows Azure Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider
в качестве поставщика outputCache для приложения MVC3. Вот соответствующий метод действий:
[ActionName("sample-cached-page")]
[OutputCache(Duration = 300, VaryByCustom = "User",
Location = OutputCacheLocation.Server)]
[Authorize(Users = "[email protected],[email protected]")]
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)
Если я удалю атрибут [Авторизовать], кэширует представление, как и следовало ожидать. Означает ли это, что я не могу поместить [OutputCache] в метод действия, который должен иметь [Авторизовать]? Или мне нужно переопределить 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);
}
Когда я посещаю образец кэшированной страницы как [email protected], вывод страницы кэшируется, и на экране отображается "Эта страница была кэширована в 12/31/2011 11:06: 12 AM (UTC)". Если я затем выйду из системы и зарегистрирую его как [email protected] и перейдя на страницу, он отобразит "Эта страница была кеширована в 12/31/2011 11:06: 38 AM (UTC)". Подпись в виде [email protected] и пересмотр страницы приводит к отображению кеша. "Эта страница была снова сохранена в кэше в 12/31/2011 11:06: 12 AM (UTC)". Дальнейшие попытки входа/выхода показывают, что разные выходные данные кэшируются и возвращаются в зависимости от пользователя.
Это заставляет меня думать, что вывод кэшируется отдельно на основе пользователя, что является намерением с настройкой и переопределением VaryByCustom = "Пользователь". Проблема в том, что он не работает с поставщиком распределенных кешей 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
делегирует проверку кэша на protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase)
, что, конечно, не является статичным. Одна из причин, почему он не является статичным, заключается в том, что, как отмечено в ВАЖНОМ комментарии выше, он вызывает protected virtual bool AuthorizeCore(HttpContextBase)
.
Чтобы выполнить любую логику AuthorizeCore из метода обратного вызова проверки статического кеша, ему необходимо знать свойства "Пользователи и роли" экземпляра AuthorizeAttribute. Однако, кажется, нет простого способа подключиться. Мне пришлось бы переопределить OnAuthorization, чтобы поместить эти 2 значения в HttpContext (коллекция Items?), А затем переопределить OnCacheAuthorization, чтобы вернуть их. Но это пахнет грязным.
Если мы стараемся использовать свойство VaryByCustom = "User" в атрибуте OutputCache, можем ли мы просто переопределить OnCacheAuthorization, чтобы всегда возвращать HttpValidationStatus.Valid? Когда у метода действия нет атрибута OutputCache, нам не нужно беспокоиться об этом обратном вызове, когда-либо вызываемом, правильно? И если у нас есть атрибут OutputCache без VaryByCustom = "Пользователь", тогда должно быть очевидно, что страница может вернуть любую кешированную версию, независимо от того, какой пользовательский запрос создал кешированную копию. Насколько это рискованно?