Динамическая карта сайта в ASP.NET MVC - PullRequest
28 голосов
/ 08 апреля 2009

Я пытаюсь создать автоматический файл Sitemap ActionResult, который выводит действительный файл sitemap.xml. Фактическое создание файла не является проблемой, но я не могу понять, как заполнить список URL-адресов в системе. Вот код, который у меня есть:

    public ContentResult Sitemap()
    {
        XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
        XElement root = new XElement(xmlns + "urlset");

        //some kind of foreach here to get the loc variable for all URLs in the site
        //for each URL in the collection, add it to the root element as here

        //root.Add(
        //    new XElement("url", 
        //        new XElement("loc", "http://google.com"), 
        //        new XElement("changefreq", "daily")));

        using (MemoryStream ms = new MemoryStream())
        {
            using (StreamWriter writer = new StreamWriter(ms, Encoding.UTF8))
            {
                root.Save(writer);
            }

            return Content(Encoding.UTF8.GetString(ms.ToArray()), "text/xml", Encoding.UTF8);
        }
    }

Например, предположим, у меня есть два контроллера, и с каждым контроллером связаны два действия:

HelpController

  • Редактировать
  • Создать

AboutController

  • Компания
  • Управление

Я не могу понять, как получить список URL, например:

Ответы [ 7 ]

13 голосов
/ 11 сентября 2009

Я разместил do-it-yourself ответ внизу. Но вот пакет, который делает это из коробки для сайтов MVC:

http://mvcsitemap.codeplex.com/ (<- старый сайт, но с обширной документацией!) </p>

https://github.com/maartenba/MvcSiteMapProvider/wiki (<- перемещен на новый сайт, не хватает какой-либо документации и не активен) </p>

Обратите внимание, что он делает множество вещей:

  • Автоматически регистрируется в маршрутах Mvc для ответа на запросы SEO /sitemap.xml (даже если для /sitemap.xml нет физического файла). Это полностью совместимо со всеми роботами поисковых систем, которые я нашел, а также переворачивается, когда достигает 10000 и т. Д.
  • Поставляется с набором частичных видов для использования в BreadCrumb встроенная навигация! Мы используем это довольно широко, хотя часть динамических данных немного громоздка, она работает.
  • Поставляется с набором частичных видов для управления меню.
  • Уважает биты безопасности [Authorize] ваших контроллеров и методов действий.

Все вышеперечисленные пункты управляются из одного XML-файла mvc.sitemap, который вы редактируете и настраиваете. Я использовал это в ряде проектов, чтобы сделать 2 или 3 из вышеуказанных пунктов. Иметь возможность конфигурировать все в одном месте и динамически генерировать - это действительно приятно.

Хотя я нахожу возможность создавать провайдеров динамических данных немного громоздкой (и грубо нарушает любой тип IoC, который вы хотите сделать), она действительно выполняет свою работу и хорошо масштабируется, как только вы обходите их кэширование и используете свое. 1030 *

7 голосов
/ 08 апреля 2009

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

Возможно, вы можете создать свой собственный атрибут [SitemapAttribute], который позволит вам выборочно указать, какие методы индексировать в карте сайта (то есть, Index (), но не Edit ()). Да, мне нравится идея контролировать, какие методы (URL) пишутся.

Это отличный вопрос, потому что я просто думал сделать то же самое. +1!

// Controller abstract implements IController
public class HelpController : Controller
{
  public HelpController()
  {
  }

  [Sitemap]
  public ActionResult Index()
  {
    // does get written to the file, cause of [Sitemap]
  }

  public ActionResult Create()
  {
    // does not get mapped to the file
  }

  public ActionResult Edit()
  {
    // does not get mapped to the file
  }

  [Sitemap]
  public ActionResult ViewArticle()
  {
    // would get indexed.
  }
}

Чтобы узнать, как сделать рефлексию, вот хорошая статья MSDN, чтобы познакомить вас с рефлексией:

http://msdn.microsoft.com/en-us/library/ms172331.aspx

Хороший вопрос!

6 голосов
/ 09 апреля 2009

Я посмотрел на подход Мартена Баллиау в ответ на комментарий Ликвида, но, похоже, излишне то, что я пытаюсь сделать.

