我正在尝试创建一个自动站点地图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); } }
例如,假设我有两个控制器,每个控制器有两个与之关联的操作:
HelpController
编辑
创建
AboutController
公司
管理
我似乎无法弄清楚如何获取URL的列表:
HTTP://本地主机/帮助/编辑
HTTP://本地主机/帮助/创建
HTTP://本地主机/约/公司
HTTP://本地主机/约/管理
eduncan911.. 13
我在do-it-yourself
下面发布了一个答案.但是这里有一个开箱即用的MVC站点包:
http://mvcsitemap.codeplex.com/(< - 旧网站,但有大量文档!)
https://github.com/maartenba/MvcSiteMapProvider/wiki (< - 迁移到新网站,缺少一些文档,而不是活动)
请注意,它做了很多事情:
自动地将自身注册在MVC路线搜索引擎优化/sitemap.xml响应请求(即使是/sitemap.xml没有物理文件).这与我找到的所有搜索引擎机器人完全兼容,并且当它达到10,000等时滚动.
附带一组部分视图,用于内置BreadCrumb导航!我们使用它非常广泛,虽然动态数据部分有点麻烦,但确实有效.
附带一组部分视图,可以控制Menu.
尊重Controllers和Action方法的[Authorize]安全位.
以上所有要点均由您编辑和配置的单个mvc.sitemap XML文件控制.我现在在许多项目中使用它来做上述两点或三点.让它可以在一个地方配置,并动态生成,非常好.
虽然我发现创建动态数据提供程序的能力有点麻烦(并且严重违反了您希望做的任何类型的IoC),但是一旦绕过缓存并使用自己的缓存,它确实可以完成工作并且可以很好地扩展.
我在do-it-yourself
下面发布了一个答案.但是这里有一个开箱即用的MVC站点包:
http://mvcsitemap.codeplex.com/(< - 旧网站,但有大量文档!)
https://github.com/maartenba/MvcSiteMapProvider/wiki (< - 迁移到新网站,缺少一些文档,而不是活动)
请注意,它做了很多事情:
自动地将自身注册在MVC路线搜索引擎优化/sitemap.xml响应请求(即使是/sitemap.xml没有物理文件).这与我找到的所有搜索引擎机器人完全兼容,并且当它达到10,000等时滚动.
附带一组部分视图,用于内置BreadCrumb导航!我们使用它非常广泛,虽然动态数据部分有点麻烦,但确实有效.
附带一组部分视图,可以控制Menu.
尊重Controllers和Action方法的[Authorize]安全位.
以上所有要点均由您编辑和配置的单个mvc.sitemap XML文件控制.我现在在许多项目中使用它来做上述两点或三点.让它可以在一个地方配置,并动态生成,非常好.
虽然我发现创建动态数据提供程序的能力有点麻烦(并且严重违反了您希望做的任何类型的IoC),但是一旦绕过缓存并使用自己的缓存,它确实可以完成工作并且可以很好地扩展.
如同提及的那样,您希望反映您的模型命名空间并获取实现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
好问题!
我看了一下Maarten Balliauw的方法,但是对于我正在尝试做的事情似乎有些过分.
我已经将一个临时解决方案整合在一起.我只是传递控制器和动作名称来生成URL.为了生成URL,我使用以下代码:
ListurlList = new List (); 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应用于某些自动拉到一起的动作.
定义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); } }
现在将这样的内容添加到您的全局应用程序启动代码中,或者在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().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 } } }
您可以将属性类扩展为可能还包括更新提示的频率.