【问题标题】:Dynamic sitemap in ASP.NET MVCASP.NET MVC 中的动态站点地图
【发布时间】:2009-04-07 23:17:46
【问题描述】:

我正在尝试创建一个输出有效 sitemap.xml 文件的自动站点地图 ActionResult。文件的实际生成不是问题,但我似乎无法弄清楚如何填充系统中的 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);
        }
    }

例如,假设我有两个控制器,每个控制器都有两个与之关联的操作:

帮助控制器

  • 编辑
  • 创建

关于控制器

  • 公司
  • 管理

我似乎无法弄清楚如何获取 URL 列表:

【问题讨论】:

  • 最近,像@eduncan911的回答一样,最好的解决方案是使用mvcsitemap.codeplex.com活动和更新项目,支持安全修剪并生成sitemap.xml。如果代理支持它,它还可以自动压缩站点地图,如果站点太大,它可以将站点地图拆分为子站点地图,因为 sitemap.xml 标准仅限于 50k 节点。
  • 感谢 CallMeLaNN。我最近更新了答案,列出了这些要点以及更多内容,并列出了它移动到的网站。

标签: asp.net-mvc


【解决方案1】:

我在下面发布了do-it-yourself 答案。但这里有一个为 MVC 网站提供开箱即用的包:

http://mvcsitemap.codeplex.com/

https://github.com/maartenba/MvcSiteMapProvider/wiki

请注意,它做了很多事情:

  • 自动在 Mvc 路由中注册以响应 SEO /sitemap.xml 请求(即使 /sitemap.xml 没有物理文件)。这与我发现的所有搜索引擎机器人完全兼容,并且在达到 10,000 时滚动等等。
  • 带有一组局部视图,用于内置 BreadCrumb 导航!我们非常广泛地使用它,虽然动态数据部分有点麻烦,但它确实有效。
  • 附带一组局部视图,用于控制菜单。
  • 尊重控制器和操作方法的 [Authorize] 安全位。

以上所有点都由您编辑和配置的单个 mvc.sitemap XML 文件控制。我现在在许多项目中使用它来完成上述两点或三点。将其全部配置在 1 个位置并动态生成,非常棒。

虽然我发现创建动态数据提供者的能力有点麻烦(并且严重违反了您希望做的任何类型的 IoC),但一旦您绕过它们的缓存并使用您自己的缓存,它确实可以完成工作并很好地扩展。

【讨论】:

  • 这是一个 ASP.NET 站点地图,而不是用于搜索引擎的 sitemap.xml 文件
  • @Hightechrider 它会为您注册的地图创建一个 sitemap.xml。启用是微不足道的。 github.com/maartenba/MvcSiteMapProvider/wiki/…
  • 不幸的是,MvcSiteMap 的项目所有者已经离开了 CodePlex,它在 MvcSitemap 周围有一个蓬勃发展的社区,并搬到了 GitHub,它是这个项目的鬼城(我们已经报告了一些错误,创作它的“单一”人的零响应)。更不用说 CodePlex 项目的文档记录如何,以及 Github 版本如何缺乏一切。嘘。
  • ^- 回来评论我上面的评论。看起来兴趣已经回升,鬼城已经重新充满了积极贡献的人(大约是时候了!)。不过,问题列表显示截至评论发布之日有些不稳定的版本。我正在更新这个答案以指向新的 Github 站点。
  • 回复伊恩默瑟,你是不正确的。这是一个为 MVC 网站生成 /Sitemap.xml 的提供程序。任何人都可以查看链接并查看详细信息。因此,名称“MVC Sitemap Provider”
【解决方案2】:

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

好问题!

【讨论】:

  • 您能发布一个使用反射构建内容的示例吗?具体如何从action中获取url?
  • 谢谢迈克,但我没有任何代码。这只是一个建议。下面似乎是其他示例。
【解决方案3】:

我根据 likwid 的评论查看了 Maarten Balliauw 的方法,但对于我正在尝试做的事情来说,这似乎有点过头了。

我想出了一个临时解决方案。我只是传递控制器和动作名称来生成 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 应用于某些自动组合在一起的动作的想法。

【讨论】:

  • 看起来不错,但是如果我还需要添加一个参数......就像 /About/Management?level='top' 怎么做......它是在 routevalue 中使用 id
【解决方案4】:

像这样定义一个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
            }
        }
    }

您可以扩展属性类,也许还包括更新提示的频率。

【讨论】:

  • 嗨。根据 google 的指南,此站点地图是否有效?
【解决方案5】:

因此,在我看来,控制器和动作的获取似乎是相对微不足道的部分。困难的部分是能够获取您可能希望在站点地图的 url 中显示的所有可能的参数值。如果您有像{controller}/{action}/{id} 这样的URL 模式,那么您将无法通过反射确定id 的含义或可能的值。您能做的最好的事情就是确定系统类型。

当我看到这个时,我想到的是,站点地图实际上只是您站点数据的另一种视图。所以我的一个随机想法是,如果您从应用程序中的基本控制器继承,并且您在该基本控制器上有一个必须实现的方法,例如:

abstract ActionResult SiteMapSnippet();

然后您可以创建一个SiteMapController,它调用解决方案中的每个其他控制器并询问它们的sn-p,然后将它们全部呈现在一个最终视图中。一种复合控制器,虽然这还不是这个框架中添加的概念。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-07
    • 1970-01-01
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多