如果当前会话IP位于以色列,我想隐藏菜单中的某个页面。这是我尝试过的,但事实上菜单项并没有出现在任何地方。
我测试了GeoIP提供商,它似乎正在工作,我做错了什么?
这里是我如何创建菜单以及如何跳过菜单中我不想要的项目:
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;
}
DynamicNodeProvider
是否已缓存?如果是的话,也许这就是造成这个问题的原因?如何在每个会话中进行缓存,以便每个会话都有其特定的菜单?答案 0 :(得分:1)
我不确定您使用的是哪个版本的MVCSiteMapProvider,但最新版本是非常可扩展的,因为它允许使用内部/外部DI(依赖注入)。
在您的情况下,通过将滑动缓存过期设置为会话超时,可以轻松地为每个会话配置缓存。
// 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
答案 1 :(得分:1)
您的链接没有出现在任何地方的原因是因为SiteMap被缓存并在所有用户之间共享。无论构建缓存的用户请求的状态是什么,所有用户都会看到。
但是,如果没有缓存,查找节点层次结构的性能对于每个请求来说都非常昂贵。通常,支持使用每个SiteMap会话的方法(使用外部DI),但不建议出于性能和可伸缩性的原因。
建议的方法是始终将每个用户的所有预期节点加载到SiteMap的缓存中(或者通过forcing a match伪造它)。然后使用以下方法之一来适当地显示和/或隐藏节点。</ p>
/Views/Shared/DisplayTemplates/
文件夹中)最好将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");