.net mvc:custom authorizeattribute和customoutputcache provider

时间:2015-01-23 10:08:26

标签: c# asp.net-mvc caching outputcache authorize-attribute

我写了一个自定义的outputcache提供程序,它将输出保存在磁盘上,它正常工作,但在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; }
}

自定义AuthorizeAttribute(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)
{
    ....
}

任何帮助将不胜感激

1 个答案:

答案 0 :(得分:2)

问题是由于您的CacheValidateHandler不是静态方法。我测试了它,如果你评论它的内容并将其改为静态,那么错误就会消失。

然而,当你这样做时,它也没有在方法中遇到断点,所以我不认为这是一个可行的解决方案。

似乎有很多关于它的讨论on this thread,但似乎没有任何真正的答案。

我认为自定义输出缓存不是与AuthorizeAttribute一起使用的,或者这是某种MVC错误。请记住,MVC比OutputCache(来自.NET 2.0)要新得多,所以这可能只是一种不兼容,如果不引入破坏性的API更改就无法解决。如果您觉得这很重要,可以向MVC团队报告。

但是,恕我直言,你应该只使用System.Runtime.Caching.ObjectCache抽象类,它也可以扩展为file-based而不是输出缓存来处理这种情况。它实际上并不缓存页面内容(您只需缓存数据块),但如果您尝试解决此问题,它仍然可以防止往返数据库。

请注意,您仍然可以将FileCacheProvider用于不在登录后面的公共页面,但是每个需要AuthorizeAttribute的操作都应该使用System.Runtime.Caching提供程序。此外,缓存登录后面的页面是一种不寻常的情况,因为它们往往需要在大多数时间实时查看数据。