2015-04-20 3 views
2

Я хочу скрыть определенную страницу из меню, если текущий IP-адрес сеанса находится в Израиле. Вот что я пробовал, но на самом деле пункт меню нигде не появляется.
Я протестировал поставщика GeoIP и, похоже, работает, что я делаю неправильно?Видимость отдельных элементов в MvcSiteMapProvider?

Вот как я создан меню и как я стараюсь, чтобы пропустить детали я не хочу в меню:

public class PagesDynamicNodeProvider 
    : DynamicNodeProviderBase 
{ 
    private static readonly Guid KeyGuid = Guid.NewGuid(); 
    private const string IsraelOnlyItemsPageKey = "publications-in-hebrew"; 

    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode siteMapNode) 
    { 
    using (var context = new Context()) 
    { 
     var pages = context.Pages 
        .Include(p => p.Language) 
        .Where(p => p.IsPublished) 
        .OrderBy(p => p.SortOrder) 
        .ThenByDescending(p => p.PublishDate) 
        .ToArray(); 

     foreach (var page in pages) 
     { 


     //********************************************************* 
     //Is it the right way to 'hide' the page in current session 
     if (page.MenuKey == IsraelOnlyItemsPageKey && !Constants.IsIsraeliIp) 
      continue; 

     var node = new DynamicNode(
      key: page.MenuKey, 
      parentKey: page.MenuParentKey, 
      title: page.MenuTitle, 
      description: page.Title, 
      controller: "Home", 
      action: "Page");   

     node.RouteValues.Add("id", page.PageId); 
     node.RouteValues.Add("pagetitle", page.MenuKey); 

     yield return node; 
     } 
    } 
    } 
} 

Вот как я определяю и кэш является ли IP из Израиля:

private const string IsIsraeliIpCacheKey = "5522EDE1-0E22-4FDE-A664-7A5A594D3992"; 
private static bool? _IsIsraeliIp; 
/// <summary> 
/// Gets a value indicating wheather the current request IP is from Israel 
/// </summary> 
public static bool IsIsraeliIp 
{ 
    get 
    { 
    if (!_IsIsraeliIp.HasValue) 
    { 
     var value = HttpContext.Current.Session[IsIsraeliIpCacheKey]; 
     if (value != null) 
     _IsIsraeliIp = (bool)value; 
     else 
     HttpContext.Current.Session[IsIsraeliIpCacheKey] = _IsIsraeliIp = GetIsIsraelIpFromServer() == true; 
    } 
    return _IsIsraeliIp.Value; 
    } 
} 

private static readonly Func<string, string> FormatIpWithGeoIpServerAddress = (ip) => @"http://www.telize.com/geoip/" + ip; 
private static bool? GetIsIsraelIpFromServer() 
{ 
    var ip = HttpContext.Current.Request.UserHostAddress; 
    var address = FormatIpWithGeoIpServerAddress(ip); 
    string jsonResult = null; 
    using (var client = new WebClient()) 
    { 
    try 
    { 
     jsonResult = client.DownloadString(address); 
    } 
    catch 
    { 
     return null; 
    } 
    } 

    if (jsonResult != null) 
    { 
    var obj = JObject.Parse(jsonResult); 
    var countryCode = obj["country_code"]; 

    if (countryCode != null) 
     return string.Equals(countryCode.Value<string>(), "IL", StringComparison.OrdinalIgnoreCase); 
    } 
    return null; 
} 
  1. Является ли DynamicNodeProvider кешем? Если да, возможно, именно это вызывает проблему? Как я могу сделать его кеш за сеанс, поэтому каждый сеанс получает свое конкретное меню?
  2. Правильно ли кэшировать IP за сеанс?
  3. Любые другие подсказки по отслеживанию проблемы?

ответ

1

Причина, по которой ваша ссылка нигде не появляется, связана с тем, что SiteMap кэшируется и делится между всеми, если его пользователи. Независимо от состояния запроса пользователя, который создает кеш, все ваши пользователи будут видеть.

Однако без кэширования производительность поиска иерархии узлов будет действительно дорогостоящей для каждого запроса. В общем, поддерживается подход к использованию сеанса на SiteMap (с внешним DI), но не рекомендуется для повышения производительности и масштабируемости.

Рекомендованный подход - всегда загружать все ожидаемые узлы для каждого пользователя в кеш сайта SiteMap (или подделывать его на forcing a match). Затем используйте один из следующих подходов, чтобы показать и/или скрыть узлы, если это необходимо.

  1. Security Trimming
  2. Встроенный или custom visibility providers
  3. Customized HTML вспомогательные шаблоны (в папке /Views/Shared/DisplayTemplates/)
  4. Произвольный HTML помощник

Лучше думать о SiteMap как иерархическая база данных. Вы делаете немного больше, чем настраиваете структуру данных, и эта структура данных применяется ко всем пользователям приложения. Затем вы делаете запросы по запросу с этими общими данными (объектом SiteMap), которые можно фильтровать по желанию.

Конечно, если ни один из вышеперечисленных вариантов не прикрывайте случай использования, пожалуйста answer my open question as to why anyone would want to cache per user, так как это в значительной степени поражения цели создания сайта карты с.

Вот как вы можете настроить поставщика видимости для фильтрации в этом случае.

public class IsrealVisibilityProvider : SiteMapNodeVisibilityProviderBase 
{ 
    public override bool IsVisible(ISiteMapNode node, IDictionary<string, object> sourceMetadata) 
    { 
     return Constants.IsIsraeliIp; 
    } 
} 

