Skip to main content

ViewState ve SEO: ViewState Nesnesini Formun Sonuna Taşıyın!

ASP.NET WebForm’larının kimine göre en kullanışlı, kimine göre en başa bela nesnesidir ViewState. Bilhassa yoğun Postback işlemleri yaptığınız sayfalarda merak edipte oluşan HTML kodlarını incelemiyorsanız, sayfanızın yarısından çoğunun ViewState tarafından doldurulmasına şaşırmayın. Gerekli görülmeyen sayfalar ve kontroller için ViewState özelliğini kapatılması gerektiğini fırsat buldukça her ortamda yazılımcı arkadaşlarla paylaşıyoruz. Ama bazı durumlarda kontrol veya sayfa bazında ViewState nesnesinin kapanmasınında çare olmadığı durumlar söz konusu oluyor. Sözü nereye getireceğimi tahmin edenleriniz olmuştur: SEO

Arama motoru optimizasyonu konusunda ViewState nesnesine iki farklı yönden bakmamız gerekir; ViewState’in sayfa boyutuna etkisi ve ViewState’in sayfa içerisindeki konumu. Sayfa boyutunun yüksek olması sayfanın yüklenme hızını düşürecektir. Bu nedenle büyük boyuttaki sayfalar arama motorları tarafından çok sevilmez. En köklü değişikliği yapıp ViewState’i sayfa bazında kapatsak dahi ASP.NET sayfa çıktısında ViewState’i taşıyan hidden input elementini sayfaya ekleyecektir. Dolayısıyla dönüyor dolaşıyor ve yine SEO konusuna geliyoruz: ViewState nesnesinin sayfa içerisindeki konumunun arama motoru optimizasyonuna ne gibi etkileri olabilir?


Bildiğimiz gibi ViewState nesnesi HTML çıktının en üst kısmında yer almaktadır. Yani bir arama motoru sayfadaki içeriği süzdüğünde karşısına ilk olarak ViewState nesnesi çıkacaktır. Eminiz ki arama motorları ViewState nesnesini okumadan geçecek algoritmayı yıllar öncesinden geliştirmiştir. Ancak arama motorları sayfa içeriğini okurken ViewState nesnesini önemsemeden geçmesine rağmen hanemize eksi puanları yazacaktır(Arama motoruna ait örümcekler “Benim için önemli olmayan bir içeriğin sayfanın en tepesinde ne işi var?” diye soracaktır). Bu noktada akla şu soru gelebilir, madem ViewState’i kapattık, bu nesneyi sayfadan tamamen kaldırsak nasıl olur? Teorik olarak mümkün ama sayfanın işleyişi için çok kritik olan bir nesneyi sayfadan tamamen kaldırmanın doğru olmayacağını düşünüyorum. Diğer taraftan, madem ki arama motoru ViewState’in sayfanın en üst kısmında olmasına kızıyor, o zaman onun daha az kızdıracak, hatta hoşuna gidecek birşeyler yapabilir miyiz sorusu gelecektir akla. Mesela ViewState’i sayfanın en üst kısmından alıp, sayfanın en alt kısmına taşımak gibi. İşte size süper bir SEO taktiği!

Bu yazıyı aslında 2 ay kadar önce yazmış ama sonunu getirememiştim, nasip bugüneymiş. İtiraf ediyorum aslında 26 Temmuz’da yazdığım Bir ASP.NET Sayfasının HTML Çıktısını Değiştirmek konulu yazı bu konu için ön hazırlıktı :) Eğer o yazıyı okumadıysanız incelemenizi tavsiye ederim, dediğim gibi bu konu için ön hazırlık niteliğinde. Bir ASP.NET sayfasının HTML çıktısını programatik olarak değiştirmemiz mümkünse ve elimizde Regular Expression gibi ViewState nesnesini HTML kodu içerisinde kolayca yakalamamızı sağlayacak bir yapı varsa, işin geri kalanı sadece basit bir algoritma hazırlamaya kalıyor. Bu işlemi sayfa bazında yapmak isterseniz, sayfanın Render metodunu ezerek(override) aşağıdaki kodları bu metot için düzenleyebilirsiniz. Ben yazımda daha işe yarar ve uygulama genelinde kullanılabilecek bir yardımcı bir Stream ve HttpModule nesnesi hazırlayacağım. Bu nesneleri ayrı bir sınıf kütüphanesi olarak tasarlayarak farklı projelerde kullanabilir şekilde geliştireceğim.

ViewStateSeoHelper adında bir sınıf kütüphanesi projesi oluşturuyor ve aşağıdaki sınıf diagramında göreceğiniz iki nesneyi tasarlıyoruz.

İlk olarak HtmlFilterStream nesnesini oluşturuyoruz. Önceki yazımı okuyanlar bu sınıfın hemen hemen aynı olduğunu görecektir.

HtmlFilterStream.cs

