Site İçeriğini GZip İle Sıkıştırarak Site Performansını Arttırmak

23. Şubat 2014

Günümüzde web uygulaması geliştirirken, web uygulamasının hızlı açılması dikkate edilmesi gereken en önemli kriterlerden biridir. Anasayfanın bir kaç saniye geç açılması binlerce potansiyel ziyaretçinin rakip uygulamalara gitmesine sebep olabilir. Hızlı gelen içeriği ziyaretçilerin sevdiği kadar arama motorları da sever. Bir web uygulamasının yavaş çalışmasına pek çok sebep var iken, her bir sebebe göre ayrı çözümler vardır. Bu yazımda bu sebeplerden biri olan yoğun içeriğe dikkat çekmeye çalışarak çözüm yöntemi olarak GZip üzerinde durmaya çalışacağım.

GZip ile Sıkıştırılmamış Bir Site

Büyük bir dosyayı bir e-postaya ekleyeceğimiz zaman alacağımız ilk aksiyonlardan biri dosyayı sıkıştırmak olacağı gibi, yoğun içerikli bir siteyi de ziyaretçilerimize sunacağımız zaman da aynı aksiyonu almalıyız. Sıkıştırılmış bir dosyayı e-postamıza eklemek bizim bandwidth ve zamanımızdan tasarruf sağladığı gibi aynı zamanda alıcının da dosyayı indirme zamanı düşeceğinden onun da zamanını ve bandwidth'ini tasrarruflu kullanmış oluruz. Aynı mantık web uygulamamız için de geçerli. İçeriğin sıkıştırılması ile hem sunucu kaynaklarını hem de ziyaretçilerimizin kaynaklarını tasarruflu kullanarak her iki tarafın da kazanmasını sağlıyoruz.

checkgzipcompression.com gibi bir site'de GZip ile ziplanmamiş bir siteyi incelersek, sitenin sıkıştırılmamış hali ile neler kaçırdığımızı rahatça görebiliriz. Aşağıdaki örneğimizde sitemiz %80 oranında sıkıştırılabilecek iken hiç bir aksiyon alınmadığını görüyoruz.

Aynı siteyi Google Chrome Geliştirici araçları ile inceleyelim. F12'ye basark geliştirici araçlarını açıp Network tabına gelip Response altındaki sunucu başlıklarını incelersek siteminizin içeriğinin boyutu ve sıkıştırılma kullanılıp kullanılmadığını görebiliriz. Şayet sunucu içeriği sıkıştırmış olsaydı, burada Content-Encoding diye bir başlık görecektik. Site ziyaretçisi olarak kullandığımız tarayıcılar ziyeret ettiğimiz siteye bir talep gönderdiklerinde site içeriğini nasıl kabul edebilecekleri konusunda bazı bilgileri header bilgileri ile gönderirler. Accept-encoding header'ı ile tarayıcılar hangi sıkıştırma formatlarını (gzip, deflate gibi) kabul edebildiklerini sunucuya gönderirler. Sunucu bu header bilgisine göre eğer sıkıştırma formatlarından biri aktif ise içeriği sıkıştırarak ziyaretçiye gönderir. 

 

GZip İle Sıkıştırılmış Bir Site

Ziyaretçinin tarayıcısının sıkıştırma formatlarını kabul etmesi durumunda web sunucusu içeriği, tanımı yapılmış bir formatta sıkıştırıp ziyaretçiye gönderir. Ziyaretçinin tarayıcısı sıkıştırılmış içeriği açarak render eder. GZip sıkıştırılmış bir siteyi checkgzipcompression.com da incelersek aşağıdaki gibi yüksek oranda bir sıkıştırma ortalaması ile karşılaşabiliriz. HTML, kendini tekrar eden bir çok etiketten oluşan bir markup dili olduğu için sıkıştırma algoritmaları HTML üzerinde çok başarılı sonuç vermektedir.

Şayet Google developer tools ile aynı siteyi incelersek sıkıştırılmış içeriğin ve sıkıştırma formatının tarayıcıya nasıl bildirildiği konusunda fikir sahibi olabiliriz : 

 

IIS üzerinde GZip İçerik Sıkıştırma Formatının Açılması

Şuana kadar sıkıştırma formatlarının nasıl işlediğini, tarayıcı ile web sunucusunun uygun format üzerinde nasıl anlaştığını anlatmaya çalıştım. Peki GZip sıkıştırma formatını web sunucumuz üzerinde aktif hale getirmek için ne yapmalıyız ?

IIS üzerinde sitemizi bulup seçtiğimiz takdirde sitemiz ile alakalı pek çok ayara sağ taraftaki panelden erişebiliyoruz. Bu konfigurasyon ayarları arasında Sıkıştırma / Compression'ı seçersek yukarıdaki gibi bir ekran ile karşılaşırız. Bu iki ayarı açmamız halinde sitemizde sıkıştırma formatlarını aktif hale getirmiş oluyoruz.

Peki Web sunucumuzun IIS'ine direk erişimimiz yok ise sıkıştırma formatlarını nasıl aktif hale getiririz ? Bu sorunun ve pek çok konfigurasyon sorusunun çözümü web.config dosyasında saklı. <system.webServer> etiketi altında tanımlayacağımız urlCompression etiketi sayesinde IIS'e direk erişmeden sıkıştırma formatlarını aktif hale getirebiliriz : 

<system.webServer>

    <urlCompression doStaticCompression="true" doDynamicCompression="true" />

</system.webServer>

Ufak bir ayar ile içeriğimizde %70-80 oranında sıkıştırma sağlayarak bandwidth tasarrufu sağlayan sıkıştırma ayarları ile web uygulamalarımızı daha hızlı hale getirebiliriz. Her talepte içerik yeniden sıkıştırıldığı için sunucunun CPU kaynağı kullanılıyor. Bu sebepten de paylaşımlı sitelerde default ayarda kapalı olan sıkıştırma formatları çok büyük boyutta dinamik içeriğe sahip sitelerde tavsiye edilmiyor. 50 KB - 100KB arası büyüklüğe sahip içerikler için en iyi sonucu verdiği belirtiliyor.

