<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>http://www.example.com/</loc> <lastmod>2005-01-01</lastmod> <changefreq>monthly</changefreq> <priority>0.8</priority> </url> </urlset>
که یک فایل XML متشکل از یک تگ urlset است و این تگ نیز حاوی یک یا چند تگ url میباشد. با توجه به تعاریف بالا به یک چنین کلاسی خواهیم رسید:
public enum ChangeFreq { Always, Hourly, Daily, Weekly, Monthly, Yearly, Never } [XmlElement("loc")] public string Url { get; set; } [XmlElement("lastmod")] public DateTime? LastModified { get; set; } public bool ShouldSerializeLastModified() { return LastModified.HasValue; } [XmlElement("changefreq")] public ChangeFreq? ChangeFrequency { get; set; } public bool ShouldSerializeChangeFrequency() { return ChangeFrequency.HasValue; } [XmlElement("priority")] public float? Priority { get; set; } public bool ShouldSerializePriority() { return Priority.HasValue; } }
دقت داشته باشید که چون پروپرتیهای LastModified ، ChangeFrequency و Priority از نوع Nullable تعریف شدهاند، پس باید کاری کنیم در صورتیکه این پروپرتیها نال بودند سریالیز نشوند. بدین منظور از تابع ShouldSerialize[MemberName] استفاده میشود. این تابع جزئی از دات نت است. کافی است بعد از ShouldSerialize نام پروپرتی را ذکر کنید. حال به کلاس دیگری نیاز داریم تا لیستی از کلاس فوق را دربر داشته باشد.
[XmlRoot("urlset",Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")] public class SiteMp { private readonly List<Location> _locations; public SiteMp() { _locations = new List<Location>(); } [XmlElement("url")] public List<Location> Locations { get { return _locations; } set { foreach (var location in value) { Add(location); } } } public void Add(Location location) { _locations.Add(location); } }
حال برای پردازش کلاس بالا لازم است ActionResultی را طراحی کنیم تا خروجی Response را به فرمت XML پردازش کند:
public class XmlResult : ActionResult { private readonly object _objectToSerialize; public XmlResult(object objectToSerialize) { _objectToSerialize = objectToSerialize; } public override void ExecuteResult(ControllerContext context) { if (_objectToSerialize == null) return; context.HttpContext.Response.Clear(); var xmlSerializer = new XmlSerializer(_objectToSerialize.GetType()); context.HttpContext.Response.ContentType = "text/xml"; xmlSerializer.Serialize(context.HttpContext.Response.Output, _objectToSerialize); } }
و در آخر یک کنترلر ساخته و به صورت زیر از آن استفاده میکنیم:
public class SiteMapController : Controller { // GET: SiteMap public ActionResult Index() { SiteMp siteMap = new SiteMp(); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/Index" }); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/NewRequest", ChangeFrequency = Location.ChangeFreq.Always, LastModified = DateTime.UtcNow, Priority = 0.5f }); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/FindRequest", ChangeFrequency = Location.ChangeFreq.Always, LastModified = DateTime.UtcNow, Priority = 0.5f }); siteMap.Add(new Location { Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/ContactUs/Index", ChangeFrequency = Location.ChangeFreq.Daily, LastModified = DateTime.UtcNow, Priority = 0.5f }); return new XmlResult(siteMap); }
اگر دقت کنید لینکهای ثابت باید به صورت دستی اضافه شوند. سناریویی را تصور کنید که لینکها زیاد باشند(جدای از لینک هایی که از دیتابیس لود میشوند) این کار کمی ناجور به نظر میرسد. در اینجا میخواهیم از طریق امکانات ،Reflection عمل اضافه کردن لینک به صورت خودکار انجام شود.
public class ControllerScanner { public static List<string> ScanAllControllers(HttpRequestBase requestBase) { Assembly asm = Assembly.GetAssembly(typeof(MvcApplication)); var controllerActionlist = asm.GetTypes() .Where(type => typeof (Controller).IsAssignableFrom(type)) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)) .Where((returnType => returnType.ReturnType == (typeof(ViewResult)) || returnType.ReturnType==(typeof(ActionResult)))) .Select( x => new { Controller = x.DeclaringType.Name, Action = x.Name, ReturnType = x.ReturnType.Name }) .OrderBy(x => x.Controller).ThenBy(x => x.Action).Distinct().ToList(); if (requestBase.Url == null) return null; var url = requestBase.Url.GetLeftPart(UriPartial.Authority); return controllerActionlist.Select(controller => $"{url}/{controller.Controller}/{controller.Action}").ToList(); } }
حال از کلاس بالا در کنترلر SiteMap به صورت زیر استفاده میکنیم :
public class SiteMapController : Controller { // GET: SiteMap public ActionResult Index() { var siteMap = new SiteMap(); var controllers = ControllerScanner.ScanAllControllers(Request); foreach (var controller in controllers) { siteMap.Add(new Location { Url = controller, ChangeFrequency = Location.ChangeFreq.Always, LastModified = DateTime.UtcNow, Priority = 0.5f }); } return new XmlResult(siteMap); } }
در آخر نیز سطر زیر را به سیستم مسیریابی اضافه نمایید تا در صورت درخواست فایل sitemap.xml اکشن Index از کنترلر SiteMap فراخوانی شود.
routes.MapRoute( "SiteMap", // Route name "sitemap.xml", // URL with parameters new { controller = "Sitemap", action = "Index", name = UrlParameter.Optional, area = "" } );