Makale Özeti

MVC ile Proje geliştirmek artık çok kolaylaştı. Gerek Şablonlar gerek dokümanlar bunun en basit örneği. Ancak herzaman olduğu gibi bizler Katmanlı programlama yaparken en büyük sorun Hata Ayıklama ve Yetkilendirme olmuştur. MVC bu işlemleri kolaylaştırmak adına bizlere Filtreler yazma imkanı sunmaktadır.

Makale

MVC (Model View Controller) Yapısı günümüzde çok kullanılmasada geleceği çok fazla olan bir teknoloji. Tabi bunu afaki söylüyor gibi görünsekte kodlama kolaylığı olsun, izlenebilirliği olsun modülerliği olsun her şeyiyle çok iyi tasarlamış bir yazılım geliştirme yaklaşımıdır. Tarihine bakacak olursak 2007 yılından beri Microsoft tarafından açık kaynak kodlu olarak geliştirilse'de 80'li yıllardan günümüze kadar farklı dil ve platformlarda yaygın olarak kullanılan bir tasarım kalıbıdır aslında.

 

Hal böyle olunca Yılların verdiği birikime rağmen hala insanlar bunu kullanıyorsa geleceğinden söz etmek kaçınılmaz olacaktır. Özellikle "Günümüzde Web Heryerde!" sloganına inanıyorsak.

 

Yazılım geliştiriciler bir n-Tier mimari ile yazılım geliştirirken bu katmanlardan bir tanesi hata yakalama katmanı olurdu. Bu katman, Proje içinde bir yerde hata çıkınca gerekli Loglama, Uyarı (Notification), Yönlendirme (Redirection) gibi işlemleri yapsın diye tasarlanır ve kullanılırdı. MVC içinde Model-View-Controller arasında düm düz bir bakış ile görüldüğü gibi aslında bir Hata ayıklama katmanı gibi bir yapı söz konusu değil ancak kendimiz ekleyebiliyoruz. Yine yetkilendirme için bu yapı söz konusu olabilir. 

 

Peki nereye yerleştireceğiz biz bu yapıları ? Model içerisine  mi? Controller İçersine mi ? View İçerisine mi?

Kod yazarken öğrenebiliriz; Bence Action yani Controller içinde çalışan bir metod için genel bir hata yakalama işlemi yürütsek aslında bizim işimizi görecektir. Peki ama nasıl ? Asp.Net MVC bizler için System.Web.Mvc.dll içerisinde bizlere GlobalFilters adında static bir sınıf ve bu sınıfın içinde GlobalFilterCollection adında static bir collection sunmaktadır. Bu Kolleksiyona bizler ActionFilterAttribute abstract tipinden türettiğimiz Filtreleri ekleyebiliyoruz. Peki bu ne anlama geliyor?

Biz bir FilterAttribute yazıyoruz kendimiz için, Bu attribute'u uyguladığımız her sınıf için Attribute içinde imzası tanımlanmış veya override edilmiş tüm filtre metodları Action'dan önce tetikleniyor. Peki ama biz attribute uygulamadan her Request için çalışmasını istiyorsak ne yapıyoruz ? İşte o zaman uygulama çalışmaya başladığı anda yani Global.asax dosyasının içersinde Application_Start metodunun içerisinde, GlobalFilterCollection içerisine gerekli tanımlamayı yapıyoruz. Böylece her action tetiklendiğinde filtre kodumuzun çalışacağını biliyoruz. 

Şimdi kendimiz için bir FilterAttribute yazalım ama önce hangi metodları kullanacağımızı da bilmek adına bu konuya bir açıklık getirelim. Bu Attribute'a 3 farklı Interface uygulayabiliyoruz. Bu Interface'ler IActionFilter, IResultFilter, IExceptionFilter. Eğer temel sınıf olarak ActionFilterAttribute yazmayı seçersek zaten IResutFilter ve IActionFilter impemente edilmiş ve bu arayüzlerde imzası tanımlı olan OnActionExecuted, OnActionExecuting , OnResultExecuted, OnResultExecuting metodlarını ezmemiz(override) yeterli olacaktır ancak IExceptionFilter yani bir hata oluştuğunda tetiklecenek olan OnException metodu için bizim IExceptionFilter arayüzünü Attribute(Nitelik) sınıfımıza uygulamamız gerekiyor.  Basit bir uygulama yazıp denemekte fayda olduğu düşüncesindeyim. 

 