, ,

IBundleOrderer İle Bundle İçerisindeki Dosyaların Sıralanması

28. Ocak 2013

Geçtiğimiz haftalarda incelediğim Bundling ve Minification yazısında bir dizin içerisindeki kaynak dosyalarımızı joker karakterler ile bir bundle haline getirip sayfamıza ekleyebileceğimizden bahsetmiştik. Bu dizin içerisindeki tüm dosyalar alfabetik sırada bundle içerisinde işlenir ve referans olarak verilen yerlerde bu sıralamada istemciye sunulur. Çoğu zaman bu tercih edilen bir durum değildir. Bazı temel script dosyalarımızın sayfamızda öncelikli olarak referans verilmesini isteriz. Bu temel script dosyalarını kullanan diğer script dosyalarımız ise sonrasında sayfamıza eklenir. İşte bu kurduğumuz hiyerarşik yapıyı oluşturacağımız bundle'lar içerisinde de yapabilmemiz için ya dosya isimlerini alfabetik olarak yarattığımız hiyerarşiye uyacak şekilde hazırlamamız gerekir ya da IBundleOrderer kullanarak bundle içerisindeki dosyalarımızı sıralayacak bir mekanizma oluşturabiliriz. 

Bu yazımda IBundleOrderer interface'ini inceleyerek, basit bir örnek ile bundle içerisindeki script dosyalarımızın hiyerarşisini web.config dosyası içerisinde tanımlayacağımız bir parametre ile yönetmeyi deneyeceğiz.

 

IBundleOrderer interface'i ve Bundle içerisindeki dosyaların sıralanması

Uygulamamız içerisinde oluşturduğumuz Bundle'ların özellikleri arasında Orderer adında bir özellik ile Bundle'ımıza dosyalarını sıralayabileceğimiz bir IBundleOrderer interface'ini uygulayan bir sınıf verebiliyoruz.

Bundle myBundle = new Bundle("~/bundles/SiteScripts", new JsMinify());
myBundle.IncludeDirectory("~/Scripts/SiteScripts", "*.js");
myBundle.Orderer = new MyBundleOrderer();
bundles.Add(myBundle);

Eğer IBundleOrderer interface'ine gözatarsak eğer OrderFiles metodu ile bundle içerisindeki dosyaları sıralayacak bir metod içeren bir interface olduğunu görüyoruz. Bundle oluşturulurken içerisindeki dosyaları parametre olarak gönderip, dönüş parametresi olarak da sıralanmış dosyaların listesini içeren bir kolleksiyon bekleniyor  : 

namespace System.Web.Optimization
{
    public interface IBundleOrderer
    {
        IEnumerable<System.IO.FileInfo> OrderFiles(BundleContext context, 
                                                IEnumerable<System.IO.FileInfo> files);
    }
}

Şimdi yukarıdaki örneğimizde kullandığımız ve IBundleOrderer interface'ini uygulayan sınıfımıza gelecek olursak : 

public class MyBundleOrderer : IBundleOrderer
    {
        public IEnumerable<System.IO.FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
        {
            if (ConfigurationManager.AppSettings["HighPriorityScripts"] != null)
            {
                string[] highPriorityScripts = ConfigurationManager.AppSettings["HighPriorityScripts"].Split(',');
                List<FileInfo> listFiles = new List<FileInfo>(files);
                List<FileInfo> orderedFiles = new List<FileInfo>();

                foreach (string highPriorityFile in highPriorityScripts)
                {
                    FileInfo nextFileInfo = listFiles.Find(delegate(FileInfo arg) 
                                    {
                                        return arg.Name == highPriorityFile;
                                    }
                                  );
                    if (nextFileInfo != null)
                    {
                        orderedFiles.Add(nextFileInfo);
                    }
                }

                foreach (FileInfo lowPriorityFile in listFiles)
                {
                    if (!orderedFiles.Contains(lowPriorityFile))
                    {
                        orderedFiles.Add(lowPriorityFile);
                    }
                }

                return orderedFiles;
            }

            return files;
        }
    }
 
<!-- Virgülle ayrılmış script dosyaları -->
<add key="HighPriorityScripts" value="Z_HighPriorityScript.js,SmallJSFile.js" />

 

MyBundleOrderer sınıfı web.config'de yukarıdaki gibi tanımlayacağımız HighPriorityScripts parametresini alıp, öncelik verilecek script dosyalarını belirleyip dönüş yapılacak listeye öncelikli olarak bu dosyaları ekliyor. Sonrasında geriye kalan dosyalar dönüş listesine ekleniyor ve oluşturulan sıralı liste dönülüyor. Böylece bundle'ımız içerisindeki dosyalarımızı web.config dosyasındaki parametremiz ile önceliklendirerek yönetebilir hale gelmiş oluyoruz.

, , ,

ASP.NET MVC 4 Bundling ve Minification

25. Aralık 2012

ASP.NET 4.5 ile yeni gelen özelliklerden biri olan Bundling and Minification temelde sunucuya gelen bir talebe (request), talep sayısını ve boyutunu düşürerek performans kazandırmayı amaçlayan bir özellik. Bu yazımda ASP.NET 4.5 ile gelen bu yeni özelliği ASP.NET MVC 4 ile incelemeye çalışacağım.

Bundling ve Minification'dan Önce

Bundling ve Minification öncesinde bir çok projemizde stil ve script dosyalarımızı sayfalarımıza aşağıdaki gibi tek tek tanımlardık : 

<script src="~/Scripts/SiteScripts/LargeJSFile.js" type="text/javascript"></script>
<script src="~/Scripts/SiteScripts/SmallJSFile.js" type="text/javascript"></script> 
<script src="~/Scripts/SiteScripts/Z_HighPriorityScript.js" type="text/javascript"></script> 

<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />

