c# - MvcSiteMapProvider 中各个项目的可见性?

标签 c# asp.net-mvc session caching mvcsitemapprovider

如果当前 session 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;
}
  1. DynamicNodeProvider 是否已缓存?如果是,也许这就是导致问题的原因?如何让它缓存每个 session ,以便每个 session 都有其特定的菜单?
  2. 缓存每个 session 的 IP 是否正确?
  3. 关于追踪问题还有其他提示吗?

最佳答案

您的链接没有出现在任何地方的原因是站点地图被缓存并在所有用户之间共享。无论构建缓存的用户请求的状态如何,所有用户都会看到。

但是,如果不缓存,则每个请求查找节点层次结构的性能将非常昂贵。一般来说,支持每个 SiteMap 使用 session 的方法(通过外部 DI),但出于性能和可扩展性原因,不建议使用。

推荐的方法是始终将每个用户的所有预期节点加载到 SiteMap 的缓存中(或通过 forcing a match 来伪造它)。然后使用以下方法之一根据需要显示和/或隐藏节点。<​​/p>

  1. Security Trimming
  2. 内置或 custom visibility providers
  3. 自定义 HTML 帮助程序模板(位于 /Views/Shared/DisplayTemplates/ 文件夹中)
  4. 自定义 HTML 帮助器

最好将 SiteMap 视为分层数据库。您只需设置数据结构,并且该数据结构适用于应用程序的每个用户。然后,您可以针对可根据需要进行过滤的共享数据(SiteMap 对象)进行按请求查询。

当然,如果以上选项均不能满足您的使用场景,请answer my open question as to why anyone would want to cache per user ,因为它几乎违背了制作站点 map 的目的。

在这种情况下,您可以通过以下方式设置可见性提供程序来进行过滤。

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");

关于c# - MvcSiteMapProvider 中各个项目的可见性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29751962/

相关文章:

c# - 使用流添加到 azure blob 存储

c# - 持久化 bool 逻辑

node.js - Express.js session 在服务器重新启动时重置

asp.net - SessionState 过期行为

node.js - 在nodejs/expressjs中使用RedisStore

c# - 我可以使用 WPF 窗口构造函数来改变区域性吗?

c# - 在 Controller 上找不到操作方法

javascript - 我可以从 kendo UI MVC 项目中删除什么

asp.net - 在 MVC Controller 中将 JSON 绑定(bind)到 JToken

c# - 使用 Html.Raw 将 ASP.NET 模型序列化为 JSON 时出现 Razor 语法错误