Я взломал временное решение. Я просто передаю имена контроллеров и действий для генерации URL. Для генерации URL я использую следующий код:

    List<string> urlList = new List<string>();
    urlList.Add(GetUrl(new { controller = "Help", action = "Edit" }));
    urlList.Add(GetUrl(new { controller = "Help", action = "Create" }));
    urlList.Add(GetUrl(new { controller = "About", action = "Company" }));
    urlList.Add(GetUrl(new { controller = "About", action = "Management" }));

где GetUrl как показано ниже:

    protected string GetUrl(object routeValues)
    {
        RouteValueDictionary values = new RouteValueDictionary(routeValues);
        RequestContext context = new RequestContext(HttpContext, RouteData);

        string url = RouteTable.Routes.GetVirtualPath(context, values).VirtualPath;

        return new Uri(Request.Url, url).AbsoluteUri;
    }

Кажется, сейчас это работает, хотя мне нравится идея применять actionfilter к определенным действиям, которые автоматически объединяются.

5 голосов
/ 10 февраля 2010

Определите ActionFilterAttribute, как этот, чтобы добавить любой метод Action, который является реальной страницей, которую вы хотите отобразить в вашей карте сайта: -

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class MVCUrlAttribute : ActionFilterAttribute
{
    public string Url { get; private set; }

    public MVCUrlAttribute(string url)
    {
        this.Url = url;
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        // Put this 'canonical url' into the model (which feeds the view)
        // to help search engines with issues of duplicate content
        filterContext.Controller.ViewData["CanonicalUrl"] = url;
        base.OnResultExecuting(filterContext);
    }
}

Теперь добавьте что-то подобное в стартовый код вашего глобального приложения или используйте его в генерирующем коде sitemap.xml: -

   // Find all the MVC Routes
    Log.Debug("*** FINDING ALL MVC ROUTES MARKED FOR INCLUSION IN SITEMAP");
    var allControllers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsSubclassOf(typeof(Controller)));
    Log.DebugFormat("Found {0} controllers", allControllers.Count());

    foreach (var controllerType in allControllers)
    {
        var allPublicMethodsOnController = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
        Log.DebugFormat("Found {0} public methods on {1}", allPublicMethodsOnController.Count(), controllerType.Name);

        foreach (var publicMethod in allPublicMethodsOnController)
        {
            var mvcurlattr = publicMethod.GetCustomAttributes(true).OfType<MVCUrlAttribute>().FirstOrDefault();
            if (mvcurlattr != null)
            {
                string url = mvcurlattr.Url;
                Log.Debug("Found " + controllerType.Name + "." + publicMethod.Name + " <-- " + url);
                Global.SiteMapUrls.Add(url);  //<-- your code here using url
            }
        }
    }

Вы можете расширить класс атрибута, возможно, включив также частоту подсказок об обновлении.

2 голосов
/ 08 апреля 2009

Итак, получение контроллеров и действий кажется мне относительно тривиальной частью. Сложной является возможность получить все возможные значения параметров, которые вы, возможно, захотите показать в URL-адресах вашей карты сайта. Если у вас есть шаблон URL, такой как {controller}/{action}/{id}, то вы не сможете определить путем размышления, что означает значение id или возможные значения. Лучшее, что вы можете сделать, это определить тип системы.

Что мне пришло в голову, когда я смотрел на это, так это то, что карта сайта - это просто еще одно представление данных вашего сайта. Одна случайная мысль, которая у меня возникла, заключалась в том, что если вы наследуете от базового контроллера в своем приложении и у вас есть метод на этом базовом контроллере, который должен быть реализован, например:

abstract ActionResult SiteMapSnippet();

Затем вы можете создать SiteMapController, который вызывает каждый из других контроллеров в решении и запрашивает их фрагмент, а затем отображает их все вместе в одном конечном виде. Это своего рода составной контроллер, хотя это понятие еще не добавлено в эту платформу.

0 голосов
/ 08 апреля 2009

Взгляните на код, стоящий за «Отладчиком маршрута» Фила Хаака:

http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx

0 голосов
/ 08 апреля 2009

Вы пробовали что-то вроде этого:

http://blog.maartenballiauw.be/post/2008/08/29/Building-an-ASPNET-MVC-sitemap-provider-with-security-trimming.aspx

Перечитав ваш вопрос, я вижу, что вы хотите что-то немного отличное от приведенного мною примера. Я думаю, что вам придется отразить все известные контроллеры и их действия для динамического построения карты сайта.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...