Boş bir MVC 4 uygulaması açalım. Zaten çoğumuz nerden açıldığını biliyoruz bilmeyenler için Asp.Net'in MVC bölümünden Proje şabloları ve proje oluşturma konusunda yardım almaları faydalı olacaktır. 

 

Projemizin içerisinde Varsayılan olarak bazı klasörler zaten oluşturulmuş olarak gelicek. App_Start, Controllers, Models ve Views. Biz bunların yanına birde Filters klasörü koyacağız ve Attribute Classımızı buraya yerleştireceğiz. Sınıfımızın kalıtımını yaparken dikkat etmemiz gereken bir unsur var. Biz Base sınıf olarak FilterAttribute sınıfını kullanacağız yanlız bu sınıf birden fazla NameSpace içerisinde geçiyor biz bunlardan System.Web.Mvc içinde tanımlı olanı kullanacağız bunun için using belirtecini doğru kullanmanızda fayda var. Temel sınıf olarak System.Web.Mvc.FilterAttribute sınıfını kullandık ve IExceptionFilter arayüzünü implemente ettik. Kodumuzun mevcut görüntüsü aşağıdaki gibi olacaktır;

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 

namespace FiltersTest.Filters
{
    public class HataOlursaMailAt :  FilterAttribute, IExceptionFilter
    {

        public void OnException(ExceptionContext filterContext)
        {
           
        }
    }
}

 

Burada OnException Metodu içine hata olursa Bir hata View nesnesi görüntületip bize mail göndermesini sağlayacağız. HomeController adında bir Controller oluşturalım ve içerisini aşağıdaki gibi dolduralım.

 

 using System;
 using System.Web.Mvc;
 
 namespace FiltersTest.Controllers
 {
     public class HomeController : Controller
     {
 
         public ActionResult Index()
         {
             return View();
         }
 
 
         public ActionResult HataOlustur()
         {
             throw new Exception ("Bu Hata Manuel Olarak Programin Filtre Icinde Hataya Dusmesi Icin Olusturuldu" );
 
         }
 
     }
 }
 
 

 Index Action'u üzerine Sağ tıklayıp AddView diyoruz ve Aşağıdaki gibi bir View Oluşturuyoruz.

  @{  
     Layout = null ;
  }  
 <!DOCTYPE html>
 <html>
 <head>
     <meta name="viewport" content="width=device-width" />
     <title> Index</title>
 </head>
 <body>
     <div>
          @  Html.ActionLink("Hata Olustur" , "HataOlustur" )
     </div>
 </body>
 </html>
 
 