Затем удалите условную логику из вашего DynamicNodeProvider и добавьте поставщик видимости для каждого узла, в котором она применяется.

public class PagesDynamicNodeProvider 
    : DynamicNodeProviderBase 
{ 
    private const string IsraelOnlyItemsPageKey = "publications-in-hebrew"; 

    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode siteMapNode) 
    { 
     using (var context = new Context()) 
     { 
      var pages = context.Pages 
         .Include(p => p.Language) 
         .Where(p => p.IsPublished) 
         .OrderBy(p => p.SortOrder) 
         .ThenByDescending(p => p.PublishDate) 
         .ToArray(); 

      foreach (var page in pages) 
      { 
       var node = new DynamicNode(
        key: page.MenuKey, 
        parentKey: page.MenuParentKey, 
        title: page.MenuTitle, 
        description: page.Title, 
        controller: "Home", 
        action: "Page");   

       // Add the visibility provider to each node that has the condition you want to check 
       if (page.MenuKey == IsraelOnlyItemsPageKey) 
       { 
        node.VisibilityProvider = typeof(IsraelVisibilityProvider).AssemblyQualifiedName; 
       } 
       node.RouteValues.Add("id", page.PageId); 
       node.RouteValues.Add("pagetitle", page.MenuKey); 

       yield return node; 
      } 
     } 
    } 
} 

Для более сложной схемы видимости, вы можете захотеть сделать родительскую поставщику видимости, вызывающую провайдер видимости детей на основе вашей собственной пользовательской логики, а затем установить родительский поставщик видимости по умолчанию в web.config.

<add key="MvcSiteMapProvider_DefaultSiteMapNodeVisibiltyProvider" value="MyNamespace.ParentVisibilityProvider, MyAssembly"/> 

Или, используя внешний DI, вы должны установить значение по умолчанию в конструкторе SiteMapNodeVisibilityProviderStrategy.

// Visibility Providers 
this.For<ISiteMapNodeVisibilityProviderStrategy>().Use<SiteMapNodeVisibilityProviderStrategy>() 
    .Ctor<string>("defaultProviderName").Is("MyNamespace.ParentVisibilityProvider, MyAssembly"); 
+0

Привет, и спасибо за ваш полный ответ. Поставщик видимости выглядит как лучшее решение. Не только это, но и то, является ли IP израильским, уже кэшируется за сеанс, что делает его еще лучше. Теперь я также узнал, что я могу сделать недействительным кеш программным путем, поэтому я собираюсь увеличить продолжительность кеша и сбросить его вручную при изменении меню. В любом случае ** вышеуказанный код не работает **, он не может найти поставщика видимости. – Shimmy

+0

Вот ошибка, которую я получаю: «Экземпляр поставщика видимости с именем« MyNamespace.IsraelVisibilityProvider, MyAssembly »не найден. Проверьте конфигурацию DI, чтобы убедиться, что экземпляр поставщика видимости с этим именем существует и настроен правильно». – Shimmy

+1

Вместо этого вы можете использовать 'node.VisibilityProvider = typeof (IsraelVisibilityProvider) .AssemblyQualifiedName', чтобы гарантировать правильность строки. Кроме того, если вы импортируете пространство имен 'MvcSiteMapProvider.Reflection', вы можете использовать метод расширения typeof (IsraelVisibilityProvider).ShortAssemblyQualifiedName() '. – NightOwl888

1

Я не уверен, какая версия MVCSiteMapProvider вы используете, но последняя версия очень расширяема, так как позволяет использовать внутренний/внешний DI (инъекция на изгиб).

В вашем случае легко настроить кеш за сеанс, используя скользящий кеш-выход, установленный на время сеанса.

Link

// Setup cache 
SmartInstance<CacheDetails> cacheDetails; 

this.For<System.Runtime.Caching.ObjectCache>() 
    .Use(s => System.Runtime.Caching.MemoryCache.Default); 

this.For<ICacheProvider<ISiteMap>>().Use<RuntimeCacheProvider<ISiteMap>>(); 

var cacheDependency = 
    this.For<ICacheDependency>().Use<RuntimeFileCacheDependency>() 
     .Ctor<string>("fileName").Is(absoluteFileName); 

cacheDetails = 
    this.For<ICacheDetails>().Use<CacheDetails>() 
     .Ctor<TimeSpan>("absoluteCacheExpiration").Is(absoluteCacheExpiration) 
     .Ctor<TimeSpan>("slidingCacheExpiration").Is(TimeSpan.MinValue) 
     .Ctor<ICacheDependency>().Is(cacheDependency); 

Если вы используете Старые версии, вы можете попытаться реализовать GetCacheDescription метод в IDynamicNodeProvider

public interface IDynamicNodeProvider 
{ 
    IEnumerable<DynamicNode> GetDynamicNodeCollection(); 
    CacheDescription GetCacheDescription(); 
} 

Здесь приведены подробные сведения о структуре CacheDescription. Link

+0

Спасибо за ваш ответ, но это старый веб-сайт и не включен DI. Есть ли более простой способ достичь этого? Я не хочу слишком много общаться с этим сайтом, поэтому я хочу, чтобы мои изменения были минимальными, насколько это возможно. Есть ли способ кэшировать карту сайта за сеанс? – Shimmy

+0

Это от Нугета. Текущая версия - 4.6.3. Интерфейс 'IDynamicNodeProvider', похоже, не имеет метода' GetCacheDescription'. – Shimmy

+0

Я нашел [это] (http://stackoverflow.com/a/18163187/75500) сообщение, из которого звучит то, что мне нужно, но я хочу избежать включения DI в этот проект. – Shimmy