MVC sitemap.xml

İlk olarak yapmamız gereken sitemize gelen sitemap.xml dosya isteğini yakalamamız ve bir action'a yönlendirmemiz gerekmektedir. Uygulamanın başlangıçında (Global.asax Application_Start()) içinde sitemap.xml yönlendirmeyi yapalım:

[Code]

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("robots.txt");
            routes.MapRoute("sitemap", "sitemap.xml", new { controller = "Rss", action = "SiteMap" });

Sitemize gelen sitemap.xml dosyası isteği RssController.SiteMap() action'a yönledirmiş olduk. RssController.SiteMap() action metotdu stansart bir html yerine sitemap.xml protokolüne uygun çıktı üretmelidir. Böylece arama motorlarının anlayabileceği bir içerik oluşturmuş olunur. XmlWriter kodları aşağıdaki gibi olmalıdır.

[Code]

    public class SitemapActionResult : ActionResult
    {
        private readonly string _siteUrl;
        private readonly List<Page> _pages;

        public SitemapActionResult(string siteUrl, List<Page> pages)
        {
            _siteUrl = siteUrl;
            _pages = pages;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = "text/xsl";
            using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                writer.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
                writer.WriteStartElement("url");
                writer.WriteElementString("loc", _siteUrl);
                writer.WriteElementString("lastmod", DateTime.Now.ToString("yyyy-MM-dd"));
                writer.WriteElementString("changefreq", "daily");
                writer.WriteElementString("priority", "1.0");
                writer.WriteEndElement();

                foreach (var page in _pages)
                {
                    writer.WriteStartElement("url");
                    writer.WriteElementString("loc",
                                              string.Format("{0}/{1}/{2}/{3}", _siteUrl, page.Published.Year,
                                                            page.Published.Month, page.Slug)); 
                    writer.WriteElementString("lastmod", page.LastChanged.ToString("yyyy-MM-dd")); 
                    writer.WriteElementString("changefreq", "daily");
                    writer.WriteElementString("priority", "0.5");
                    writer.WriteEndElement();
                } 
                writer.WriteEndElement(); 
                writer.Flush();
                writer.Close();
            }
        }
    }

Sitemap.xml isteklerini yönlendirdiğimiz RssController.SiteMap() action metodunun SitemapActionResult un isteklere dönmesi için aşağıdaki kodları yazalım.