Şimdi elimizde Bir Index sayfası var ve biz bu sayfadaki Hata Oluştur linkine tıkladığımızda hata oluşturan bir HataOlustur Action'umuz var. Peki bu hatayı bize mail atsın istiyoruz ama Try Catch içine bu kodu yazmak bizim için biraz zor olacaktır. O Yüzden Başlamış olduğumuz HataOlursaMailAt filterimizin içini aşağıdaki gibi dolduralım.

 

 using System.Web.Mvc;
 using System.Net.Mail;
 using System.Net;
 
 namespace FiltersTest.Filters
 {
     public class HataOlursaMailAt : ActionFilterAttribute , IExceptionFilter
     {
 
         /// <summary>
         /// Action Calismasi esnasinda bir Exception throw edilirse bu metod calisacak.
         /// </summary>
         /// <param name="filterContext"></param>
         public void OnException(ExceptionContext filterContext)
         {
 
             //Exception nesnesi'mu degilmi kontrol ediyoruz. 
             if (filterContext.Exception != null )
             {
                 //Basitce Mail atacak olan kodlarimizi yaziyoruz.
                 SmtpClient client = new System.Net.Mail.SmtpClient ("mail.*********.com.tr" , 25);
                 NetworkCredential sifreBilgileri = new NetworkCredential ("ertugrul.kara@*********.com.tr" , "****@****" );
                 client.Credentials = sifreBilgileri;
 
                 string Mesaj = filterContext.Exception.Message;
                 MailMessage mail = new MailMessage (
                     "ertugrul.kara@*********.com.tr" , //Kimden
                     "ertugrul.kara@*********.com.tr" , //Kime
                     "Web Sitesinde Hata" , //Konu
                     "Olusan Hata :" + Mesaj //Mesaj icerigi
                     );
 
                 client.Send(mail);
                 //Olusan hatanin maili gonderilmis oldu. 
                 //Peki biz bu noktada hata mesaji goruntuleme isini nasil yapacagiz.
 
                 //Isimsiz tipleri kullanarak bir anonymus tip yarattik.
                 string ErrorModel = Mesaj;
 
                 //Hatayi yakaladiğimizi belirtiyoruz
                 filterContext.ExceptionHandled = true ;
 
                 //Çalıştırmış olduğumuz Action'a bir result dondurmemız gerek. 
                 //Ben View Klasörü içine bir _Hata.cshtml dosyası yazdım ve View'ın 
                 //oraya düşmesini istedim Data olarakta Hata Mesajı aktardım
                 filterContext.Result = new ViewResult
                     {
                         ViewName = "~/Views/_Hata.cshtml" ,
                         ViewData = new ViewDataDictionary (ErrorModel)
                     };
             }
         }
     }
 }
 

 Şimdi tek yapmamız gereken GlobalFilters statik sınıfının içerisindeki Filters kolleksiyonuna yazdığımız Filtrenin bir örneğini atamak. Global.Asax içersindeki Application_Start metoduna gidiyoruz ve sadece aşağıdaki işaretli satırı ekliyoruz.

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 using System.Web.Http;
 using System.Web.Mvc;
 using System.Web.Routing;
 
 namespace FiltersTest
 {
 
     public class MvcApplication : System.Web.HttpApplication
     {
         protected void Application_Start()
         {
             AreaRegistration .RegisterAllAreas();
 
             RouteConfig .RegisterRoutes(RouteTable .Routes);
 
 GlobalFilters .Filters.Add(new Filters.HataOlursaMailAt ());
         }
     }
 }
 

Dilerseniz şimdi  Uygulamamızı test edelim...

 1. Projemizi çalıştırıyoruz ve aşağıdaki ekranı görüyoruz.  Hata Oluştur linkine tıklayınca Debug Modda bize Exception'u yakalamadınız diye tekrar Visual Studio ekranına yönlendirecek ama heyecanlanmayın. Programın Çalışmasını Devam Ettirin.

 

2. Programın çalışmasını devam ettirdiğimizde yazdığımız Exception mesajı Ekranda görüntüleniyor olacak şekilde ayarlandığını göreceksiniz. Aslında bizim amacımız exception'u yönetmek değil bir de mail almaktı Sahi ne oldu mail ?

 

 

3. Gelen mail için Outlook programının ekran görüntüsünü göstermekte fayda var =)

 

 

 

Bu şekilde basitçe bir MVC uygulamasına nasıl bir filtre dahil edeceğimizi anlatmış olduk. Ben burada hatayı mail atsın dedim ama tabi ki örnekleri çoğaltılabilir. Örneğin hatalo login denemeleri için veritabanına bir log kaydı yapabilecek filtreyi yazabilirsiniz... Yine MVC ile birlikte Membership kontrolleri kullanmak istemiyor bunun yerine kendi User - Role based Authentication yapınızı kullanmak istiyorsanız yine aynı şekilde filtre ve Attirbute'lerdan faydalanmalısınız.  

 Bu arada belirtmekte fayda var ki... Eğer yazdığınız filtre Global değil de sadece bazı Action'lar için geçerli olacaksa; "Ör: Kredi kartı ödemelerinde hata olursa mail atsın." Sorunun çözümü için yazdığınız attribute'u Method'a yani Action'a uygulamanız yeterli olacaktır. Böylece sadece Nitelediğiniz metod için filtreleme yapmış olursunuz.

 

 

Ertuğrul KARA

Yazılım Geliştirme Uzmanı

ertugrulkara@msn.com

 

Örnek Uygulama Kodları :

FiltersTest.zip