...
using System.Text.RegularExpressions;
namespace ViewStateSeoHelper
{
    public class HtmlFilterStream : Stream
    {
        Stream _baseStream;
        long _position;
        string _html = "";
        public HtmlFilterStream(Stream stream)
        {
            _baseStream = stream;
        }
        public override bool CanRead { get { return true; } }
        public override bool CanSeek { get { return true; } }
        public override bool CanWrite { get { return true; } }
        public override long Length { get { return 0; } }
        public override long Position
        {
            get { return _position; }
            set { _position = value; }
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            _html += Encoding.Default.GetString(buffer, offset, count);
            string viewStateRegex = @"(<input type=""hidden""                                          name=""__VIEWSTATE"" id=""__VIEWSTATE""                                          value=""[a-zA-Z0-9\+=\\/]+"" />)";
             //Kodları copy-paste yaparak alırsanız üst kısımda üç satır olarak
             //yazılmış string ifadeyi tek satırda toplamanız gerekebilir.
            Regex objRegex = new Regex(viewStateRegex, RegexOptions.Multiline);
            MatchCollection objCol = objRegex.Matches(_html);
            if (objCol.Count > 0 && _html.Contains("</form>"))
            {
                string viewState = objCol[0].Value;
                _html = _html.Replace(viewState, "");
                int formClosingPosition = _html.IndexOf("</form>");
                if (formClosingPosition > 0)
                {
                    _html = _html.Insert(formClosingPosition, viewState);
                    buffer = Encoding.Default.GetBytes(_html);
                    _baseStream.Write(buffer, 0, buffer.Length);
                }
            }
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            return _baseStream.Read(buffer, offset, count);
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            return _baseStream.Seek(offset, origin);
        }
        public override void SetLength(long value)
        {
            _baseStream.SetLength(value);
        }
        public override void Flush()
        {
            string output = _html;
            _baseStream.Flush();
        }
    }
}

ViewState nesnesinin içeriğini alarak önce HTML çıktıdan siliyoruz, ardından ise sayfanın alt kısmına, yani </form> ifadesinin hemen öncesine ekliyoruz. Bu şekilde HTML çıktıda ViewState’in konumunu sayfanın üst kısmından alt kısmına taşımış olduk. Gelelim bu işi otomatik şekilde tüm sayfalarda çalışabilir hale getirmeye. Sınıfımıza IHttpModule arayüzünü(interface) uygulayarak bir HttpModule nesnesi oluşturuyoruz. Yani konfigürasyon dosyamızda gerekli ayarları yaptığımızda artık uygulamaya gelen her talepte bu nesne devreye girebilecek. Nesnemize ait kodlar aşağıdaki gibidir:

ViewStateSeoModule.cs

using System.Web;
namespace ViewStateSeoHelper
{
    class ViewStateSeoModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
        }
        void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication application = sender as HttpApplication;
            if (application.Context.Request.Url.AbsolutePath.Contains(".aspx"))
                application.Response.Filter = new HtmlFilterStream(application.Response.Filter);
        }
        public void Dispose()
        {
        }
    }
}

Talep başlangıcında devreye giren HttpModule nesnesi eğer talep yapılan dosya .aspx uzantılı bir dosya ise HTML çıktıyı yukarıda hazırladığımız HtmlStreamFilter nesnesine iletecektir. Böylece uygulamadaki tüm .aspx sayfalarında Stream nesnemiz elde ettiği HTML çıktı üzerinde değişiklik yaparak ViewState nesnesini sayfanın en alt kısmına taşıyacaktır.

Hazırladığımız ViewStateSeoHelper isimli sınıf kütüphanesini ASP.NET Web Site veya Web Application projemizin referanslarına ekledikten sonra, HttpModule nesnesini web.config dosyasına kaydederek uygulama genelindeki tüm dosyalar için çalışabilir hale getirmemiz gerekiyor.

 <system.web>
    <httpModules>
      <add name="ViewStateSeoModule" type="ViewStateSeoHelper.ViewStateSeoModule"/>     </httpModules>
    ...
  </system.web>

Artık uygulamadaki bir sayfayı çalıştırarak ViewState’in konumunu görebiliriz.

Umarım SEO konusunda çalışan arkadaşlar için faydalı bir yazı olmuştur.

