2009-04-07 15 views
28

Próbuję utworzyć automatyczną mapę witryny ActionResult, która wyświetla prawidłowy plik sitemap.xml. Faktyczne generowanie pliku nie stanowi problemu, ale nie mogę wymyślić, jak wypełnić listę adresów URL w systemie. Oto kod mam tak daleko:Dynamiczna mapa strony w ASP.NET MVC

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

Na przykład załóżmy, że mam dwa kontrolery, a każdy kontroler ma dwa działania z nimi związane:

HelpController

  • Edycja
  • Utwórz

AboutController

  • Firma
  • Zarządzanie

I nie wydaje się dowiedzieć się, jak uzyskać listę adresów URL, takich jak:

+1

Ostatnio, podobnie jak w odpowiedzi na @ eduncan911, najlepszym rozwiązaniem jest użycie http://mvcsitemap.codeplex.com/ aktywnego i zaktualizowanego projektu, obsługa przycinania zabezpieczeń i wygenerowanie pliku sitemap.xml. Może również automatycznie skompresować mapę witryny, jeśli agent ją obsługuje i podzielić mapę witryny na podstrony, jeśli witryna jest zbyt duża, ponieważ standard sitemap.xml ma limit tylko do 50 000 węzłów. – CallMeLaNN

+0

Dzięki CallMeLaNN. Niedawno zaktualizowałem odpowiedź, aby wymienić te punkty i więcej, a także listę witryn, do których się przeniosły. – eduncan911

Odpowiedz

6

Spojrzałem na podejście Maarten Balliauw do każdego komentarza, ale wydaje mi się, że jest to przesada w stosunku do tego, co próbuję zrobić.

Zhakowałem razem tymczasowe rozwiązanie. Po prostu przekazuję kontroler i nazwy akcji, aby wygenerować adresy URL. W celu wygenerowania URL, używam następujący kod:

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

gdzie getUrl jest jak poniżej:

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; 
    } 

Wydaje się to załatwi teraz, chociaż lubię idei o zastosowanie filtru akcji do pewnych działań, które są automatycznie łączone.

0

Próbowałeś coś takiego:

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

Po ponownym przeczytaniu pytanie, widzę, że chcesz coś trochę innego niż przykładzie I, pod warunkiem. Myślę, że musiałbyś odzwierciedlać wszystkie znane kontrolery i ich działania, by dynamicznie budować mapę witryny.

O wiele łatwiej byłoby użyć bazy danych lub pliku mapy witryny, jak uważam to za źródło.

7

Jako wyróżnienia, należy zastanowić się nad przestrzenią nazw modeli i uzyskać wszystkie klasy implementujące IController. Po utworzeniu kolekcji należy zastanowić się, co członkowie (metody) zwracają typ ActionResult.

Być może można utworzyć własny atrybut [SitemapAttribute], który pozwala selektywnie określać metody indeksowania w mapie witryny (np. Index(), ale nie Edycja()). Tak, podoba mi się ten pomysł kontrolowania, które metody (adresy URL) są zapisywane.

To jest doskonałe pytanie, ponieważ myślałem o zrobieniu tego samego. +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. 
    } 
} 

Na jak zrobić odbicie, tu jest dobry artykuł MSDN, aby dostać się wprowadził do refleksji:

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

Dobre pytanie!

+0

Czy możesz podać przykład użycia refleksji do budowania treści? Konkretnie, jak uzyskać adres URL z działania? –

+0

Dziękuję Mike'owi, ale nie mam żadnego kodu. To była tylko sugestia. Poniżej wydają się być inne przykłady. – eduncan911

2

Tak, coraz kontrolerów i działań wydaje mi się być stosunkowo błahe część. Najtrudniejszą rzeczą jest uzyskanie wszystkich możliwych wartości parametrów, które możesz chcieć pokazać w adresach URL mapy witryny. Jeśli masz wzorzec adresu URL, taki jak {controller}/{action}/{id}, nie będziesz w stanie określić poprzez odbicie, co oznacza id lub możliwych wartości. Najlepsze, co możesz zrobić, to określić typ systemu.

