asp.net-mvc ASP.NET MVC 中的动态站点地图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/727961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 23:28:37  来源:igfitidea点击:

Dynamic sitemap in ASP.NET MVC

asp.net-mvc

提问by dp.

I'm trying to create an automatic sitemap ActionResult that outputs a valid sitemap.xml file. The actual generation of the file is not a problem, but I can't seem to figure out how to populate the list of URL's in the system. Here is the code I have so far:

我正在尝试创建一个自动站点地图 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);
        }
    }

For instance, suppose I have two controllers, and each controller has two actions associated with them:

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

HelpController

帮助控制器

  • Edit
  • Create
  • 编辑
  • 创建

AboutController

关于控制器

  • Company
  • Management
  • 公司
  • 管理

I can't seem to figure out how to get a list of URL's like:

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

采纳答案by dp.

I took a look at Maarten Balliauw's approach per likwid's comment, but it seems to be overkill for what I'm trying to do.

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

I've hacked together a temporary solution. I'm simply passing the controller and action names to generate the URL's. In order to generate the URL's, I'm using the following code:

我已经破解了一个临时解决方案。我只是传递控制器和动作名称来生成 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" }));

where GetUrl is as below:

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

This seems to do the trick for now, though I do like the idea of having actionfilter's applied to certain actions that get pulled together automatically.

现在这似乎可以解决问题,尽管我确实喜欢将 actionfilter 应用于某些自动组合在一起的操作的想法。

回答by eduncan911

I posted a do-it-yourselfanswer down below. But here is a package that does it out of the box for MVC sites:

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

http://mvcsitemap.codeplex.com/(<- old site, but with extensive documentation!)

http://mvcsitemap.codeplex.com/(<-旧网站,但有大量文档!)

https://github.com/maartenba/MvcSiteMapProvider/wiki(<- moved to new site, lacking some documentation, and not as active)

https://github.com/maartenba/MvcSiteMapProvider/wiki(<-移动到新站点,缺少一些文档,并且不那么活跃)

Note that it does a multitude of things:

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

  • Automagically registers itself in the Mvc routes to respond to SEO /sitemap.xmlrequests (even though there is no physical file for /sitemap.xml). This is completely compatible with all search engine robots I've found, as well as rolling over when it gets to 10,000, etc.
  • Comes with a set of partial views to use for BreadCrumbnavigation built-in! We use this quite extensively, though the dynamic data portion is a bit cumbersome, it does work.
  • Comes with a set of partial views for Menu to be controlled as well.
  • Honors the [Authorize] security bits of your Controllers and Action methods.
  • 自动地将自身注册在MVC路线回应SEO /sitemap.xml请求(即使是/sitemap.xml没有物理文件)。这与我发现的所有搜索引擎机器人完全兼容,并且在达到 10,000 时会滚动,等等。
  • 带有一组用于内置BreadCrumb导航的局部视图!我们非常广泛地使用它,虽然动态数据部分有点麻烦,但它确实有效。
  • 还带有一组用于控制菜单的局部视图。
  • 尊重您的控制器和操作方法的 [授权] 安全位。

All of the above points are controlled from the single mvc.sitemap XML file you edit and configure. I've used this in a number of projects now to do 2 or 3 of the above points. Have it all configurable in 1 place, and dynamically generated, is really nice.

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

Though I find the ability to create dynamic data providers a bit cumbersome (and grossly violates any type of IoC you wish to do), it does get the job done and scales nicely once you bypass their caching and use your own.

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

回答by eduncan911

As likwid mentions, you want to reflect upon your model(s) namespace and obtain all classes that implement IController. Once you have the collection, you want to reflect to see what Members (methods) return the type ActionResult.

正如 likwid 提到的,您希望反映您的模型命名空间并获取实现 IController 的所有类。拥有集合后,您要反映以查看哪些成员(方法)返回 ActionResult 类型。

Perhaps you can create your own attribute, [SitemapAttribute] that lets you selectively specify what methods to index in the sitemap (i.e., Index(), but not Edit()). Yeah, I like that idea of controlling which methods (urls) gets written.

也许您可以创建自己的属性,[SitemapAttribute],它允许您有选择地指定要在站点地图中索引的方法(即,Index(),而不是 Edit())。是的,我喜欢控制写入哪些方法(网址)的想法。

This is an excellent question because I was just thinking of doing the same. +1!

这是一个很好的问题,因为我只是想这样做。+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.
  }
}

For how to do reflection, here's a good MSDN article to get you introduced to reflection:

关于如何进行反射,这里有一篇很好的 MSDN 文章,可以让您了解反射:

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

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

Good question!

好问题!

回答by Ian Mercer

Define an ActionFilterAttributelike this to put on any Action method that is an actual page that you want to list in your sitemap:-

定义一个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);
    }
}

Now add something like this to your Global application start code, or use it in your sitemap.xml generating code:-

现在将这样的内容添加到您的全局应用程序启动代码中,或者在您的 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
            }
        }
    }

You can extend the attribute class to perhaps also include the frequency of update hint.

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

回答by Paul

So, the getting of the controllers and actions seems to me to be the relatively trivial part. The hard part is being able to get all the possible parameter values that you might want to show in the urls of your sitemap. If you have a URL pattern like {controller}/{action}/{id}, then you're not going to be able to determine through reflection what the meaning of idis, or the possible values. The best you can do is determine the system type.

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

What occurred to me as I was looking at this is that a sitemap is really just another view of your site's data. So one random thought I had was that if you inherit from a base controller in your app, and you have a method on that base controller that has to be implemented, e.g.:

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

abstract ActionResult SiteMapSnippet();

Then you could create a SiteMapControllerwhich calls each of the other controllers in the solution and asks them for their snippet, and then renders them all together in one final view. Sort of a composite controller, though that's not a concept that's been added to this framework yet.

然后您可以创建一个SiteMapController调用解决方案中的每个其他控制器并询问他们的代码片段,然后将它们一起呈现在一个最终视图中。有点像复合控制器,虽然这还不是一个被添加到这个框架中的概念。

回答by user88363

Have you tried something like this:

你有没有尝试过这样的事情:

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

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

After re-reading your question, I see that you want something a little different than the example I provided. I think you would have to reflect all of the known controllers and their actions to build a sitemap dynamically.

重新阅读您的问题后,我发现您想要的东西与我提供的示例略有不同。我认为您必须反映所有已知的控制器及其操作才能动态构建站点地图。

It would be far simpler to use a database or sitemap file as your source I think.

我认为使用数据库或站点地图文件作为您的来源会简单得多。

回答by defeated

Take a look at the code behind Phil Haack's "Route Debugger":

看看 Phil Haack 的“Route Debugger”背后的代码:

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

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