2009-04-07 88 views
28

我正在嘗試創建一個自動站點地圖ActionResult,它輸出一個有效的sitemap.xml文件。文件的實際生成不是問題,但我似乎無法弄清楚如何在系統中填充URL列表。這裏是我的代碼至今: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); 
     } 
    } 

舉例來說,假設我有兩個控制器,每個控制器具有與之相關聯的兩個動作:

HelpController

  • 編輯
  • 創建

AboutController

  • 公司
  • 管理

我似乎無法弄清楚如何獲得URL的喜歡的列表:

+1

最近,像@ eduncan911的回答一樣,最好的解決方案是使用http://mvcsitemap.codeplex.com/活動和更新的項目,支持安全修整並生成sitemap.xml。如果代理支持它,它也可以自動壓縮站點地圖,並且如果站點太大,則將站點地圖分割成子站點地圖,因爲sitemap.xml標準僅限於50k節點。 – CallMeLaNN 2011-03-16 17:14:51

+0

感謝CallMeLaNN。我最近更新了答案,列出了這些要點以及更多內容,並列出了它轉移到的網站。 – eduncan911 2012-09-06 18:42:18

回答

6

我看了Maarten Balliauw的方法,每個likwid的評論,但它似乎是我想要做的矯枉過正。

我一起砍了一個臨時解決方案。我只是傳遞控制器和操作名稱來生成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應用於某些自動拉到一起的動作。

7

正如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

問得好!

+0

你能發表一個使用反射來構建內容的例子嗎?具體如何從操作中獲取URL? – 2011-02-28 18:38:05

+0

THanks Mike,但我沒有任何代碼。這只是一個建議。下面似乎是其他例子。 – eduncan911 2011-02-28 20:56:37

2

因此,控制器和行動讓我看來是比較瑣碎的部分。最難的部分是能夠獲得所有可能的參數值,您可能想要顯示在站點地圖的網址中。如果您的網址格式爲{controller}/{action}/{id},那麼您無法通過反思確定id的含義或可能的值。您可以做的最好的是確定系統類型。

我在看這個的時候發生了什麼,一個網站地圖實際上只是您網站數據的另一個視圖。所以,一個隨機我想過,如果你在你的應用程序一個基本的控制器繼承,你有基礎控制器實現的問題,例如在一個方法:

abstract ActionResult SiteMapSnippet(); 

然後,你可以創建一個SiteMapController其調用解決方案中的每個其他控制器,並要求他們提供他們的片段,然後在最後一個視圖中將它們一起呈現。對一個複合控制器進行排序,儘管這不是一個已經添加到這個框架中的概念。

13

我在下面發佈了一個do-it-yourself的答案。但這裏要說的是做出來的MVC網站盒子的包裝:

http://mvcsitemap.codeplex.com/(< - 舊網站,但有大量的文檔!)

https://github.com/maartenba/MvcSiteMapProvider/wiki(< - 遷新址,缺少一些文檔,而不是主動)

請注意,它的千頭萬緒:

  • 自動地自身註冊MVC中的路線搜索引擎優化/sitemap.xm迴應l請求(即使/sitemap.xml沒有物理文件)。這與我發現的所有搜索引擎機器人完全兼容,並且在達到10,000時滾動。
  • 附帶一組局部視圖用於BreadCrumb導航內置!我們廣泛使用它,雖然動態數據部分有點麻煩,但它確實有效。
  • 附帶一組控制菜單的部分視圖。
  • 尊重您的控制器和操作方法的[授權]安全位。

以上所有點都是由您編輯和配置的單個mvc.sitemap XML文件控制的。我已經在一些項目中使用了這個來做上述兩點或三點。全部配置在1個位置,並且動態生成,非常好。儘管我發現創建動態數據提供者的能力有點麻煩(並且嚴重違反了你希望做的任何類型的IoC),但它確實可以完成工作,並且在您繞過緩存並使用自己的緩存後可以很好地進行擴展。

5

定義一個ActionFilterAttribute像這樣穿上那就是你想在你的站點地圖列出實際的網頁,任何動作方法: -

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

你可以擴展屬性類,也許還可以包括更新提示的頻率。