Навигация по SiteMap и строка запроса

В настоящее время я пытаюсь выяснить, как добавить параметры строки динамического запроса в меню навигации по карте сайта. Например, пользователь выбирает источник и редакцию, с которой он хочет работать. У меня есть простая карта сайта, которая создает навигационные ссылки, но параметры, выбранные пользователем, должны быть переданы в строке запроса. Карта по умолчанию выглядит так:

<siteMapNode url="" title=""  description="" >
   <siteMapNode url="~/Image.aspx?location=Our Products" title="Our Products" description="Our Products" />
   <siteMapNode url="~/Headline.aspx?location=Our Authors" title="Our Authors"  description="Our Authors" />
</siteMapNode>

Теперь к ссылкам нужно будет динамически добавлять параметры в зависимости от того, что выбрал пользователь. Например:

<siteMapNode url="~/Image.aspx?location=Our Products&Source=12345&Edition=asdfff" title="Our Products"  description="Our Products" />
<siteMapNode url="~/Headline.aspx?location=Our Authors&Source=12345&Edition=asdfff" title="Our Authors"  description="Our Authors" />

Надеюсь, это довольно ясно. Дайте мне знать, если кому-то понадобится более глубокое объяснение.

Спасибо

3 ответа

Решение

К сожалению, это не поддерживается по умолчанию. Но вы можете реализовать SiteMap.SiteMapResolve событие в вашем Global.asax поймать такие расширенные URL-адреса и позвонить SiteMapProvider.FindSiteMapNode с правильным URL:

private void Application_Start(object sender, EventArgs e)
{
    SiteMap.SiteMapResolve += ResolveCustomNodes;
}

private SiteMapNode ResolveCustomNodes(object sender, SiteMapResolveEventArgs e)
{
    // catch ~/Image.aspx and ~/Headline.aspx
    if (e.Context.Request.AppRelativeCurrentExecutionFilePath.Equals(
        "~/Image.aspx", StringComparison.OrdinalIgnoreCase)
      || e.Context.Request.AppRelativeCurrentExecutionFilePath.Equals(
        "~/Headline.aspx", StringComparison.OrdinalIgnoreCase))
    {
        string location = context.Request.QueryString["location"];
        if (location != null) // ignore everything except location=
            return e.Provider.FindSiteMapNode(
                e.Context.Request.AppRelativeCurrentExecutionFilePath
                "?location=" + HttpUtility.UrlEncode(location));
    }
    return null; // use default implementation;
}

Нет необходимости для пользовательских SiteMapProviderс, это работает с любым провайдером.

Теперь, если вы хотите быть более динамичным, вы можете сделать несколько вещей, например (есть несколько способов):

Отметить все <siteMapNode> теги с частичным соответствием строки запроса со специальным атрибутом, и загрузите этот список, просматривая всю карту сайта. Проблема с этим подходом состоит в том, что это может быть очень неэффективным для некоторых провайдеров карты сайта (провайдер на основе файлов является примером провайдера, который хорошо подходит для такого подхода). Что бы вы сделали тогда, это сказать что-то вроде

<siteMapNode url="~/Image.aspx?location=Our Products"
             queryStringField="location"
             title="Our Products" description="Our Products" />

В коде вы можете найти такие узлы рекурсивно, начав с корневого узла и запомнив все узлы с queryStringField атрибут:

private IEnumerable<SiteMapNode> FindNodesWithQueryString(SiteMapNode node)
{
    if (node["queryStringField"] != null)
        yield return node;
    foreach (SiteMapNode childNode in node.ChildNodes)
    {
        foreach (SiteMapNode matchingNode in FindNodesWithQueryString(childNode))
        {
            yield return matchingNode;
        }
    }
}

Имея этот список в руках и немного помахивая руками, вы сможете сделать тот же трюк. Обратите внимание, что вам, вероятно, нужно кэшировать этот список, потому что SiteMapResolve событие может быть вызвано чаще, чем вы ожидаете. Специально для базы данных SiteMapProviders.

private SiteMapNode ResolveCustomNodes(object sender, SiteMapResolveEventArgs e)
{
    string path = e.Context.Request.AppRelativeCurrentExecutionFilePath;
    foreach (var candidate in from node in FindNodesWithQueryString(
                                                              SiteMap.RootNode)
                              select new {
                                  Url = node.Url,
                                  UrlNoQuery = node.Url.Split('?')[0],
                                  QueryStringField = node["queryStringField"],
                                  Node = node
                              } into x
                              where path.Equals(x.UrlNoQuery, 
                                             StringComparison.OrdinalIgnoreCase)
                              select x)
    {
        string paramValue = context.Request.QueryString[
                                                    candidate.QueryStringField];

        if (paramValue != null)
        {
            string url = candidate.UrlNoQuery + "?" + candidate.QueryStringField
                       + "=" + HttpUtility.UrlEncode(paramValue);
            if (url.Equals(candidate.Url, StringComparison.OrdinalIgnoreCase))
                return candidate.Node;
        }   
    }
    return null;
}

У меня была похожая проблема, связанная с использованием base64, строки в кодировке URL в качестве параметра запроса на карте сайта. Простейшим решением было просто обработать событие MenuItemDataBound и выполнить там мою кодировку:

    string[] url = e.Item.NavigateUrl.Split('?');

    if (url.Length == 2)
    {
        url[1] = WebUtils.encodeString(url[1]);
    }

    e.Item.NavigateUrl = string.Join("?", url);

Мой метод webutils:

    public static string encodeString(string value)
{
    return HttpUtility.UrlEncode(Convert.ToBase64String(Encoding.UTF8.GetBytes(value)));
}

public static string decodeString(string value)
{
    return Encoding.UTF8.GetString(Convert.FromBase64String(HttpUtility.UrlDecode(value)));
}

К сожалению, поставщик SiteMap по умолчанию в ASP.NET не допускает строки запросов.

Это проблема, с которой сталкивались многие люди - достаточно легко написать собственного провайдера, который имеет функцию строки запроса.

Поставщик ниже работал хорошо для меня в течение нескольких лет.

http://www.csharper.net/blog/custom_sitemapprovider_incorporates_querystring_reliance.aspx

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