Использование Web.SiteMap с динамическими URL-адресами (маршрутизация URL)

Я хотел бы сопоставить "приблизительные" совпадения в Web.SiteMap

Поставщик статического файла Sitemap Web.Sitemap работает хорошо, за исключением одной вещи. ЭТО СТАТИКА!

Итак, если бы мне нужно было иметь sitemapnode для каждой из 10000 статей на моей странице, вот так:

  • site.com/articles/1/article-title
  • site.com/articles/2/another-article-title
  • site.com/articles/3/another-article-again
  • ...
  • site.com/articles/9999/the-last-article

Есть ли какое-то сопоставление по шаблону, которое я могу сделать с SiteMap, чтобы сопоставить что-либо в соответствии со статьями?

Или, возможно, на моей странице Webforms есть способ вручную установить текущий узел?

Я нашел "небольшую" справку на этой странице, когда делал это с ASP.Net MVC Framework, но все еще искал хорошее решение для веб-форм.

Я думаю, что мне нужно сделать, это создать собственный поставщик SiteMap

3 ответа

Решение

Это в ответ на комментарий выше. Я не могу опубликовать полный код, но так работает мой провайдер.

Предположим, у вас есть страница article.aspx, и она использует параметр строки запроса "id" для получения и отображения заголовка и тела статьи. Тогда это в Web.sitemap:

<siteMapNode url="/article.aspx" title="(this will be replaced)" param="id" />

Затем вы создаете этот класс:

public class DynamicSiteMapPath : SiteMapPath
{
  protected override void InitializeItem(SiteMapNodeItem item)
  {
    if (item.ItemType != SiteMapNodeItemType.PathSeparator)
    {
      string url = item.SiteMapNode.Url;
      string param = item.SiteMapNode["param"];

      // get parameter value
      int id = System.Web.HttpContext.Current.Request.QueryString[param];

      // retrieve article from database using id
      <write your own code>

      // override node link
      HyperLink link = new HyperLink();
      link.NavigateUrl = url + "?" + param + "=" + id.ToString();
      link.Text = <the article title from the database>;
      link.ToolTip = <the article title from the database>;
      item.Controls.Add(link);
    }
    else
    {
      // if current node is a separator, initialize as usual
      base.InitializeItem(item);
    }
  }
}

Наконец, вы используете этот провайдер в своем коде, как если бы вы использовали статический провайдер.

<mycontrols:DynamicSiteMapPath ID="dsmpMain" runat="server" />

Мой класс сложнее, чем это, но это основы. Вместо того, чтобы использовать параметр строки запроса, вы можете просто проанализировать дружественный URL-адрес, который вы используете, и использовать его вместо этого, чтобы получить правильный контент. Чтобы свести к минимуму дополнительные просмотры БД при каждом запросе, вы можете добавить механизм кэширования в провайдера (заголовок статьи обычно не будет меняться часто).

Надеюсь это поможет.

Я сталкивался с этой проблемой и, честно говоря, не нашел решений, которые бы делали меня счастливым... поэтому я заимствую идеи здесь и там. Мое решение состоит из нескольких частей: а) сделать так, чтобы SiteMapProvider находил реальную страницу, обрабатывающую запрос, и использовал ее узел, и б) использовал стандартные методы для обновления узла карты сайта оттуда.

A) Проблема, с которой я продолжал сталкиваться, заключается в том, что, если бы у меня не было правильного виртуального пути, SiteMap.CurrentNode был бы нулевым, а функция SiteMapResolve вызывала. Чтобы решить эту проблему, я вложил в класс XmlSiteMapProvider и переопределил CurrentNode:

 namespace WebFormTools
{
    class RouteBaseSitemapProvider : XmlSiteMapProvider
    {
        public override SiteMapNode CurrentNode
        {
            get
            {
                var node = base.CurrentNode;


                if (node == null) 
                {
                    // we don't have a node, see if this is from a route
                    var page = HttpContext.Current.CurrentHandler as System.Web.UI.Page;

                    if (page != null && page.RouteData != null)
                    {
                        // try and get the Virtual path associated with this route
                        var handler = page.RouteData.RouteHandler as PageRouteHandler;

                        if (handler != null) {
                            // try and find that path instead.
                            node = FindSiteMapNode(handler.VirtualPath);
                        }
                    }

                }

                return node;
            }
        }
    }
}

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

Для справки здесь есть часть моих файлов Web.Config, Global.asax и SiteMap:

Добавление провайдера

    <siteMap defaultProvider="RouteBaseSitemapProvider">
  <providers>
    <add name="RouteBaseSitemapProvider" type="WebFormTools.RouteBaseSitemapProvider" siteMapFile="Web.sitemap" />
  </providers>
</siteMap>

Маршрут:

            routes.MapPageRoute("EvalRoutes",
            "Evals/{type}/New.aspx",
            "~/Evals/New.aspx");

И Карта сайта:

        <siteMapNode url="~/Evals/New.aspx" title="New Eval - {type}"  description="" />

Б) Я подкласс System.Web.UI.Page, метко названный BaseClass, который добавляет метод для регистрации обработчиков для события SiteMapResolve:

public System.Web.SiteMapNode Process(System.Web.SiteMapNode currentNode)
    {
        if (currentNode == null) return currentNode;

        var page = HttpContext.Current.CurrentHandler as System.Web.UI.Page;

        if (page != null && page.RouteData != null)
        {

            Dictionary<Regex, string> replacements = new Dictionary<Regex, string>();

            // build a list of RegEx to aid in converstion, using RegEx so I can ignore class.  Technically I could also
            foreach (var key in page.RouteData.Values.Keys)
            {
                replacements.Add(new Regex(string.Format("\\{{{0}\\}}", key), RegexOptions.IgnoreCase), page.RouteData.Values[key].ToString());              
            }


            // navigate up the nodes
            var activeNode = currentNode;
            while (activeNode != null)
            {
                // to the replacements
                foreach(var replacement in replacements)
                {
                    activeNode.Title =  replacement.Key.Replace(activeNode.Title, replacement.Value);
                }

                activeNode = activeNode.ParentNode;
            }

        }

        return currentNode;
    }

Мне все еще нужно, чтобы URL-адреса отображались надлежащим образом (они будут использовать URL-адрес страницы, получающей маршрут), то есть без информации о маршрутизации. Я, вероятно, буду использовать пользовательский атрибут в карте сайта, чтобы сообщить узлу, как отображать URL.

Я думаю, что это не совсем ответ на ваш вопрос, но, возможно, это дает вам представление. Однажды я написал класс DynamicSiteMapPath, унаследовавший SiteMapPath. Я использую пользовательский атрибут в каждом <siteMapNode> тег в Web.sitemap, например так:

<siteMapNode url="dynamicpage.aspx" title="blah" params="id" />

Затем класс DynamicSiteMapPath получает значение параметра "id", извлекает содержимое из базы данных и переопределяет отображаемый в данный момент узел элемента карты сайта с правильным заголовком и ссылкой. Это займет немного работы, но когда все сделано правильно, это очень аккуратный способ обеспечения динамической поддержки страниц.

Другие вопросы по тегам