Yukarıdaki script ve stil dosyalarını referans eden bir sayfaya istemciden gelen bir talep dört yeni talep daha yaparak her bir dosyayı da sunucudan talep eder (tabii eğer dosyalar cachelenmedi ise). Sayfamızda bulunan resim, video veya animasyon dosyaları gibi harici dosyaların da ayrı birer talep olarak sunucuya geldiği de düşünülürse bir web sayfası içerisinde kaç dosyaya referans veriyor ise sunucuya her biri için birer talep gidiyor demektir. Bu da web sayfalarımızın yüklenme zamanı performansını etkileyen kriterlerden biridir. 

 

Yukarıdaki kod örneğindeki sayfamızı Google Chrome Developer Tools ile izler isek yukarıdaki gibi bir manzara ile karşılaşırız. Sayfamız ilk satırda yükleniyor, sonrasında stil ve script dosyaları (turuncu ve yeşil renkli) sunucudan talep ediliyor.

Bundlelar

 ASP.NET MVC 4 template projesini açtığınızda bu projede bundle'ların kullanıldığını farkedeceksiniz. Aşağıdaki gibi App_Start dizini altındaki BundleConfig sınıfına gözatarsak projede kullanılan script ve stil bundlelarının bu sınıf içerisinde tanımlandığını görürüz. Global.asax.cs sınıfına da gözatarsak eğer Application_Start olayında App_Start dizinindeki tüm config sınıflarının metodları çağrılarak web uygulamamızın ayarlarının yapıldığını görebiliriz. Eğer ASP.NET MVC 3 veya daha önceki versiyondan upgrade ettiğiniz bir web MVC uygulaması üzerinde çalışıyorsanız NuGet ile Microsoft.AspNet.Web.Optimization paketini almayı unutmayın.

StyleBundle ve ScriptBundle sınıflarını kullanıp yaratacağımız yeni bundleları BundleCollection'a ekleyerek uygulamamız içerisinde kullanacağımız yeni bundleları tanımlayabiliriz.

bundles.Add(new ScriptBundle("~/bundles/SiteScripts")
               .Include("~/Scripts/SiteScripts/*.js"));

Yukarıdaki örnekte joker karakterler (* wildcard karakteri) ile Scripts/SiteScripts dizini altındaki tüm javascript dosyalarını yeni tanımladığım bundle'a dahil ediyorum. Burada ufak bir kısıtımız var, joker karakterleri, dizindeki tüm dosyaları kapsayacak şekilde tanımlamama izin verilmiyor.  Yani "Scripts/SiteScripts/*" şeklinde bir tanım çalışma zamanında aşağıdaki hata ile engelleniyor : 

Yukarıdaki yarattığımız bundle'ı aşağıdaki gibi View'larımızda kullanabiliriz : 

@Scripts.Render("~/bundles/SiteScripts")
@Styles.Render("~/Content/css")

Bu durumda sunucudan talep ettiğimiz sayfamızın kaynak koduna gözatarsak eğer aşağıdaki gibi bundle'ımızı tanımladığımız dizindeki script dosyalarının alfabetik olarak sıralı biçimde sayfamıza eklendiğini görebiliriz : 

Hiyerarşik yapıda bir script veya stil dizinimiz var ve bunu bundle haline getirmek istiyorsak IncludeDirectory metodu daha çok işimize yarayacaktır. Metod bize aşağıdaki gibi alt dizinleri de yönetebileceğimiz bir esneklik sağlıyor : 

bool includeSubDirectories = true;
bundles.Add(new ScriptBundle("~/bundles/SiteScripts").IncludeDirectory(
                    "~/Scripts/SiteScripts", "*.js", includeSubDirectories));

Minification

Şimdiye kadar yaptığımız bundle örneklernini tek dosya halinde toplayıp içerisindeki gereksiz satırları (yorum satırları, boşluklar, yeni satırlar, vb. ) atıp, değişken isimlerini kısaltıp yani script veya stil dosyamızı optimize edip istemciye gönderilecek dosya boyutunu ve talep sayısını azaltan sihir aşağıdaki gibi EnableOptimization propertisi ile sağlanıyor : 

// BundleConfig.cs içerisine eklenecek.
BundleTable.EnableOptimizations = true; 

Minification'ı aktif hale getiren yukarıdaki satırı eklediğimiz zaman aşağıdaki gibi bir sonuç ile karşılaşıyoruz : 

Görüldüğü üzere üç script dosyası tek dosya halinde ve optimize edilmiş olarak 'SiteScripts?v=xyz-xyz-xyz' gibi bir dosya şeklinde istemciye gönderiliyor. Burada dikkat etmemiz gerene bir diğer nokta dosya isminin sonundaki v parametresi. Versiyon amaçlı kullanılan v parametresi sayesinde optimize ettiğimiz script dosyalarımız 1 sene boyunca istemcide cachelenmiş oluyor. Bu zaman zarfında eğer script dosyalarımız değişir ise yeni bir versiyon numarası otomatik olarak verilerek yeni bir dosya oluşturulup, istemcinin bu yeni dosyayı kullanması sağlanıyor.

Toplam boyutu 160KB olan ve 3 script dosyasından oluşan örneğimin 89KB'a ve tek dosyaya indirmemi sağlayan Bundling and Minification özelliği performans açısından getirdiği artıların yanı sıra uygulamamızda kullandığımız script ve stil dosyalarını derleyip toparlayıp ve daha okunabilir bir kod sağlıyor.

Güncelleme

16 Şubat 2014 

Eğer ASP.NET MVC 3 veya daha önceki versiyondan upgrade ettiğiniz bir web ASP.NET MVC uygulaması üzerinde çalışıyorsanız NuGet ile Microsoft.AspNet.Web.Optimization paketini almayı unutmayın. Bu sayede System.Web.Optimization isim alanına ve yazımda bahsettiğim bundling sınıflarına erişebilirsiniz.

, ,

HTML5Shiv ve Modernizr Kullanarak HTML5 Desteklemeyen Tarayıcılarla Çalışmak

22. Nisan 2012

Yeni bir teknoloji çıktığında her uygulama geliştiricinin aklına gelen/gelmesi gereken sorulardan biri, mevcut altyapılar (işletim sistemleri, tarayıcılar, veya teknoloji ile alakalı uygulamalar, vs.) bu teknolojiyi kullanabiliyor mu ?