To, co przyszło mi na myśl, gdy na to patrzyłem, to fakt, że mapa witryny to po prostu kolejny widok danych Twojej witryny. Więc jeden przypadkowy myślą było to, że jeśli dziedziczą ze sterownika bazowego w Twojej aplikacji i masz metodę na tym kontrolerze bazowej, która ma być realizowana, np

abstract ActionResult SiteMapSnippet(); 

Następnie można utworzyć SiteMapController który wywołuje każdego z pozostałych kontrolerów w roztworze i prosi o podanie ich fragmentu, a następnie renderuje je wszystkie w jednym ostatecznym widoku. Rodzaj złożonego kontrolera, choć nie jest to koncepcja, która została jeszcze dodana do tego szkieletu.

13

Opublikowałem odpowiedź do-it-yourself poniżej. Ale tutaj to pakiet, który robi to po wyjęciu z pudełka dla MVC stron:

http://mvcsitemap.codeplex.com/ (< - starym miejscu, ale z obszernej dokumentacji!)

https://github.com/maartenba/MvcSiteMapProvider/wiki (< - przeniósł się do nowego miejsca, brakuje trochę dokumentacji, a nie jako aktywny)

Zauważ, że robi wiele rzeczy:

  • Automagicznie rejestruje się w trasach MVC odpowiedzieć SEO /sitemap.xm l żądań (nawet jeśli nie ma fizycznego pliku dla /sitemap.xml). Jest to w pełni kompatybilne z wszystkimi robotami wyszukiwarek, które znalazłem, a także przeniesieniem, gdy osiągnie 10.000 itd.
  • Zawiera zestaw częściowych widoków do wykorzystania na potrzeby wbudowanej nawigacji BreadCrumb! Używamy tego dość szeroko, chociaż dynamiczna część danych jest nieco uciążliwa, działa.
  • Zawiera zestaw widoków częściowych dla kontrolowanego menu.
  • Honoruje bity bezpieczeństwa [autoryzuj] kontrolerów i metod działania.

Wszystkie powyższe punkty są kontrolowane z edytowanego i konfigurowanego pliku XML mvc.sitemap. Używałem tego w wielu projektach, aby zrobić 2 lub 3 z powyższych punktów. Wszystko, co można skonfigurować w jednym miejscu, a generowane dynamicznie, jest naprawdę miłe.

Chociaż uważam, że możliwość tworzenia dynamicznych dostawców danych jest nieco uciążliwa (i rażąco narusza każdy rodzaj IoC, który chce się zrobić), wykonuje to zadanie i ładnie się skaluje, gdy obejdzie się buforowanie i użyje własnego.

+8

To jest mapa witryny ASP.NET, a nie plik sitemap.xml dla wyszukiwarki –

+1

@Hightechrider tworzy plik sitemap.xml dla zarejestrowanych map. Włączanie jest trywialne. https://github.com/maartenba/MvcSiteMapProvider/wiki/Exporting-the-sitemap-for-search-engine-indexing – tugberk

+1

To nowe, dzięki za wskazanie i nową lokalizację. –

5

Definiowanie ActionFilterAttribute takiego umieścić na dowolnym sposobem działania, który jest rzeczywistą stronę, którą chcesz wymienić w mapie witryny: -

[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); 
    } 
} 

Teraz dodać coś takiego do globalnego aplikacji kodu startowego, lub używać go w sitemap.xml kodu generującego: -

// 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 
      } 
     } 
    } 

można rozszerzyć klasę atrybutu do być może także częstotliwość aktualizacji podpowiedź.

+0

Witam. Czy ta mapa witryny jest ważna zgodnie z wytycznymi Google? –

Powiązane problemy