[英]How to turn output caching off for authenticated users in ASP.NET MVC?
I have an ASP.NET MVC application. 我有一个ASP.NET MVC应用程序。 I need to cache some pages however only for non-authenticated users.
我需要缓存一些页面,但仅限于未经过身份验证的用户。
I've tried to use VaryByCustom="user"
with the following GetVaryByCustomString
implementation: 我尝试使用
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);
}
However this isn't exactly what I need because pages are still cached. 然而,这并不是我需要的,因为页面仍然被缓存。 Only difference is that now is cached for each user separately.
唯一的区别是现在分别为每个用户缓存。
One possible solution is to return Guid.NewGuid()
each time when user is Authenticated, but it looks like a huge waste of resources to me. 一种可能的解决方案是每次用户进行身份验证时返回
Guid.NewGuid()
,但这对我来说似乎是一种巨大的资源浪费。
So do you have any tips for me? 所以你有什么提示吗?
So here is what I done: 所以这就是我所做的:
public class NonAuthenticatedOnlyCacheAttribute : OutputCacheAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var httpContext = filterContext.HttpContext;
if (httpContext.User.Identity.IsAuthenticated)
{
// it's 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;
}
}
Many thanks to Craig Stuntz who pointed me to correct direction and whose answer I unwittingly downvoted. 非常感谢Craig Stuntz向我指出了正确的方向,我的答案在不知不觉中被低估了。
Attributes in general are cached, then you need to store original Location. 通常会对属性进行缓存,然后您需要存储原始位置。 If you access the page Logged, it set Location to None, then when you access as anonymous, it still None.
如果您访问Logged页面,它将Location设置为None,然后当您以匿名方式访问时,它仍然是None。
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);
}
}
The accepted answer is correct but it doesn't work for caching in this way Partial views. 接受的答案是正确的,但它不适用于以这种方式缓存部分视图。 I have combined both variants:
GetVaryByCustomString
and set Duration
to the minimum - for Partial Views and AddValidationCallback
method for pages. 我结合了两种变体:
GetVaryByCustomString
并将Duration
设置为最小 - 对于页面的部分视图和AddValidationCallback
方法。 Actually it is possible to use only the first method but the second one looks not such expensive - it doesn't call OnResultExecuting
each time but only registered handler. 实际上可以只使用第一种方法,但第二种方法看起来不那么昂贵 - 它不会每次调用
OnResultExecuting
而只调用注册处理程序。
So the custom cache attribute class 所以自定义缓存属性类
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's 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;
}
}
Override GetVaryByCustomString method in Global.asax.cs 覆盖Global.asax.cs中的GetVaryByCustomString方法
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 );
}
Use it like this: 像这样使用它:
[Cache]
public virtual ActionResult Index()
{
return PartialView();
}
[ChildActionOnly, Cache(PartialView=true)]
public virtual ActionResult IndexPartial()
{
return PartialView();
}
Updated: I have also added Fujiy's fix here 更新:我还在这里添加了Fujiy的修复程序
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.