.net mvc:自定义授权属性和自定义输出缓存提供程序
.net mvc: custom authorizeattribute and customoutputcache provider
我编写了一个将输出保存在磁盘上的自定义输出缓存提供程序,它工作正常,但在使用 AuthorizeAttribute 修饰的操作中除外。
查看问题,解决方案似乎有一个管理缓存的自定义 AuthorizeAttribute。
然后我添加了我的自定义 AuthorizeAttribute,但不幸的是,我收到了错误
“使用像 'FileCacheProvider' 这样的自定义输出缓存提供程序时,仅支持以下过期策略和缓存功能:文件依赖性、绝对过期、静态验证回调和静态替换回调。 “
代码:
自定义 OutputCache 提供程序 (FileCacheProvider)
public class FileCacheProvider : OutputCacheProvider
{
public string CacheLocation
{
get
{
if (ConfigurationManager.AppSettings["FileCacheLocationRelativePath"] == null)
{
throw new ApplicationException("The FileCacheLocationRelativePath AppSettings key is not configured.");
}
string strCacheLocation = ConfigurationManager.AppSettings["FileCacheLocationRelativePath"];
strCacheLocation = HttpContext.Current.Server.MapPath(strCacheLocation);
return strCacheLocation + @"\";
}
}
public override object Add(string key, object entry, DateTime utcExpiry)
{
object obj = this.Get(key);
if (obj != null)
{
return obj;
}
else
{
this.Set(key, entry, utcExpiry);
return entry;
}
}
public override void Remove(string key)
{
string filePath = GetFullPathForKey(key);
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
public override object Get(string key)
{
string filePath = GetFullPathForKey(key);
if (!File.Exists(filePath))
{
return null;
}
CacheItem item = null;
FileStream fileStream = File.OpenRead(filePath);
BinaryFormatter formatter = new BinaryFormatter();
item = (CacheItem)formatter.Deserialize(fileStream);
fileStream.Close();
if (item == null || item.Expiry <= DateTime.UtcNow)
{
Remove(key);
return null;
}
return item.Item;
}
public override void Set(string key, object entry, DateTime utcExpiry)
{
string filePath = GetFullPathForKey(key);
CacheItem item = new CacheItem { Expiry = utcExpiry, Item = entry };
FileStream fileStream = File.OpenWrite(filePath);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fileStream, item);
fileStream.Close();
}
private string GetFullPathForKey(string key)
{
string temp = key.Replace('/', '$');
return CacheLocation + temp;
}
}
[Serializable]
public class CacheItem
{
public object Item { get; set; }
public DateTime Expiry { get; set; }
}
自定义授权属性(DFAuthorizeAttribute)
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class DFAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
private AuthenticationManager authentication = new AuthenticationManager();
protected void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
protected void SetCachePolicy(AuthorizationContext filterContext)
{
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (AuthorizeCore(filterContext.HttpContext))
{
SetCachePolicy(filterContext);
}
else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
}
else if (authentication != null || authentication.AuthenticationData != null)
{
SetCachePolicy(filterContext);
}
else
{
ViewDataDictionary viewData = new ViewDataDictionary();
viewData.Add("Message", "You do not have sufficient privileges for this operation.");
filterContext.Result = new ViewResult { ViewName = "Error", ViewData = viewData };
}
}
}
Web.config
<caching>
<outputCache defaultProvider="FileCacheProvider">
<providers>
<add name="FileCacheProvider" type="MyNameSpace.FileCacheProvider"/>
</providers>
</outputCache>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Index" duration="3600" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
动作
[OutputCache(CacheProfile = "Index")]
[MyNameSpace.DFAuthorize]
public ActionResult Index(string pageId)
{
....
}
任何帮助将不胜感激
问题是因为您的 CacheValidateHandler
不是静态方法。我对其进行了测试,如果您评论其内容并将其更改为静态,错误就会消失。
但是,当您这样做时,它也不会在方法中遇到断点,所以我认为这不是一个可行的解决方案。
似乎有很多关于它的讨论on this thread,但似乎没有任何真正的答案。
我认为自定义输出缓存并非设计用于与 AuthorizeAttribute 结合使用,或者这是某种 MVC 错误。请记住,MVC 比 OutputCache(来自 .NET 2.0)更新得多,因此这可能只是一种不兼容,如果不引入重大 API 更改就无法解决。如果您觉得这足够重要,可以向 MVC 团队报告。
但是,恕我直言,您应该只使用 System.Runtime.Caching.ObjectCache abstract class which also can be extended to be file-based 而不是输出缓存来处理这种情况。它实际上并不缓存页面内容(您只会缓存数据块),但如果您正试图解决这个问题,它仍然可以防止往返数据库。
请注意,您仍然可以将 FileCacheProvider 用于 public 无需登录的页面,但每个需要 AuthorizeAttribute 的操作都应改用 System.Runtime.Caching
提供程序。此外,缓存登录后的页面是一种不寻常的情况,因为它们往往需要在大多数时间实时查看数据。
我编写了一个将输出保存在磁盘上的自定义输出缓存提供程序,它工作正常,但在使用 AuthorizeAttribute 修饰的操作中除外。
查看问题,解决方案似乎有一个管理缓存的自定义 AuthorizeAttribute。
然后我添加了我的自定义 AuthorizeAttribute,但不幸的是,我收到了错误
“使用像 'FileCacheProvider' 这样的自定义输出缓存提供程序时,仅支持以下过期策略和缓存功能:文件依赖性、绝对过期、静态验证回调和静态替换回调。 “
代码:
自定义 OutputCache 提供程序 (FileCacheProvider)
public class FileCacheProvider : OutputCacheProvider
{
public string CacheLocation
{
get
{
if (ConfigurationManager.AppSettings["FileCacheLocationRelativePath"] == null)
{
throw new ApplicationException("The FileCacheLocationRelativePath AppSettings key is not configured.");
}
string strCacheLocation = ConfigurationManager.AppSettings["FileCacheLocationRelativePath"];
strCacheLocation = HttpContext.Current.Server.MapPath(strCacheLocation);
return strCacheLocation + @"\";
}
}
public override object Add(string key, object entry, DateTime utcExpiry)
{
object obj = this.Get(key);
if (obj != null)
{
return obj;
}
else
{
this.Set(key, entry, utcExpiry);
return entry;
}
}
public override void Remove(string key)
{
string filePath = GetFullPathForKey(key);
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
public override object Get(string key)
{
string filePath = GetFullPathForKey(key);
if (!File.Exists(filePath))
{
return null;
}
CacheItem item = null;
FileStream fileStream = File.OpenRead(filePath);
BinaryFormatter formatter = new BinaryFormatter();
item = (CacheItem)formatter.Deserialize(fileStream);
fileStream.Close();
if (item == null || item.Expiry <= DateTime.UtcNow)
{
Remove(key);
return null;
}
return item.Item;
}
public override void Set(string key, object entry, DateTime utcExpiry)
{
string filePath = GetFullPathForKey(key);
CacheItem item = new CacheItem { Expiry = utcExpiry, Item = entry };
FileStream fileStream = File.OpenWrite(filePath);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fileStream, item);
fileStream.Close();
}
private string GetFullPathForKey(string key)
{
string temp = key.Replace('/', '$');
return CacheLocation + temp;
}
}
[Serializable]
public class CacheItem
{
public object Item { get; set; }
public DateTime Expiry { get; set; }
}
自定义授权属性(DFAuthorizeAttribute)
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class DFAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
private AuthenticationManager authentication = new AuthenticationManager();
protected void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
protected void SetCachePolicy(AuthorizationContext filterContext)
{
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (AuthorizeCore(filterContext.HttpContext))
{
SetCachePolicy(filterContext);
}
else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
}
else if (authentication != null || authentication.AuthenticationData != null)
{
SetCachePolicy(filterContext);
}
else
{
ViewDataDictionary viewData = new ViewDataDictionary();
viewData.Add("Message", "You do not have sufficient privileges for this operation.");
filterContext.Result = new ViewResult { ViewName = "Error", ViewData = viewData };
}
}
}
Web.config
<caching>
<outputCache defaultProvider="FileCacheProvider">
<providers>
<add name="FileCacheProvider" type="MyNameSpace.FileCacheProvider"/>
</providers>
</outputCache>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Index" duration="3600" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
动作
[OutputCache(CacheProfile = "Index")]
[MyNameSpace.DFAuthorize]
public ActionResult Index(string pageId)
{
....
}
任何帮助将不胜感激
问题是因为您的 CacheValidateHandler
不是静态方法。我对其进行了测试,如果您评论其内容并将其更改为静态,错误就会消失。
但是,当您这样做时,它也不会在方法中遇到断点,所以我认为这不是一个可行的解决方案。
似乎有很多关于它的讨论on this thread,但似乎没有任何真正的答案。
我认为自定义输出缓存并非设计用于与 AuthorizeAttribute 结合使用,或者这是某种 MVC 错误。请记住,MVC 比 OutputCache(来自 .NET 2.0)更新得多,因此这可能只是一种不兼容,如果不引入重大 API 更改就无法解决。如果您觉得这足够重要,可以向 MVC 团队报告。
但是,恕我直言,您应该只使用 System.Runtime.Caching.ObjectCache abstract class which also can be extended to be file-based 而不是输出缓存来处理这种情况。它实际上并不缓存页面内容(您只会缓存数据块),但如果您正试图解决这个问题,它仍然可以防止往返数据库。
请注意,您仍然可以将 FileCacheProvider 用于 public 无需登录的页面,但每个需要 AuthorizeAttribute 的操作都应改用 System.Runtime.Caching
提供程序。此外,缓存登录后的页面是一种不寻常的情况,因为它们往往需要在大多数时间实时查看数据。