[C#]

        public ActionResult SiteMap()
        {
            return new SitemapActionResult(
                _settingRepository[SiteSettingKey.SiteUrl],
                _pageRepository.Query()
                    .Where(c => c.IsPublish && c.Published < DateTime.Now)
                    .OrderByDescending(c => c.Published).ToList());
        }

Böylelikle yayında olan sayfaları listeleyip sitemap.xml protokolüne uygun olarak cevap dönmüş olduk.

sitemap.xml görüntüsü

RSS Nedir, Neden Kullanılmalıdır ?

RSS (Really Simple Syndication) web içeriği abonelik formatıdır. RSS içeriği sabit bir xml yapısıdır. <rss> tagları arasında sitede yer tüm içeriği sunabilmektesiniz. Site haritası'na (sitemap.xml) göre çok daha gelişmiş ve detaylı bir veri yapısına sahiptir. Yorumlar, resimler, yayın tarihi web master bilgisine kadar geniş bir veri yapısı ile besleme oluşturabilirsiniz.

RSS ve MVC .NET

Farklı besleme formatlarını destekleyen bir besleme kaynağı oluşturma aracına ihtiyaçımız varsa, SyndicationFeed sınıfı bu ihtiyacımızı karşılamak için en ideal yoldur.  SyndicationFeed nesnesi oluşturup site içeriğinizi feed.items altına doldurarak kendi besleme kaynağınızı oluşturabilirsiniz.

[C#]

        public SyndicationFeed CreateFeed()
        {
            var pages = _pageRepository.Query();
            
            var feed = new SyndicationFeed( 
                    _settingRepository[SiteSettingKey.SiteTitle],
                    _settingRepository[SiteSettingKey.SiteSlogan],
                    new Uri(_settingRepository[SiteSettingKey.SiteUrl]));
            feed.Categories.Add(new SyndicationCategory("Blog"));
            feed.Language = "tr-TR";
            feed.Copyright = new TextSyndicationContent(
                string.Format("{0} {1}", DateTime.Now.Year, _settingRepository[SiteSettingKey.SiteTitle]));
            feed.LastUpdatedTime = DateTime.Now;
            feed.Authors.Add(
                    new SyndicationPerson
                        {
                            Name = _settingRepository[SiteSettingKey.SiteTitle], 
                            Email = _settingRepository[SiteSettingKey.Email]
                        });
            var feedItems = new List<SyndicationItem>();
            foreach (var item in pages)
            {
                var sItem = 
                    new SyndicationItem(
                        item.Title,
                        null,  /*content*/
                        new Uri(string.Format("{0}/{1}/{2}/{3}", 
                                _settingRepository[SiteSettingKey.SiteUrl], 
                                item.Published.Year, 
                                item.Published.Month, 
                                item.Slug)))
                        {
                            Summary = new TextSyndicationContent(item.Summary),
                            Id = item.Slug,
                            PublishDate = item.Created
                        };
                sItem.Links.Add(
                    new SyndicationLink
                        {
                            Title = item.Title,
                            Uri =
                                new Uri(string.Format("{0}/{1}/{2}/{3}", 
                                    _settingRepository[SiteSettingKey.SiteUrl], 
                                    item.Published.Year, 
                                    item.Published.Month, 
                                    item.Slug)),
                            Length = item.ContentHtml.Length,
                            MediaType = "html"
                        });
                feedItems.Add(sItem);
            }
            feed.Items = feedItems;
            return feed;
        }

Yukarıdaki örnekte bir besleme kaynağı (feed) oluşturduk. Besleme kaynağı içerisine sitenin başlığı, url bilgisi, dil bilgisi, yazar bilgisi gibi bir çok bilgi ekledil. Sayfa içeriğinde adece özet bilgisi gönderdik. Hem içerik özetini hemde linki ekledik. Böylece sitemize ait içeriği webe uygun hale getirdik

RSS ve MVC

Rss içeriğimizi oluşturduğumuza göre şimdi sıra içeriklerimizi MVC ile sitemize aktarmaya geldi. Rss içeriğini için aşağıdaki kodlara bakalım.

[C#]

    public class SyndicationFeedResult : ContentResult
    {
        public SyndicationFeedResult(SyndicationFeed feed)
        {
            using (var memstream = new MemoryStream())
            using (var writer = new XmlTextWriter(memstream, System.Text.Encoding.UTF8))
            {
                feed.SaveAsRss20(writer);
                writer.Flush();
                memstream.Position = 0;
                Content = new StreamReader(memstream).ReadToEnd();
                ContentType = "application/rss+xml";
            }
        }
    }

Yukarıdaki kod bloğunda MVC ContentResult sınıfından türetilen SyndicationFeedResult sınıfı içerik olarak .NET'e ait SyndicationFeed nesnesini Rss  formatında dönüştürmektedir. Rss içeriğini CreadFeed() ile webe uygun formata  dönüştürecek SyndicationFeedResult sınıfını action metot ile site beslemesine aktaralım.

[C#]

        public SyndicationFeedResult Feed()
        {
            var feed = CreateFeed(); 
            return new SyndicationFeedResult(feed);
        }

mvc feed görüntüsü

Arama motorları web üzerinden ki aynı içeriğe birden fazla url ile ulaşılması durumunda tekrarlanan içerik olarak algılamaktadır. Arama motorları sitenizi tararken www'li e www'siz iki ayrı içeriği algılarlar. Yani http://www.siteniz.com/ ve http://siteniz.com/  arama motorları için iki farklı tekrar edilen içeriktir. Bu durumda arama motorları tüm sitenizi tekrar eden içerik olarak algılar ve sitenizin bulunabilirliği azalır. Bunu önlemek için 301 Redirect kullanılır. 

301 Redirect

Yapmanız gereken sitenizin www'li veya www'siz versiyonundan birini seçip gelen istekleri  diğer versiyona yönlendirmektir. Genellikle www'li versiyon kullanılmaktadır. Bende örnek olarak www'siz versiyondan gelen istekleri www'li versiyona yönlendireceğim. Yapmamız gereken gelen tüm istekleri kontrol edecek bir MvcHandler yazmak. MvcHandler'ımız gelen istekleri kontrol edecek ve istek adresinde www yoksa isteği www'li versiyona yönlendirecek.

Mvc 301 Redirect


MvcHandler'lar Global.asax içinde yaptığımız Route tanımlarına eklenen nesnelerdir. MvcHandler bir Route işlemi çalışırken hangi Controller'ın seçilmesi gerektiğine karar verirler. Bu işlemi yapabilmek için tüm isteklerin başında çalışırlar. Mvc gelen isteğe göre önce hangi Route tanımının çalışması gerektiğine karar verir daha sonra Route içinde tanımlı Handler'larda hangi Controller sınıfının çalışması gerektiğine karar verir. Böyle her hangi bir Router için tüm istekler "Handle" edebileceğiniz yer MvcHandler'dır.

MvcHandler‘ımız aşağıdaki gibidir.

[cs]

    public class RedirectHandler : MvcHandler
    {
        public RedirectHandler(RequestContext requestContext)
            : base(requestContext) { }

        protected override IAsyncResult BeginProcessRequest(
            HttpContext httpContext,
            AsyncCallback callback, object state)
        {

            if (!httpContext.Request.Url.AbsoluteUri.Contains("://www."))
            {

                httpContext.Response.Status = "301 Moved Permanently";
                httpContext.Response.StatusCode = 301;
                httpContext.Response.AppendHeader(
                    "Location",
                    httpContext.Request.Url.AbsoluteUri
                        .Replace("://", "://www.")
                    );
            }

            return base.BeginProcessRequest(
                httpContext, callback, state);
        }
    }

Route tanımlarımıza yazdığımız RedirectHandler ile çalışarak gerekli Controller'ı seçmesini söylemeliyiz.

[cs]

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("Content/{*pathInfo}");

            routes.MapRoute(
                "DefaultController",
                "{controller}/{action}/{id}",
                new {controller = "Home", action = "Index",id=UrlParameter.Optional});

            RouteHandler<RedirectHandler>.Assign(RouteTable.Routes);

RouteHandler sınıfı MvcHandler sınıflarını Route listelerine ekleyebilecek yardımcı bir sınıfdır.

[cs]

 public class RouteHandler<THttpHandler> : MvcRouteHandler
    where THttpHandler : MvcHandler
    {
        public RouteHandler(IControllerFactory controllerFactory)
            : base(controllerFactory) { }
        public static void Assign(RouteCollection routes)
        {
            using (routes.GetReadLock())
            {

                var routeHandler
                    = new RouteHandler<THttpHandler>(
                        ControllerBuilder.Current.GetControllerFactory());

                foreach (var route in routes
                    .OfType<Route>()
                    .Where(r => (r.RouteHandler is MvcRouteHandler)))
                {

                    route.RouteHandler = routeHandler;
                }
            }
        }
    }