Как отключить кэширование вывода для аутентифицированных пользователей в ASP.NET MVC?
У меня есть приложение ASP.NET MVC. Мне нужно кэшировать некоторые страницы, однако только для пользователей, не прошедших проверку подлинности.
Я попытался использовать VaryByCustom="user"
со следующей реализацией GetVaryByCustomString
:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "user")
{
if (context.User.Identity.IsAuthenticated)
{
return context.User.Identity.Name;
}
else
{
return "";
}
}
return base.GetVaryByCustomString(context, custom);
}
Однако это не совсем то, что мне нужно, потому что страницы все еще кэшируются. Разница только в том, что теперь кэшируется для каждого пользователя отдельно.
Одним из возможных решений является возвращение Guid.NewGuid()
каждый раз, когда пользователь аутентифицируется, но для меня это выглядит огромной тратой ресурсов.
У вас есть какие-нибудь советы для меня?
Ответы
Ответ 1
Итак, вот что я сделал:
public class NonAuthenticatedOnlyCacheAttribute : OutputCacheAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var httpContext = filterContext.HttpContext;
if (httpContext.User.Identity.IsAuthenticated)
{
// it crucial not to cache Authenticated content
Location = OutputCacheLocation.None;
}
// this smells a little but it works
httpContext.Response.Cache.AddValidationCallback(IgnoreAuthenticated, null);
base.OnResultExecuting(filterContext);
}
// This method is called each time when cached page is going to be
// served and ensures that cache is ignored for authenticated users.
private void IgnoreAuthenticated(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
if (context.User.Identity.IsAuthenticated)
validationStatus = HttpValidationStatus.IgnoreThisRequest;
else
validationStatus = HttpValidationStatus.Valid;
}
}
Огромное спасибо Крэйгу Стунцу, который указал мне на правильное направление и чей ответ я невольно отказался.
Ответ 2
Атрибуты в целом кэшируются, тогда вам нужно сохранить оригинальное местоположение. Если вы заходите на страницу "Записан", она устанавливает "Местоположение" на "Нет", а затем, когда вы получаете доступ как анонимный, она по-прежнему отсутствует.
public class AuthenticatedOnServerCacheAttribute : OutputCacheAttribute
{
private OutputCacheLocation? originalLocation;
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var httpContext = filterContext.HttpContext;
if (httpContext.User.Identity.IsAuthenticated)
{
originalLocation = originalLocation ?? Location;
Location = OutputCacheLocation.None;
}
else
{
Location = originalLocation ?? Location;
}
base.OnResultExecuting(filterContext);
}
}
Ответ 3
Принятый ответ правильный, но он не работает для кэширования таким образом Частичные представления.
Я объединил оба варианта:
GetVaryByCustomString
и установите Duration
как минимум - для частичных представлений и AddValidationCallback
для страниц. На самом деле можно использовать только первый метод, но второй выглядит не так дорого - он не вызывает OnResultExecuting
каждый раз, а только зарегистрированный обработчик.
Таким образом, класс атрибутов пользовательского кеша
public class CacheAttribute : OutputCacheAttribute
{
public CacheAttribute()
{
Duration = 300; /*default cache time*/
}
private bool _partialView;
/// <summary>
/// Set true if Partial view is cached
/// </summary>
public bool PartialView
{
get { return _partialView; }
set
{
_partialView = value;
if ( _partialView ) {
VaryByCustom = "Auth";
}
}
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if ( PartialView ) OnCachePartialEnabled( filterContext );
else OnCacheEnabled(filterContext);
base.OnResultExecuting( filterContext );
}
private OutputCacheLocation? originalLocation;
private int? _prevDuration;
protected void OnCachePartialEnabled(ResultExecutingContext filterContext)
{
var httpContext = filterContext.HttpContext;
if ( !_prevDuration.HasValue) _prevDuration = Duration;
Duration = httpContext.User.Identity.IsAuthenticated ? 1 : _prevDuration.Value;
}
protected void OnCacheEnabled(ResultExecutingContext filterContext)
{
var httpContext = filterContext.HttpContext;
if ( httpContext.User.Identity.IsAuthenticated ) {
// it crucial not to cache Authenticated content
originalLocation = originalLocation ?? Location;
Location = OutputCacheLocation.None;
}
else {
Location = originalLocation ?? Location;
}
// this smells a little but it works
httpContext.Response.Cache.AddValidationCallback( IgnoreAuthenticated, null );
}
// This method is called each time when cached page is going to be
// served and ensures that cache is ignored for authenticated users.
private void IgnoreAuthenticated(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = context.User.Identity.IsAuthenticated
? HttpValidationStatus.IgnoreThisRequest
: HttpValidationStatus.Valid;
}
}
Переопределить метод GetVaryByCustomString в Global.asax.cs
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if ( custom == "Auth" ) {
//do not cache when user is authenticated
if ( context.User.Identity.IsAuthenticated ) {
return base.GetVaryByCustomString( context, custom );
}
return "NotAuth";
}
return base.GetVaryByCustomString( context, custom );
}
Используйте его следующим образом:
[Cache]
public virtual ActionResult Index()
{
return PartialView();
}
[ChildActionOnly, Cache(PartialView=true)]
public virtual ActionResult IndexPartial()
{
return PartialView();
}
Обновлено: Я также добавил исправление Fujiy здесь