Почему я не могу объединить атрибуты [Authorize] и [OutputCache] при использовании кэша Azure (приложение .NET MVC3)?

Использование Windows AzureMicrosoft.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)

Если я удалю атрибут [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);
}

Когда я захожу на пример кэшированной страницы с именем [email protected], выходные данные страницы кэшируются, и в представлении отображается «Эта страница была кэширована 31.12.2011 11:06:12 AM (UTC) ". Если я затем выйду из системы и войду как [email protected] и заеду на страницу, на ней отобразится сообщение" Эта страница была кэширована 31.12.2011 11:06:38 AM (UTC) ". Повторный вход в систему как [email protected] и повторное посещение страницы приводит к отображению кэша" Эта страница была кэширована 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 делегирует проверку кэшаprotected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase)что, конечно, не является статичным. Одна из причин, почему он не является статичным, заключается в том, что, как отмечено в ВАЖНОМ комментарии выше, он вызываетprotected virtual bool AuthorizeCore(HttpContextBase).

Чтобы выполнить любую логику AuthorizeCore из метода обратного вызова проверки статического кэша, ему необходимо знать свойства Users и Roles экземпляра AuthorizeAttribute. Однако, кажется, нет простого способа подключить его. Мне пришлось бы переопределить OnAuthorization, чтобы поместить эти 2 значения в HttpContext (коллекция Items?), А затем переопределить OnCacheAuthorization, чтобы вернуть их обратно. Но это пахнет грязно.

Если мы осторожно используем свойство VaryByCustom = "User" в атрибуте OutputCache, можем ли мы просто переопределить OnCacheAuthorization, чтобы всегда возвращать HttpValidationStatus.Valid? Если у метода действия нет атрибута OutputCache, нам не нужно беспокоиться о том, что этот обратный вызов когда-либо будет вызван, верно? И если у нас есть атрибут OutputCache без VaryByCustom = "User", то должно быть очевидно, что страница может вернуть любую кэшированную версию независимо от того, какой запрос пользователя создал кэшированную копию. Насколько это рискованно?

Ответы на вопрос(0)

Ваш ответ на вопрос