.net mvc: пользовательский атрибут авторизации и провайдер customoutputcache

Я написал собственный провайдер 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)
{
    ....
}

Любая помощь будет оценена


person pas    schedule 23.01.2015    source источник


Ответы (1)


Проблема связана с тем, что ваш CacheValidateHandler не является статическим методом. Я протестировал его, и если вы прокомментируете его содержимое и измените его на статическое, ошибка исчезнет.

Тем не менее, когда вы делаете это таким образом, он также не достигает точки останова в методе, поэтому я не думаю, что это работоспособное решение.

Кажется, об этом много говорят ">в этой ветке, но, похоже, там нет правильного ответа.

Я считаю, что настраиваемое кэширование вывода не предназначено для использования в сочетании с AuthorizeAttribute или это какая-то ошибка MVC. Имейте в виду, что MVC намного новее, чем OutputCache (который был из .NET 2.0), так что, вероятно, это просто несовместимость, которую нельзя решить без внесения критических изменений в API. Если вы считаете, что это достаточно важно, вы можете сообщить об этом команде MVC.

Однако, ИМХО, вам следует просто использовать System.Runtime.Caching.ObjectCache, который также может быть расширен до на основе файлов, а не в кэше вывода, чтобы справиться с этим сценарием. На самом деле он не кэширует содержимое страницы (вы бы просто кэшировали фрагменты данных), но все же может предотвратить обращение к базе данных, если это то, что вы пытаетесь решить.

Обратите внимание, что вы по-прежнему можете использовать свой FileCacheProvider для общедоступных страниц, которые не защищены логином, но каждое действие, требующее AuthorizeAttribute, должно вместо этого использовать поставщика System.Runtime.Caching. Кроме того, это немного необычная ситуация для кэширования страниц, которые находятся за логином, потому что они, как правило, требуют просмотра данных в реальном времени большую часть времени.

person NightOwl888    schedule 23.01.2015
comment
Привет, я пытался изменить CacheValidateHandler на статический, но получаю ту же ошибку. protected static void CacheValidateHandler (контекст HttpContext, данные объекта, ссылка HttpValidationStatus validationStatus) { // validationStatus = OnCacheAuthorization (новый HttpContextWrapper (контекст)); } - person pas; 26.01.2015
comment
обновление: я не получаю сообщение об ошибке, но, как вы написали, это не решение. Я ищу другое решение из ваших предложений - person pas; 26.01.2015