HTML5 için de aynı soru herhalde hemen her geliştiricinin aklına gelmiştir. HTML5 eski tarayıcılarda çalışıyor mu ? Tarayıcılar HTML5 taglarını nasıl gösteriyorlar ? Eski tarayıcıları desteklemek için ne yapmak lazım ? 

İşte bu sorulara bir miktar çözüm sağlayabileceğimiz iki JavaScript kütüphanesini aktarmaya çalışacağım. 

HTML5Shiv

HTML5Shiv (veya HTML5Shim) ana sayfasından da göreceğiniz üzere HTML5'i desteklemeyen Internet Explorer 9 öncesi versiyonlar için HTML5 taglarının desteklenmesini sağlayan küçük boyutlu bir kütüphane. Önceki cümlemde yazdığım üzere, sadece Internet Explorer için çözüm sağlıyor, böylece sayfalarımızda IE tarafından tanınmayan tagları kullandığımız takdirde görsel bozulmalarla karşılaşmıyoruz. Yine bir önceki cümlemde yazdığım üzere kütüphane sadece yeni tagların IE tarafından tanınmasını sağlıyor, işlevsel olarak çalışmalarını sağlamıyor. Örneğin <audio> veya <video> taglarının tanınmasını sağlıyor fakat bu tagların video oynatacağı anlamına gelmiyor. Bir örnek ile konuyu daha açık hale getirmeye çalışalım. Örneğin aşağıdaki HTML sayfasına gözatalım : 

HTML5Shiv İçermeyen Sayfa

<!DOCTYPE HTML>
<html lang="tr">

<head>
  <title>HTML5 Shiv olmayan sayfa</title>
  <link rel="stylesheet" type="text/css" href="assets/style.css" />
</head>

<body>
  <div id="main">
    <header id="header">
      <div id="logo">
        <div id="logo_text">
          <h1><a href="#">WebUcu<span class="logo_colour">.org</span></a></h1>
          <h2>HTML5 Shiv Olmayan Sayfa</h2>
        </div>
      </div>
      <nav id="menubar">
        <ul id="menu">
          <li class="selected"><a href="#">ANASAYFA</a></li>
          <li><a href="#">MENÜ 1</a></li>
          <li><a href="#">MENÜ 2</a></li>
          <li><a href="#">MENÜ 3</a></li>
          <li><a href="#">MENÜ 4</a></li>
        </ul>
      </nav>
    </header>
    <article id="site_content">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pharetra dictum risus, vel rhoncus metus aliquet non. Morbi ultricies aliquet pharetra. Nunc auctor pellentesque nulla nec facilisis. In fermentum nunc et neque dignissim faucibus. Pellentesque varius tempor elit, in vulputate arcu ornare ut. Ut libero neque, suscipit non sagittis vel, vestibulum et mi. Maecenas at erat nulla. Mauris mollis pretium elit, ac lacinia sem ornare ac. In libero nulla, tempor et aliquet ut, scelerisque sed enim.
		<br><br>
		Proin sed nisl risus, quis sodales est. Phasellus dapibus tincidunt mauris, in viverra lacus auctor id. Integer aliquam malesuada ante et sodales. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aliquam mollis porttitor porttitor. Ut gravida congue posuere. Phasellus ornare facilisis diam, non fermentum est semper in. Vivamus gravida convallis ligula non tempus. Proin vel tellus urna. Suspendisse congue iaculis varius. Vestibulum a nulla leo, a ullamcorper nulla. Nunc turpis tellus, scelerisque ac adipiscing eget, pretium sed nunc. Vestibulum id tempus ligula. Quisque varius, lacus sagittis tempus eleifend, nibh elit pretium nibh, tempor hendrerit erat leo sit amet dolor. Curabitur sagittis egestas massa, vitae fermentum neque pretium in.
    </article>
    <footer id="footer">
      Copyright © WebUcu.Org
    </footer>
  </div>
</body>
</html>

 

<header>, <article>, <nav> ve <footer> gibi HTML5 tagları içerdiğini görüyoruz. Bu sayfayı HTML5 destekleyen bir tarayıcı (Chrome 18) ile açtığımızda aşağıdaki gibi bir sonuç elde ediyoruz : 

 

Aynı sayfayı HTML5 desteklemeyen bir tarayıcı (Internet Explorer 6.0) ile açtığımızda aşağıdaki gibi bir sonuç elde ediyoruz : 

 

 

Desteklenmeyen HTML5 taglarından dolayı tasarımın nasıl bozulduğunu görebiliyoruz. Bunun önüne geçebilmek ve sayfamızı çalınmış güzelliğine kavuşturabilmek için yapmamız gereken çok basit, aşağıdaki gibi HTML5Shiv referansını ekliyoruz ve sayfamız IE 6.0 tarafından aşağıdaki gibi taranıyor : 

HTML5Shiv İçeren Sayfa

<head>
  <title>HTML5 Shiv olan sayfa</title>
  <link rel="stylesheet" type="text/css" href="assets/style.css" />
  <!--[if lt IE 9]>
  <script src="assets/html5.js"></script>
  <![endif]-->
</head>

 

Modernizr

ASP.NET MVC ile uğraşmış olanlar bilir, Visual Studio.NET ile yeni bir ASP.NET MVC projesi açtığınızda Scripts dizini ile içerisinde JQuery ve Modernizr Javascript kütüphanelerini görürüz. Bunu ilk farkettiğimde, HTML5 desteği en düşük tarayıcı olan IE'nin ve Microsoft'un ayıp kapatmaya çalışması gibi yorumlamıştım.

Modernizr, tarayıcıların HTML5 ve CSS3 özelliklerinden hangilerini desteklediğini test edebileceğimiz ve içeriğimizi bu test sonuçlarına göre düzenleyebileceğimiz bir yardımcı kütüphane. Modernizr da aynı HTML5Shiv gibi HTML5 taglarını çalışır hale getirmiyor, sadece bu tagların desteklenip desteklenmediği hakkında bilgi sahibi olmamızı sağlıyor. 