ViewState ve SEO: ViewState Nesnesini Formun Sonuna Taşıyın!” hakkında 20 yorum

  1. Elinize sağlık. Sormak istedigim birşey var yorumlarsanız çok sevinirim.
    Bu veya bunun gibi sistemi yaptığımız zaman daha fazla işlem gücü gerekeceginden hitli sitelerde ne kadar sorun olur veya sorun yaratır mı?

  2. Testini yapmadım ama tahminimce sayfanın üretimi esnasında %10-20 ek yük getirir yukarıda bahsi geçen işlem. Bir sayfanın sunucu tarafındaki üretiminin 0.1 saniyeden bile daha kısa sürdüğünü düşündüğümüzde çok sorun çıkarmaması gerekir.

    Tabi ki çok büyük metin içeren sayfalarda yukarıdaki işlemin ne derece yük getireceğini ayrıca test etmek gerekir. Örneğin HTML çıktının 150-200 KB ise mutlaka ViewStateSeoHelper modülü olmadan ve ViewStateSeoHelper modülü ile sayfa üretim süreleri incelenmeli. Tek sayfayı tek kullanıcı ile test etmekte doğru olmayabilir bu tip durumlarda, düzgün bir yük testi(load test) yapılarak uygulama genelindeki sayfaların üretim hızları, response-request süreleri incelenmeli elbetteki.

  3. Öncelikle yazı için teşekkürler böyle katı ve teknik yazılar fikir oluşturması açısından çok iyi oluyor :)

    Performans sorunu ile ilgili olarak bende düşüncelerimi paylaşmak isterim elbette.

    Çok yüksek trafikli sitelerde sıkıntı oluşturmaması açısından Uğur hocamızında belirttiği gibi yük testi şart gibi görünüyor.

    Benim yüzeysel görünüm ağır yük altında darboğaz oluşturabileceği yönünde.Yinede unit test yazmakta ya da load test yapmakta fayda var.

    Saygılarımla..

  4. hocam makale için teşekkürler. Fakat eklediğimde sayfamın çıktısında hiçbirşey görünmüyor.

  5. Pardon regex satırından kaynaklanıyomuş. 3 satırda olduğu için tanımlamayı kabul etmmiyor.

  6. merhaba ben uygulamayı yaptım
    ancak webconfig eklediğim kod hatta veriyor
    yardımcı olursanır sevinirim..
    teşekürler

  7. @mehmet emin

    Aldığınız hata mesajını buraya yazarsanız veya mail ile gönderirseniz yardımcı olmaya çalışırım.

  8. bu çalışmayı yaptım uyguladım ve viewstate en sayfanın en alt tarafına yerleşti fakat şöyle bir sorun var sayfa’nın en başına bir div içerisinde /> bu işareti yerleştiriyor…

  9. Eğer ilgili html kodunu string nesnesinin Replace metoduyla değiştirme şansınız varsa en kolay çözüm bu olabilir. Ancak ben yaptığım testlerde böyle bir sorunla karşılaşmamıştım.

  10. hocam makale için teşekkürler ben bu kod nerde oluşturacağımı anlamadımm

    ViewStateSeoModule.cs
    using System.Web;
    namespace ViewStateSeoHelper
    {
    class ViewStateSeoModule : IHttpModule
    {
    public void Init(HttpApplication context)
    {
    context.BeginRequest += new EventHandler(context_BeginRequest);
    }
    void context_BeginRequest(object sender, EventArgs e)
    {
    HttpApplication application = sender as HttpApplication;
    if (application.Context.Request.Url.AbsolutePath.Contains(".aspx"))
    application.Response.Filter = new HtmlFilterStream(application.Response.Filter);
    }
    public void Dispose()
    {
    }
    }
    }

  11. Merhaba,

    Modül için teşekkürler. Localhost’ta sorun olmamasına karşın sunucuda test ettiğimde aşağıdaki hatayı alıyoum.

    Application information:
    Application domain: /LM/w3svc/803/*****
    Trust level: Full
    Application Virtual Path: /
    Application Path: D:\website\******\www\
    Machine name: *****

    Process information:
    Process ID: 5300
    Process name: w3wp.exe
    Account name: NT AUTHORITY\NETWORK SERVICE

    Exception information:
    Exception type: ConfigurationErrorsException
    Exception message: Could not load type ‘ViewStateSeoHelper.ViewStateSeoModule’. (D:\website\***\www\web.config line 92) (D:\website\***\www\web.config line 92)
    at System.Web.Configuration.HttpModuleAction.get_Entry()
    at System.Web.Configuration.HttpModulesSection.CreateModules()
    at System.Web.HttpApplication.InitModules()
    at System.Web.HttpApplication.InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
    at System.Web.HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context)
    at System.Web.HttpApplicationFactory.GetApplicationInstance(HttpContext context)
    at System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)

  12. @hasan App_Code klasörü altına açacağınız bir .cs dosyasına yazabilirsiniz.

    @Crypty Web Application yerine Web Site proje template’inde yazıyor olabilir misiniz projeyi? Aynı kodları bir Web Application projesinde deneyin, çalışmazsa haberleşelim. Web Site’da yapmak isterseniz de handler class’ındaki namespace’i kaldırıp, web.config’e de namespace adını yazmadan deneyin bakalım olacak mı?

  13. Uğur, bir şekilde sorunu gidermiştim ancak buraya yazmayı unuttum. İlgin ve zaman ayırdığın için teşekkür ederim.

  14. Öncelikle Elinize sağlık hocam..sitenin her sayfası boş olarak gözüküyor.. sebebi ne olabilir acaba

  15. Biraz geç yazıyorum ama kusura bakmayın. Hangi sitenin her sayfası nasıl boş geliyor? Biraz detay verirseniz yardımcı olmaya çalışırım.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir