2009-04-07 20 views
28

Sto provando a creare una sitemap automatica ActionResult che restituisce un file sitemap.xml valido. L'effettiva generazione del file non è un problema, ma non riesco a capire come popolare l'elenco di URL nel sistema. Qui è il codice che ho finora:Sitemap dinamica in 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); 
     } 
    } 

Per esempio, supponiamo di avere due controller e ciascun controller ha due azioni ad essi associati:

HelpController

  • Modifica
  • Crea

AboutController

  • impresa
  • gestione

io non riesco a capire come ottenere un elenco di URL del tipo:

+1

Recentemente, come risposta da @ eduncan911, la soluzione migliore è utilizzare http://mvcsitemap.codeplex.com/ progetto attivo e aggiornato, supporto per il trimming di sicurezza e generare sitemap.xml. Inoltre, può comprimere automaticamente la Sitemap se l'agente lo supporta e dividere la sitemap in sub-sitemaps se il sito è troppo grande perché lo standard sitemap.xml ha il limite solo per i nodi 50k. – CallMeLaNN

+0

Grazie CallMeLaNN. Recentemente ho aggiornato la risposta per elencare quei punti e altro, oltre a elencare il sito in cui è stato spostato. – eduncan911

risposta

6

Ho dato un'occhiata all'approccio di Maarten Balliauw per il commento di likwid, ma sembra essere eccessivo per quello che sto cercando di fare.

Ho violato una soluzione temporanea. Sto semplicemente passando il controller e i nomi delle azioni per generare gli URL. Al fine di generare l'URL del, sto utilizzando il seguente codice:

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

dove GetUrl è come qui sotto:

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

Questo sembra fare il trucco per il momento, anche se mi piace l'idea di avere actionfilter applicato a determinate azioni che vengono automaticamente riunite.

0

Hai provato qualcosa di simile:

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

dopo aver riletto la tua domanda, vedo che si desidera qualcosa di un po 'diverso l'esempio che ho fornito. Penso che dovresti riflettere tutti i controllori conosciuti e le loro azioni per costruire dinamicamente una mappa del sito.

Sarebbe molto più semplice utilizzare un database o un file Sitemap come la vostra fonte credo.

7

Come accennato, si desidera riflettere sul proprio spazio dei nomi del modello (i) e ottenere tutte le classi che implementano IController. Una volta che hai la collezione, vuoi riflettere per vedere quali membri (metodi) restituiscono il tipo ActionResult.

Forse è possibile creare il proprio attributo, [SitemapAttribute] che consente di specificare in modo selettivo i metodi da indicizzare nella sitemap (ad esempio, Index(), ma non Edit()). Sì, mi piace l'idea di controllare quali metodi (url) vengono scritti.

Questa è una domanda eccellente perché stavo solo pensando di fare lo stesso. +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. 
    } 
} 

Per informazioni su come fare la riflessione, Ecco un buon articolo di MSDN per ottenere ti ha introdotto alla riflessione:

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

Bella domanda!

+0

Puoi pubblicare un esempio sull'uso del reflection per costruire il contenuto? In particolare come ottenere l'URL dall'azione? –

+0

Mike, ma non ho alcun codice. Era solo un suggerimento. Qui sotto ci sono altri esempi. – eduncan911

2

Così, l'ottenere dei controllori e le azioni mi sembra essere la parte relativamente banale. La parte difficile è riuscire a ottenere tutti i possibili valori dei parametri che potresti voler mostrare nelle url della tua sitemap. Se hai un pattern URL come {controller}/{action}/{id}, allora non sarai in grado di determinare attraverso il riflesso quale sia il significato di id o dei valori possibili. Il meglio che puoi fare è determinare il tipo di sistema.

Quello che mi è successo mentre stavo guardando questo è che una mappa del sito è in realtà solo un'altra vista dei dati del tuo sito. Così un pensiero casuale che avevo era che, se si ereditare da un controller di base nella vostra applicazione, e si dispone di un metodo su quel controller di base che deve essere attuato, ad esempio:

abstract ActionResult SiteMapSnippet(); 

allora si potrebbe creare un SiteMapController che chiama ciascuno degli altri controller nella soluzione e chiede loro il proprio snippet e quindi li visualizza tutti insieme in una vista finale. Una sorta di controller composito, anche se non è ancora un concetto che è stato aggiunto a questo framework.

13

Ho inviato una risposta do-it-yourself in basso. Ma qui è un pacchetto che fa fuori dalla scatola per i siti MVC:

http://mvcsitemap.codeplex.com/ (< - vecchio sito, ma con ampia documentazione!)

https://github.com/maartenba/MvcSiteMapProvider/wiki (< - si trasferisce a nuovo sito, mancano alcuni documenti, e non come attivo)

noti che fa una moltitudine di cose:

  • automagicamente si registri nei percorsi MVC per rispondere agli SEO /sitemap.xm l Richieste (anche se non esiste un file fisico per /sitemap.xml). Questo è completamente compatibile con tutti i robot dei motori di ricerca che ho trovato, oltre a rotolare quando arriva a 10.000, ecc.
  • Viene fornito con un set di viste parziali da utilizzare per la navigazione BreadCrumb integrata! Lo usiamo abbastanza estensivamente, sebbene la porzione di dati dinamica sia un po 'macchinosa, funziona.
  • Viene fornito con un set di viste parziali per il menu da controllare.
  • Onora i bit di sicurezza [Autorizza] dei tuoi controller e metodi di azione.

Tutti i punti sopra riportati sono controllati dal singolo file XML mvc.sitemap modificato e configurato. Ho usato questo in un certo numero di progetti ora per fare 2 o 3 dei punti di cui sopra. È tutto configurabile in 1 posto e generato dinamicamente, è davvero bello.

Anche se trovo la possibilità di creare provider di dati dinamici un po 'macchinosi (e violare grossolanamente qualsiasi tipo di IoC che si desidera fare), ottiene il lavoro fatto e si ridimensiona bene una volta bypassato il loro caching e utilizzato il proprio.

+8

Questa è una sitemap ASP.NET non un file sitemap.xml per un motore di ricerca –

+1

@Hightechrider crea un sitemap.xml per le tue mappe registrate. L'abilitazione è banale. https://github.com/maartenba/MvcSiteMapProvider/wiki/Exporting-the-sitemap-for-search-engine-indexing – tugberk

+1

Questo è nuovo, grazie per la segnalazione e la nuova posizione. –

5

Definire un ActionFilterAttribute come questa per mettere su qualsiasi metodo di azione che è una pagina reale che si desidera elencare nel vostro sito: -

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

Ora aggiungere qualcosa di simile al codice di avvio dell'applicazione globale, o utilizzarlo nel codice di generazione 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 
      } 
     } 
    } 

è possibile estendere la classe attributo da includere forse anche la frequenza di aggiornamento suggerimento.

+0

Ciao. Questa sitemap è valida in base alle linee guida di google? –