Eğer download sayfasına giderseniz, kullanacağınız özellikleri seçerek daha hafif bir kütüphane indirebilmenize olanak sağlayan Modernizr, farkedeceğiniz üzere, HTML5Shiv içeriyor. Yani sayfalarınızda Modernizr kullanıyorsanız, HTML5Shiv referansı vermenize gerek kalmıyor.

Modernizr'ı CSS ve JavaScript ile olmak üzere iki şekilde kullanabiliyoruz.

CSS ile Modernizr kullanımı

Modernizr kullanırken yapmamız gereken öncelikle modernizr kütüphanesine referans vermek : 

<head>
	<title>CSS ile Modernizr Kullanım Örneği</title>
	<script src="modernizr-2.5.3.js"></script>
</head>

Modernizr referansını verdikten sonra html tagına no-js css sınıfını vererek sayfamızda modernizr testlerinin çalışacağını belirtiyoruz : 

<!DOCTYPE html>
<html lang="tr" class="no-js">

Şuanda Modernizr sayfamızda çalışıyor halde olmalı. Bunu test etmek için tarayıcımızda sayfamızı açar ve Firebug benzeri bir geliştirici aracı ile (Birçok geliştirici aracının kısa yolu olan F12'ya basın) sayfamızın kaynak koduna bakarsak aşağıdaki gibi bir sonuç görürüz : 

 

Modernizr, html tagına verdiğimiz class değerini, yaptığı test sonuçlarına göre özellik adını içeren sınıflar ile eşitliyor. Örneğin bizim örneklerimizde kullanacağımız HTML5'in SVG özelliğini svg sınıfı olarak görebiliyoruz. Eğer tarayıcımda HTML5 SVG desteklenmiyor olsaydı no-svg diye bir sınıf görüyor olmalıydım. Yani desteklenmeyen özellikler no-özellik_adı şeklinde sınıf olarak ekleniyorlar. 

Şimdi test sayfamıza geri dönersek, sayfamızda SVG kullanmak istediğimizi varsayalım, ama SVG desteklemeyen tarayıcılar için de bir çözüm sunmak istediğimizi varsayalım. Bu durumda sayfamıza aşağıdaki gibi 2 adet div elemanı ekleyip birini SVG destekleyen tarayıcılar için hazırlıyoruz, diğerini ise SVG desteklemeyen tarayıcılar için hazırlıyoruz : 

<div class="divSvg">
	<svg id="mySvg" height="300" width="200">
		<polyline points="20,30 20,230 50,60 50,80 100,80 100,100" fill="green" x="20" y="30" />
		<text x="15" y="15" style="font-size:20;stroke:blue">HTML5 SVG Örneği</text>
	</svg>
</div>
<div class="divNoSvg">
	Tarayıcınız SVG desteklemiyor, yerine resim ekliyoruz : <br>
	<img src="svg_clone.jpg" />
</div>

Son olarak yukarıda tanımladığımız divlere verdiğin css sınıflarına gözatacak olursak, her iki sınıfında div elemanlarımızı sakladıklarını görüyoruz. Divlerimiz Svg sınıfı içeren bir tag içerisinde kullanılıyorlar ise divSvg, no-svg sınıfına sahip bir tag içerisinde ise divNoSvg sınıfı görünür halde olacak şekilde bir düzenleme yapıldığını görüyoruz. Yani yukarıda html tagının sınıfları içerisinde svg sınıfı var ise divSvg sınıflı div'imiz, no-svg sınıfı var ise divNoSvg sınıflı div'imiz kullanıcıya görünür hale gelecek : 

<style type="text/css" media="screen">
	div.divSvg { display:none; }
	div.divNoSvg { display:none; }
	
	.no-svg div.divNoSvg {
		display:block;
	}
	
	.svg div.divSvg {
		display:block;
	}
</style>

CSS kullanımını gösteren tüm kodumuza topluca göz atacak olursak :

CSS ile Modernizr Kullanım Örneği

<!DOCTYPE html>
<html lang="tr" class="no-js">
	<head>
		<meta charset="windows-1254">
		<title>CSS ile Modernizr Kullanım Örneği</title>
		<script src="modernizr-2.5.3.js"></script>
		<style type="text/css" media="screen">
			div.divSvg { display:none; }
			div.divNoSvg { display:none; }
			
			.no-svg div.divNoSvg {
				display:block;
			}
			
			.svg div.divSvg {
				display:block;
			}
		</style>
	</head>
	<body>
		<h3>CSS ile Modernizr Kullanım Örneği</h3>
		<div class="divSvg">
			<svg id="mySvg" height="300" width="200">
				<polyline points="20,30 20,230 50,60 50,80 100,80 100,100" fill="green" x="20" y="30" />
				<text x="15" y="15" style="font-size:20;stroke:blue">HTML5 SVG Örneği</text>
			</svg>
		</div>
		<div class="divNoSvg">
			Tarayıcınız SVG desteklemiyor, yerine resim ekliyoruz : <br>
			<img src="svg_clone.jpg" />
		</div>
	</body>
</html>

Örneğimizi SVG destekleyen bir tarayıcıda (Chrome 18) çalıştırır isek : 

 

Örneğimizi SVG desteklemeyen bir tarayıcı (Internet Explorer 6.0) ile çalıştırır isek : 

 

JavaScript ile Modernizr kullanımı

Son örnek olarak CSS örneğinde kullandığımız sayfayı JavaScript ile kullanmak istersek divlerimizi aşağıdaki gibi görünmez olarak tanımlıyoruz : 

<div id="divSvg" style="display:none;">
	<svg id="mySvg" height="300" width="200">
		<polyline points="20,30 20,230 50,60 50,80 100,80 100,100" fill="green" x="20" y="30" />
		<text x="15" y="15" style="font-size:20;stroke:blue">HTML5 SVG Örneği</text>
	</svg>
</div>
<div id="divNoSvg" style="display:none;">
	Tarayıcınız SVG desteklemiyor, yerine resim ekliyoruz : <br>
	<img src="svg_clone.jpg" />
</div>

Sonrasında aşağıdaki gibi bir JavaScript kodu ile svg'nin aktif olup olmamasına göre ilgili div'imizi görünür hale getiriyoruz :

window.onload = function() {
	if (Modernizr.svg) {
		document.getElementById("divSvg").style.display = 'block';
	} else {
		document.getElementById("divNoSvg").style.display = 'block';
	}
};

Tüm örneğe göz atarsak : 

JavaScript ile Modernizr Kullanım Örneği

<!DOCTYPE html>
<html lang="tr" class="no-js">
	<head>
		<meta charset="windows-1254">
		<title>JavaScript ile Modernizr Kullanım Örneği</title>
		<script src="modernizr-2.5.3.js"></script>
		
		<script type="text/javascript">
		  window.onload = function() {
			if (Modernizr.svg) {
				document.getElementById("divSvg").style.display = 'block';
			} else {
				document.getElementById("divNoSvg").style.display = 'block';
			}
		  };
		</script>
	</head>
	<body>
		<h3>JavaScript ile Modernizr Kullanım Örneği</h3>
		<div id="divSvg" style="display:none;">
			<svg id="mySvg" height="300" width="200">
				<polyline points="20,30 20,230 50,60 50,80 100,80 100,100" fill="green" x="20" y="30" />
				<text x="15" y="15" style="font-size:20;stroke:blue">HTML5 SVG Örneği</text>
			</svg>
		</div>
		<div id="divNoSvg" style="display:none;">
			Tarayıcınız SVG desteklemiyor, yerine resim ekliyoruz : <br>
			<img src="svg_clone.jpg" />
		</div>
	</body>
</html>

 Sonuç

HTML5 kullanırken işin sıkıcı yanı, HTML5'i desteklemeyen tarayıcılarla nasıl çalışmak gerektiğini incelemeye çalıştık. HTML5Shiv, yeni HTML5 taglarını kullanabilmemizi sağlayan ve kullanımı oldukça basit bir kütüphane. Modernizr ise manuel olarak yapabileceğimiz birçok özellik testini bize hazır olarak sunan başarılı kütüphane. İki kütüphaneyi kullanarak odaklanmamız gereken tek yer, HTML5 desteklenmeyen tarayıcılar'da yeni özelliklere alternatif olarak ne kullanabiliriz sorusuna yanıt aramak.

, ,

Response.RedirectPermanent ve Response.Redirect

17. Nisan 2012

Klasik ASP'den kalan alışkanlıklarımızdan Response.Redirect("url") metodunu ziyaretçimizi bir sayfadan başka bir sayfaya yönlendirmek için yıllarca kullandık. ASP.NET geldi, Redirect metodumuz baki kaldı, kullanmaya devam ettik. ASP.NET 4.0 ile gelen Response.RedirectPermanent("url") metodu, tam olarak Response.Redirect metodunun pabucunu dama atmasa da redirect metodlarını daha dikkatli kullanmaya yönlendiriyor.

Peki nedir bu iki metod arasındaki fark ? 

İki metod temelde aynı işlemi yapıyorlar. Yani parametre olarak verilen URL'e ziyaretçiyi yönlendiriyorlar. Aralarındaki tek fark iki metodun ziyaretçiye döndüğü HTTP durum kodu.

Aşağıdaki iki örneği, Firebug benzeri bir geliştirici aracı ile izleyerek çalıştırırsak resimlerdeki gibi iki metod arasındaki farkı izleyebiliriz

 

protected void linkClassicalRedirect_Click(object sender, EventArgs e)
{
       Response.Redirect("RedirectedPage.aspx");
}

 

protected void linkPermanentlyRedirect_Click(object sender, EventArgs e)
{
	Response.RedirectPermanent("RedirectedPage.aspx");
}

 

HTTP 302 ve HTTP 301

HTTP 302 durum kodu ziyaretçiye, istekte bulunan içeriğin, geçici olarak, transfer edilen sayfaya  (örnekteki RedirectedPage.aspx) taşındığını belirtiyor. 

HTTP 301 durum kodu ise ziyaretçiye, istekte bulunan içeriğin, kalıcı olarak, transfer edilen sayfaya taşındığını ifade ediyor.

Bu örneğin ve kodların bir tarayıcı vasıtası ile web uygulamamızı ziyaret eden kullanıcı için birşey ifade etmediğini söyleyebiliriz. Fakat asıl fark web uygulamamızı indexleyen arama motorlarının web uygulamamızı değerlendirmeleri sırasında ortaya çıkıyor. Sitemizi dolaşıp indexleme yapan bir arama motoru, bir içeriğin kalıcı olarak taşındığını HTTP 301 kodu ile aldığında indexlemesini yeni URL üzerinden yapacak, ve böylece arama motoru üzerinden sitemize erişen kullanıcılar için içeriğe erişim hızlanacaktır (eski link üzerinden redirect edilerek erişmek yerine yeni link ile doğrudan erişim sağlayacaklardır).

, ,

ASP.NET MVC 3 ve Elmah İle Hata Loglama

24. Mart 2012

Elmah (Error Logging Modules and Handler) kütüphanesi ASP.NET 2.0'dan beri kullandığım, ASP.NET web uygulamaları için hata loglama ve yönetimi kütüphanesi. Geliştirdiğim web uygulamalarında sadece birkaç ayar ile hata yönetimini çalışır hale getirebiliyor olmam, beni Elmah'a hayran bırakan özelliklerinden sadece biri. ASP.NET HTTP Handlers kullanımına verilebilecek en yaratıcı örneklerden biri, hatta muhtemelen en iyisi. 

ASP.NET MVC uygulamamızda Elmah'ı çalışır hale getirmek için aşağıdaki adımları takip edebiliriz.

 

ASP.NET MVC 3 Web Uygulamasına Elmah Kurulumu

1. İlk yapmamız gereken solution'ımıza sağ tıklayıp "Manage Nuget Packages"a tıklayıp NuGet paketleri içerisinde Elmah diye aratıp Elmah MS SQL Server'ı seçmek. Başka veritabanları üzerinde loglama yapacak, veya logları dosya sisteminde XML olarak kaydedecek Elmah paketleri de göreceksiniz listede. Ben örneğimi SQL Server üzerinde geliştireceğim.

 

 

2. Elmah NuGet paketini uygulamanıza yükledikten sonra solution'ınızda references'te Elmah referansını göçrebilirsiniz. Aynı zamanda uygulamanıza App_Readme diye bir dizin eklenmiş, ve içerisinde Elmah.SqlServer.sql isminde bir dosya olmalı. İşte bu dosya Elmah'ın hata loglarken kullanacağı SQL Server tablosunun ve Stored Procedure'lerinin scriptlerini barındırıyor. Şimdi yapmamız gereken bu dosyayı uygulamamızın bağlanacağı SQL Server veritabanında çalıştırmak. 

 

 

 

3. Web uygulamamızın konfigurasyon dosyasını yani web.config'i açar isek, NuGet paketi yüklenirken konfigurasyon dosyamıza elmah diye bir bölüm tanımladığını ve içerisine Elmah'ın varsayılan ayarlarını yaptığını görebiliriz. İlk aşamada yapmamız gereken Elmah'ın veritabanına bağlanırken kullanacağı bağlantı cümleciğini (connection string) uygulamamızın kullandığı bağlantı cümleciğine eşitlemek. 

Elmah'ın kullandığı bağlantı cümleciği aşağıdaki bölümde yer almakta. 

 

<elmah>
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="elmah-sqlserver" />
</elmah>

 

connectionStringName değerini, webconfig dosyamızın connectionStrings bölümüne ekleyeceğimiz ve uygulamamızın kullanacağı bağlantı cümleciği ile değiştirebiliriz : 

<connectionStrings>
    <add name="connStr" connectionString="Data Source=INSTANCE;Initial Catalog=DB;Persist Security Info=True;User ID=USER_NAME;Password=SIFRE"
         providerName="System.Data.SqlClient" />
</connectionStrings>

<!-- ... -->

<elmah>
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="connStr" />
</elmah>

 

Eğer bağlantı cümleciğini doğru tanımladıysak Elmah şuan çalışır hale gelmiş olmalı. Test edebilmek için, web uygulamamızı çalıştıralım ve uygulamamızın ana dizininde elmah.axd'yi çağıralım. Yani henüz localhostta çalıştığımız için benim çağırdığım link şu şekilde : 

http://localhost:46352/elmah.axd

 

Loglanacak Hataların Filtrelenmesi

Farkedeceğiniz üzere henüz yeni bir ASP.NET MVC uygulaması açmış olmama rağmen tarayıcım favicon.ico dosyasını sunucudan talep etmiş fakat 404 yani dosya bulunamadı hatası almış, ve bu hatalar Elmah tarafından loglanmış. İyi haber, uygulamama eklediğim Elmah paketim başarıyla çalışıyor. Kötü haber tarayıcım varsayılan olarak web uygulamalarının favicon.ico dosyasını sunucudan talep ettiği için bu hata sunucuya gelen her talepte loglanıyor olacak.

Bunun önüne geçebilmek için web.config konfigurasyon dosyam içerisindeki elmah bölümünde aşağıdaki gibi filtre tanımı yapabilirim. errorFilter tag'ı ile loglamasını istemediğimiz logları filtreleyebiliriz :

<elmah>
<errorFilter>
  <test>
	<and>
	  <equal binding="HttpStatusCode" value="404" type="Int32" />
	  <regex binding="Context.Request.ServerVariables['URL']" pattern="/favicon\.ico(\z|\?)" />
	</and>
  </test>
</errorFilter>
</elmah>

 

Elmah Güvenlik Ayarları 

Web uygulamamızı geliştirip sunucuya taşıma vakti geldiğinde, elmah.axd sayfasına yetkisiz kişilerin girmesini ve hatalarımızı görmesini istemeyiz. Hata mesajları sakıncalı ve güvenlik açığı oluşturabilecek bilgiler içerebilirler. Bu yüzden, web uygulamamızı web sunucusuna taşırsak ve tarayıcımızdan elmah.axd kaynağını talep edersek HTTP 403 Erişim Engellendi hatası ile karşılaşırız. Elmah önyüzü varsayılan ayarlarda yalnızda localde çalışır. Bunu açıp, elmah.axd'yi uygulamamızın yönetim alanına almak için öncelikle yapmamız gereken, web.config içerisindeki elmah bölümü altına security tagını eklemek ve allowRemoteAccess değerini true olarak vermek : 

<elmah>
    <security allowRemoteAccess="true" />
    <errorFilter>
      <test>
        <and>
          <equal binding="HttpStatusCode" value="404" type="Int32" />
          <regex binding="Context.Request.ServerVariables['URL']" pattern="/favicon\.ico(\z|\?)" />
        </and>
      </test>
    </errorFilter>
</elmah>

Şu durumda elmah.axd sunucudan talep eden herkese açık hale geldi. Şimdi location tagı ile elmah.axd'yi sadece yetkili kullanıcılarca erişilebilir hale getirelim. Tabi bunu kullanabilmek için uygulamamızın kullanıcı yönetiminin geliştirilmiş olması gerekmektedir :

 

<location path="elmah.axd">
    <system.web>
      <authorization>
        <deny users="?" />
        <allow roles="admin" />
      </authorization>
    </system.web>
</location>

Sonuç

Bu günlük yazımı yazmaya başladığımda ilk aklıma gelen Java dünyasında geçirdiğim 9 ay oldu. Başarılı pek çok kütüphaneyi çalışır hale getirebilmek için konfigürasyon dosyaları arasında kaybolmak...

2dk sürmeyen ve sadece 3 adımda tamamladığımız Elmah kurulumu ile web uygulamamızın hata loglama işlemlerini yapabiliyor hale getirdik. Filtreleme ve güvenlik ayarlarına göz attık. Basit ve hızlı bir çözüm ile uygulamamızda hata loglamasını, kendini kanıtlamış bir kütüphaneye emanet etmiş oluyoruz.

, ,

HTML5 Eğitimi

5. Şubat 2012

HTML5 Logo3-4 Mart 2012 tarihinde Zenith Bilişim'de eğitmeni benim olacağım 12 saatlik bir HTML5 eğitimi planlıyoruz.

Bu eğitimde HTML5 ile gelen yeni doküman yapısından yeni taglara ve desteklenmeyen taglara,

Flash player'leri tarihe karıştıracak audio ve video taglarından HTML5 canvas'a,

local ve session storage ile veri saklama APIlerinden cache altyapısına,

Geolocation API ile lokasyon tabanlı uygulamalardan Messaging API ve Web Sockets ile veri akışına kadar HTML5'in heyecan verici dünyasına dalıp, örnekler ile HTML5'i öğrenmeye çalışacağız.

 

Eğitimde yer alacak konular : 

 

  • HTML5 nedir ?
  • HTML5 markup, doküman yapısı
  • Yeni form tagları ve form doğrulama.
  • Video ve Audio tagları ile multimedia.
  • HTML5 Canvas.
  • Local storage, session storage ve Web SQL Veritabanları ile veri saklama.
  • HTML5 ile caching ve offline içerik.
  • HTML5 Geolocation API.
  • HTML5 Web Messaging API.
  • Web Workers ile Threading.
  • Web Sockets.

 

Eğitim hakkında bilgi ve kayıt için cenk.ozdemir@zenithbilisim.com adresine mail atarak iletişim bilgilerinizi bırakmanız halinde size geri dönüş yapılacaktır.

,

nopCommerce Paypal Standart Ödeme Metodunda address_override Özelliği

19. Ocak 2012

nopCommerce açık kaynak e-ticaret uygulamasıE-Ticaret uygulamaları için ödeme metodlarından biri olan Paypal'a entegrasyon için paypal'ın sağladığı Payment Data Transfer metodu kullanılıyor.

nopCommerce e-ticaret uygulamasında bu ödeme metodunu aktif etmek için öncelikle Paypal hesabını ticari veya premium hesaba çevirip, PDT özelliğini aktif etmeniz gerekiyor.

Ardından nopCommerce yönetim panelinden ödeme seçenekleri ayarlarından Paypal Standart'ı seçip PDT ayarlarınızı kaydediyorsunuz ve e-ticaret uygulamanızın Paypal entegrasyonunu sağlamış oluyorsunuz.

Benzer bir Paypal entegrasyonunu yapmaya çalışırken şöyle bir sorun ile karşılaştım. E-Ticaret uygulamasındaki test hesabım ABD adresi ile kayıtlı iken, ödeme yapmaya çalıştığım hesap Türkiye adresi ile kayıtlı idi. 

Paypal default ayarlarda bu tarz durumları güvenlik sorunu olarak değerlendirip aşağıdaki gibi bir hata mesajı ile ödemeyi durduruyor : 

Paypal does not allow your country of residence to ship to the country you wish.

Bu sorunu aşmak için 2 yöntem mevcut. İlki uygulamamızı kullanan kullanıcıların uygulamamızda kayıtlı olan adres bilgileri ile kullandıkları Paypay hesabının adres bilgilerinin denk olmasını beklemek.

İkinci yöntem ise Paypal entegrasyonunda kullanılan parametrelerden address_override değişkeninin değerini "0" olarak paypal'a iletmek. address_override değişkeni, fatura adresi ile paypal hesabının denk olması kuralını aşmayı sağlayan bir parametre, gerekli bilgi Paypal'ın 

HTML Form Basics for Website Payments Standard adresinde mevcut.

Bu değişkenin değerini nopCommerce'de değiştirmek için maalesef Nop.Plugin.Payments.PayPalStandard plugin'ini değiştirmeniz gerekiyor. 

Şuan plugin kodunda görüldüğü üzere yazılım ekibi, bu özelliği Paypal Ayarları sayfasına eklemeyi planlıyor. Ama henüz eklenmiş durumda değil. İleride çıkacak nopCommerce sürümlerinden birinde bu seçenek yönetim sayfasında yerini alabilir.

1. Nop.Plugin.Payments.PayPalStandard projesini açın.

2. PayPalStandardPaymentProcessor.cs sınıfı içerisindeki PostProcessPayment metodu içerisindeki aşağıdaki satırı bulup değerini 0 olarak eşitleyin.

//TODO move this param [address_override]

//to settings (PayPal configuration page)

builder.AppendFormat("&address_override=0");

3. Plugin'i derleyin.

4. Projenin özelliklerinden göreceğiniz üzere projenin output path'i aşağıdaki gibi : 

    ..\..\Presentation\Nop.Web\Plugins\Payments.PayPalStandard\

        Nop.Plugin.Payments.PayPalStandard.dll

Bu dizinden plugin'nin dll'ini alıp web uygulamanızın Plugins\Payments.PayPalStandard dizinine atıp uygulamanızı restart ettiğinizde Paypal entegrasyonu artık address_override parametresi aktif edilmiş olarak çalışmaya başlayacaktır.

, , , , , , ,

WebUcundan Merhaba !

19. Ocak 2012

Üçüncü denemem sonuncu olacak. Ya önceki blog denemelerim gibi silinip gidecek, yada bu sefer kalıcı olacak. Diye motivasyonum üzerinde olumlu etki yapacağını düşündüğüm bir cümle ile merhaba günlüğüme başlamak istedim.

Amacım, öğrendiklerimi ve karşılaştığım problemlere bulduğum çözümleri sistematik olarak bir yerlerde tutabilmek. Bu notlarımı günlük sayfaları halinde paylaşırken aynı zamanda yararlı olabilmek, farklı deneyimler yaşamış arkadaşlarla fikir alışverişinde bulunabilmek.

Günlüklerimde web programlama ile alakalı karşılaştığım pratik çözümler, bir yerlerde gizli kalmış özellikler, yeni trendler ve eski güzel günlerden bahsetmeye gayret edeceğim. Zaman zaman konu dışı paylaşımlar da yapma hevesim var, bu off topic paylaşımları başka bir günlük altına taşımak hevesimde.. Neyse şimdilik fazla hayal kurmadan bir başlangıç verip gerisinin zamanla oluşmasını istiyorum :)


Sevgiler herkese...

,