MvcSiteMapProvider如何让一个节点匹配当前url

MvcSiteMapProvider how to make a node match the current url

当前节点为空。我不知道如何让 MvcSiteMapProvider 在这种情况下解析节点。

这是它需要匹配的节点

<mvcSiteMapNode title="Policy" route="Details" typeName="Biz.ABC.ShampooMax.Policy" preservedRouteParameters="id" />

路线如下:

routes.MapRoute(
   "Details",
   "Details/{id}",
   new { Controller = "Object", Action = "Details" }
   ).RouteHandler = new ObjectRouteHandler();

被点击的link:

http://localhost:36695/MyGreatWebSite/Details/121534455762071

路线正常。只是 MvcSiteMapProvider.SiteMaps.Current.CurrentNode 为空。

CurrentNodenull 结果表明传入请求与 SiteMap 中的任何节点都不匹配在您的情况下,有 4 个不同的问题可能导致此问题:

  1. 您正在输入 http://localhost:36695/MyGreatWebSite/Details/121534455762071 的 URL 不(必然)匹配您在路由 "Details/{id}" 中指定的 URL 模式。如果您的站点作为 IIS 应用程序托管在 IISROOT/MyGreatWebSite/.
  2. 下,则可能会出现这种情况
  3. 您的 mvcSiteMapNode 没有指定要匹配的 controlleractionroute 只是一个额外的条件(意味着匹配中只会考虑指定的路由),但还需要提供所有参数才能与路由匹配。
  4. 您正在传递自定义 RouteHandler,这可能会改变路由与 URL 匹配的方式。如果没有看到您的 ObjectRouteHandler 中的代码,则无法判断这是否或如何影响路由与 URL.
  5. 的匹配方式
  6. 您的 mvcSiteMapNode 配置中有一个自定义属性 typeName。除非你指定为ignore this attribute,否则在URL中也需要匹配,即http://localhost:36695/MyGreatWebSite/Details/121534455762071?typeName=Biz.ABC.ShampooMax.Policy.

我建议不要使用自定义 RouteHandler 来匹配 URL。这样做的效果会使您的传入路由(URLs 进入 MVC)与您的传出路由(URLs 生成到 link 到其他页面)行为不同。由于 MvcSiteMapProvider 使用路由的两个部分,如果您只更改传入路由而不同时更改传出路由以匹配,将导致 URL 生成问题。相反,我建议您子类化 RouteBase,您可以在其中控制路线的两侧。有关自定义 RouteBase 子类的示例,请参阅

但是,请注意,常规路由非常强大,开箱即用,对于这个简单的场景,您可能不需要子类化 RouteBase

解决方案

我通过将 mvc 站点地图提供程序项目添加到我自己的解决方案中并单步执行 mvc 站点地图提供程序代码以查看为什么我的节点不匹配来找到答案。有几件事必须改变。我通过执行以下操作修复了它:

Mvc.sitemap

<mvcSiteMapNode title="Policy" controller="Object" action="Details" typeName="Biz.ABC.ShampootMax.Policy" preservedRouteParameters="id" roles="*"/>

RouteConfig.cs

  routes.MapRoute(
      name: "Details",
      url: "details/{id}",
      defaults: new { controller = "Object", action = "Details", typeName = "*" }
  ).RouteHandler = new ObjectRouteHandler();

现在一开始它不想这样工作,但我这样修改了提供者:

RouteValueDictionary.cs(添加通配符以匹配值)

    protected virtual bool MatchesValue(string key, object value)
    {
        return this[key].ToString().Equals(value.ToString(), StringComparison.OrdinalIgnoreCase) || value.ToString() == "*";
    }

SiteMapNode.cs(已更改 requestContext.RouteData.Values

/// <summary>
/// Sets the preserved route parameters of the current request to the routeValues collection.
/// </summary>
/// <remarks>
/// This method relies on the fact that the route value collection is request cached. The
/// values written are for the current request only, after which they will be discarded.
/// </remarks>
protected virtual void PreserveRouteParameters()
{
  if (this.PreservedRouteParameters.Count > 0)
  {
    var requestContext = this.mvcContextFactory.CreateRequestContext();
    var routeDataValues = requestContext.HttpContext.Request.RequestContext.RouteData.Values;// requestContext.RouteData.Values;

我认为第二次修改不是绝对必要的,因为我的请求上下文没有被缓存;如果是的话,它会起作用的。我不知道如何缓存它。

这是第一个使路由值支持通配符 (*) 的修改。这似乎是一个 hack,也许有一个内置的方式。

备注

忽略 typeName 属性:

web.config

<add key="MvcSiteMapProvider_AttributesToIgnore" value="typeName" />

使另一个节点中断:

Mvc.sitemap

<mvcSiteMapNode title="Policies" url="~/Home/Products/HarvestMAX/Policy/List" productType="HarvestMax" type="P" typeName="AACOBusinessModel.AACO.HarvestMax.Policy" roles="*">

所以这就是我没有这样做的原因。