Post Page Advertisement [Top]

Merhaba,
Bu yazımızda MVC projelerinde nasıl seo (arama motoru dostu linkler) bağlantı oluşturacağımızı anlatmaya çalışacağım. Bu oluşturduğumuz web sitelerinin google, bing, yandex gibi arama motorlarında aranabilir olması için önemli bir konu. Örneğin bir film sitemiz olduğunu düşünelim.


Sitemizde film bağlantılarının .../FilmDetay/5 şeklinde olması yerine .../FilmDetay/demir-adam-3 şeklinde olması hem sitemizin arama motarları tarafından kolayca taranıp bulunabilmesini sağlar hem de iyi bir kullanıcı deneyimi oluşturur.

Okurken ne dinlemeli ?

Çok basit ve sade bir şekilde seo url oluşturmaya çalışalım.İlk olarak bir MVC projesi oluşturalım.



Projede zaten var olan bootstrap.css ve bootstrap.js front-end framework'ünü sitemizi biçimlendirmek için kullanacağız. Diğer css ve js 'leri dilerseniz silebilirsiniz. Senaryomuz şöyle ilerleyecek bir film listesi sayfamız olacak bu Index.cshtml ve buradan film detaylarına gittiğimiz Detail sayfamız olacak.
İlk olarak Film class'ını oluşturarak başlayalım.

    public class Films
    {
        public int Id { get; set; }
        public string Title { get; set; }

    }

Index sayfamızın aşağıdaki gibi html 'ini oluşturalım.

@model List<SeoFriendly.Models.Films>
@{
    ViewBag.Title = "Home Page";
}

<div class="row">
    <table style="border-collapse:separate;border-spacing: 2px 5px" width="100%">
        @foreach (var item2 in Model)
        {
            <tr>
                <td width="13%">
                    <a class="pull-left" href='@Url.FilmSeoLink(item2)'>
                        <img class="media-object" src="http://placehold.it/400x200/" alt="">
                    </a>
                </td>
                <td width="72%"><p>@item2.Title</p></td>
                <td width="5%"></td>
            </tr>

        }
    </table>
</div>

Ve controller tarafında bir örnek film listesi oluşturup view'e bind etmeye çalışalım.

        [Route("~/")]
        [Route("Home")]
        public ActionResult Index()
        {
            List<Films> filmlist = new List<Films>();
            filmlist = GetFilms();
            return View(filmlist);
        }

        public List<Films> GetFilms()
        {
            List<Films> filmlist = new List<Films>();
            filmlist.Add(new Films { Id = 1, Title = "Iron Man 3" });
            filmlist.Add(new Films { Id = 2, Title = "Batman Begins" });
            filmlist.Add(new Films { Id = 3, Title = "Superman vs Batman Dawn of Justice" });
            filmlist.Add(new Films { Id = 4, Title = "Amazing Spiderman" });
            return filmlist;
        } 

Dikkat etmemiz gereken noktalardan ilki Route attribute'u yani contoller'e ismi ile değil (Index) Route özelliğinin içine yazdığımız (Home) dilediğimiz isim ile erişebilecek olmamız. İlerleyen kısımda oluşturacağımız film detay sayfası içinde Route "Detail" ismini kullanacağız. Tabii ki  .Net MVC projelerimizde Route özelliği default olarak gelmiyor. Biz  App_Start klasörü altındaki RouteConfig.cs sınıfında aşağıdaki kodu ekleyerek bu özelliği aktive etmiş oluyoruz.

routes.MapMvcAttributeRoutes();

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapMvcAttributeRoutes();

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        } 

Diğer dikkat etmemiz gereken nokta ise  href='@Url.FilmSeoLink(item2)peki bu nasıl olacak? a html elementinin linkini MVC'nin URL sınıfını override ederek özelleştirmiş oluyoruz. Bunun için aşağıdaki sınıfı ve metodları oluşturuyoruz. Aslında seo url 'leri üreten sınıf da bu sınıf olmuş olacak.

    public static class SeoFriendlyLink
    {
        public static string FilmSeoLink(this UrlHelper urlHelper, Films entity)
        {
            string title = entity.Title;
            title = FriendlyURLTitle(title);
            return string.Format("/Detail/{0}/{1}", entity.Id.ToString(), title);
        }

        public static string FriendlyURLTitle(string pTitle)
        {
            pTitle = pTitle.Replace(" ", "-");
            pTitle = pTitle.Replace(".", "-");
            pTitle = pTitle.Replace("ı", "i");
            pTitle = pTitle.Replace("İ", "I");

            pTitle = String.Join("", pTitle.Normalize(NormalizationForm.FormD) // türkçe karakterleri ingilizceye çevir.
                    .Where(c => char.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark));

            pTitle = HttpUtility.UrlEncode(pTitle);
            return System.Text.RegularExpressions.Regex.Replace(pTitle, @"\%[0-9A-Fa-f]{2}", "");
        }
    }
Aslında FilmSeoLink metodumuz ile film item 'ini alarak başlığından dilediğimiz standartta bir bağlantı oluşturuyoruz. Bu metodda dikkat etmemiz gereken nokta ise FrinedlyURLTitle metodu çünkü browserler url isteği yaparken özel karakterleri boşluk , . gibi veya türkçe karakterleri(ç,ş) kabul etmiyor ve istek yapılırken hata oluyor. İşte bu method başlığı ingilizceleştiriyor ve özel karakterleri url(bağlantı)'den siliyor.

Örneğin FilmDetay/pi'nin yaşamı  gibi bir bağlantı geçersiz olacağında bunu FilmDetay/pinin_yasami haline getiriyor.

Şimdi detay sayfamızın önce View sonra da Contoller'ını oluşturalım.

@model SeoFriendly.Models.Films
@{
    ViewBag.Title = "Detail";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
    <div class="col-lg-12">
        <h1 class="page-header">
           @Model.Title
        </h1>
        <img class="img-responsive" style="padding: 20px 0px;" src="http://placehold.it/700x400/" alt="">
        <p>Film info created by SeoFriendly</p>
    </div>
</div>

Contoller: 
        [Route("Detail/{Film_Id}/{seo_text}")]
        public ActionResult Detail(int Film_Id,string seo_text)
        {
           Films film = new Films();
            film = GetFilms().Where(x => x.Id == Film_Id).FirstOrDefault();
            if (SeoFriendlyLink.FriendlyURLTitle(film.Title) != seo_text)
            {
                return HttpNotFound();
            }

            return View(film);
        }

     Detail Controller'ın Route'ını oluştururken FimSeoLink metodunda oluşturduğumuz standartın aynısı olmasına dikkat edelim  ve de Detail ActionController'ın parametre isimlerinin Route'deki ile aynı olmasını sağlayalım.

    Detay sayfasının işleyişinde fark ettiğiniz üzere filmi Id'sinden buluyoruz ve bu ID'yi de bağlantıda içeriyoruz. Eğer basit düşünüp Id yeterli diyip  seo url bağlantı linki ile sistemde kayıtlı olan (id 'den bulunan) filmin başlığı ile karşılaştırma yapmazsanız güç durumlar yaşayabilirsiniz.

 if (SeoFriendlyLink.FriendlyURLTitle(film.Title) != seo_text)

    Yapmazsanız  insanlar bağlantı başlığını değiştirip Id' yi değiştirmese bile aynı sayfayı görürler. Bu bir sorun mu ? Değil ama kötüye kullananlar olabilir.   
Örneğin :  FilmDetay/5/DemirAdam-3  linkini sadece Id =5 'i kontrol edersek aşağıdaki şu link de çalışır, aynı sayfaya gider.

FilDetay/5/DemirAdamvsBatman_filmi_vizyonda

İşte bunu önlemek için DemirAdamvsBatman_filmi_vizyonda titlesi ile Id'sı 5 olan filmin title'sini karşılaştırmak faydalı olacaktır.

Şimdi projemizi derleyip çalıştıralım.



Herhangi bir filmin detayına tıklayalım.



Okuduğunuz için, teşekkürler, tam projeyi buradan indirip inceleyebilirsiniz.

Sağlıkla kalın, huzurlu olun :)

21 yorum:

  1. Çok teşekkür ederim Sağolasın beni büyük bir dertten kurtardın

    YanıtlaSil
  2. Faydalı olabildiysem ne mutlu :) okuduğunuz için teşekkürler.

    YanıtlaSil
  3. Merhaba Sema Hanım,
    Makaleniz gerçekten çok faydalı oldu. Teşekkür ederim.
    Ben adres çubuğundan detail ve id yi kaldırmak istiyorum. Yani sadece site adı ve sonrasında detayı görüntülenen sayfanın seolinki görünsün istiyorum. Bu mümkün olur mu acaba? Yardımcı olabilir misiniz?

    YanıtlaSil
    Yanıtlar
    1. Merhaba,
      İlginiz için teşekkür ederim, tabii ki mümkündür fakat dikkat ederseniz burada detail sayfayı id 'den bulup getiriyor ,id yi kaldırır iseniz , detay sayfayı title 'den getirebilirsiniz. Title'lerin unique olmasına da dikkat edersiniz.Teşekkürler.

      Sil
    2. Projemizde id,ürünad ve ürünboyut var.id gelsin istemiyoruz ama title ları kişi girdiği için uniq yapamıyoruz.Nasıl yapılabilir yardımcı olur musunuz?

      Sil
    3. Merhaba, linkde unique bir alan olmalı başka türlü istenilen veriye ulaşamazsınız, unique alanı şifreleyip, maskeleyip linkte tutabilirsiniz.

      Sil
  4. Öncelikle bu güzel örnek için teşekkür ederim.
    Ama Ben aşağıdaki gibi bir hata aldım.Sebebi nedir.?
    Derleme Hatası
    Açıklama: Bu isteği yerine getirmek için gereken kaynak derlenirken bir hata oluştu. Lütfen aşağıdaki özel hata ayrıntılarını gözden geçirin ve kaynak kodunuzu uygun biçimde değiştirin.

    Derleyici Hata İletisi: CS1617: /langversion için '6' seçeneği geçersiz; ISO-1, ISO-2, 3, 4, 5 veya Default olmalıdır

    Kaynak Hatası:


    [İlişkili kaynak satırı yok]

    YanıtlaSil
    Yanıtlar
    1. Merhaba,

      Framework versiyonu ile ilgili hata almışsınız aşagıdaki linkte answer 5 'i deneyebilir misiniz ?

      https://stackoverflow.com/questions/31868803/error-invalid-option-6-for-langversion-must-be-iso-1-iso-2-3-4-5-or-defa

      Sil


    2. Evet dediğiniz gibi WebConfig dosyasındaki langversiyon:6 değil 5 yapınca çalıştı. ozaman Mvc de yapılan örneklerin framework versiyonları önemli değil mi?
      yani eski versiyon bir örneği yeni versiyona aktarırken hata alabilir mi????

      Sil
    3. Merhaba,
      Visua Studio genellikle kendisi convert ediyor. Ama bazen böyle hatalar olabiliyor.

      Sil
  5. Merhaba, FilmSeoLink ve FriendlyURLTitle metodlarını nereye oluşturuyoruz.

    YanıtlaSil
    Yanıtlar
    1. '@Url.FilmSeoLink(item2) burada hata alıyorum. Teşekkürler.

      Sil
    2. class oluşturup içine yazıyorsunuz makalede yazıyor dikkatli okuyun

      Sil
  6. Merhabalar,
    Bunu gerekli olan entityler için yapma şansım var mıdır ? Yani gerekli olan her entity için bunu yazmak yerine Generic olarak yapabilir miyim ? Class static olduğu için olmuyor diye biliyorum ama.

    YanıtlaSil
    Yanıtlar
    1. Merhaba Haluk ne demek istediğini tam anlamadım , örnek verebilir misin ?

      Sil
    2. Merhabalar,

      Siz metoda Films entitysini göndermişsiniz. Yani metod Films entitysini alıyor.Buradaki gelen entity kısmı generic olabilir mi ? Entity tipi belirtmeden yani.

      Sil
    3. evet, elbette yapabilirsin, örnek bir makaleyi paylaşıyorum : http://www.gokhan-gokalp.com/generic-repository-ve-unit-of-work-kullanarak-basic-infrastructure-tasarlamak/

      Sil
  7. Merhaba projeniz için teşekkürler.
    @Url.FilmSeoLink forech içinde fonksiyona gitiyor hata veriyoru forech dışına alıp string gönderdiğimde çalışıyor.
    Birde performans açısından veri tabanında bir alan oluşturup seo-link adında kayıt öncesi hazır link tutmam mı daha iyi olur.

    YanıtlaSil
  8. Merhaba, okuduğunuz için teşekkürler. Projeyi tekrar indirip çalıştırıdım bir hata alamadan çalıştı. @Url.FilmSeoLink 'de aldığınız hatayı google'de aratıp çözmenizi tavsiye ederim. Seo linki veri tabanında tutarsanız örneğin editör içerik başlığını güncellediğinde veri tabanını da güncellemeniz gerekir. İstek anında url'in oluşması performans sıkıntısı yaratmaz.

    YanıtlaSil
  9. Merhaba, makalenizi okuyup işlemleri tamamladım sona geldim. herşey sorunsuz görüntüleniyor fakat benim aklıma takılan bir soru var:

    Ben çoklu dil seçeneği olan bir site yapıyorum. tüm haberlerin listelendiği sayfada dil menüsünden ingilizce veya herhangi bir dilin üzerine geldiğimde çeviri oluyor.yani : deneme.com/haberler, deneme.com/en/news oluyor. fakat yukarıdaki detay sayfası örneğinizde bu nasıl olacak bilemedim. aslında çalışıyor. yani haberler sayfasında dili ingilizceye çevirip ilerleyince : deneme.com/haberler/yeni-filmler-geldi, deneme.com/en/news/incoming-new-films oluyor. benim isteğim herhangi bi haberin içindeykende haberler sayfasına geri dönmeden başka dildeki sayfasını çağırabileyim.

    MVC'de Çoklu dil seçenekli aynı zamanda SEO Route kullanıp Detay sayfası örneği bulamadım. Sizin bu konu hakkında fikriniz vardır ve umarım problemi doğru anlatabilmişimdir :)

    YanıtlaSil
  10. Merhaba, okuduğunuz için teşekkürler. Bildiğim kadarı ile SEO Url'i dile göre otomatik çevirisini yapılamıyor. İnglizce url'leri ve content'i hazırlayıp. deneme.com/en ise ingilizce içerik oluşturduğunuz veri tabanından ya da tablodan verileri getirmeniz gerekiyor.

    YanıtlaSil

Bottom Ad [Post Page]