Asp.net MVC JsonResult Tipli Metodlar İçin Unit Test

24 Nis

ASP.NET MVC mimarisi üzerinde test güdümlü çalışırken karşılaşacağımız test tiplerinden biri de JsonResult tipindeki action metodların testidir. Test tipinde veri döndüren metodların testleri kolay olabilmektedir. Ancak JsonResult tipindeki metodlar veriyi JSON tipine dönüştürerek sunduğu için, metod tarafından bize sunulan veriler text şeklinde olmayacaktır.

Normalde önce test metodu yazılarak gidilmelidir ancak burada test kodu geliştirmeyi konu almadığımız için, yazılmış metodu test ederek ilerlemeye çalışalım.

Test edilecek metod şu şekilde belirlenebilir.

public JsonResult GetMenu()
{
     var menu = new List<Menu>
     {
         new Menu{ MenuID=1, MenuName="Hamburger, Cola, Cips"},
         new Menu{ MenuID=2, MenuName="Cheeseburger, Cola, Cips"},
         new Menu{ MenuID=3, MenuName="Pizza, Cola, Cips"}
     };

     return Json(menu,JsonRequestBehavior.AllowGet);
}

Yemek menüsünü JSON formatında sunan bir metod.

Bu metodun test edilmesi sırasında aslında bazı sorulara yanıt arıyoruz olacağız. Bir başka deyişle önce test metodunun yazıldığı senaryolarda iddalar doğrulanarak gidilir. Bizim senaryomusda ise GetMenu metodunun:

  • Tipinin JsonResult olduğu iddası
  • Boş veri döndürmediği iddası
  • Dönen verinin formatının ne olacağının iddası

ortaya atılarak doğrulanmaya çalışılmıştır.


[TestMethod]
public void GetMenu_ShouldReturn_JSON()
{
    // Arrange
    HomeController controller = new HomeController();

    // Act
    var result = controller.GetMenu() as JsonResult;

    // Assert
    Assert.IsInstanceOfType(result, typeof(JsonResult));
    Assert.IsNotNull(result.Data);
}

İlk iki iddamızı bu metod sayesinde dğrulayabiliriz. Bu test başarıyla geçecektir. Ancak metodun döndürdüğü verinin ne olduğunu veya string şeklinde alınıp şuna eşittir denilebilmesi için serileştirilme işleminden geçirilmelidir. Çünkü result.Data nesnesi object tipindedir, ve içeriği okunamaz durumdadır. Serileştirilme işlemi sonrasında işlenebilir bir somut veri elde edilebilir.

     Assert.AreEqual("", result.Data.ToString());

Şeklinde bir idda hata döndürecektir. Test Fail duruma düşecektir.

Aşağıdaki lekilde serileştirme işlemini gerçekleştirdiğimizde bu tür sorunlarla karşılaşmayacağız.

[TestMethod]
public void GetMenu_ShouldReturn_JSON()
{
     // Arrange
     HomeController controller = new HomeController();

     // Act
     var result = controller.GetMenu() as JsonResult;

     var serializer = new JavaScriptSerializer();
     var output = serializer.Serialize(result.Data);

     // Assert
     Assert.IsInstanceOfType(result, typeof(JsonResult));
     Assert.IsNotNull(result.Data);
     Assert.AreEqual(@"[{""MenuID"":1,""MenuName"":""Hamburger, Cola, Cips""},
                        {""MenuID"":2,""MenuName"":""Cheeseburger, Cola, Cips""},
                        {""MenuID"":3,""MenuName"":""Pizza, Cola, Cips""}]",
                        output);
}

Tekrar görüşmek dileğiyle…

ASP.NET MVC Returnurl

24 Nis

Web sayfalarımızda korunan bazı sayfalara yönlendirme yapan bağlantılara(link) tıklandığında, bu sayfanın koruma altında olduğunu belirtmek amacıyla kullanıcıyı sistem giriş sayfasına yönlendiririz. Kullanıcı, giriş yaptıktan sonra en son kaldığı sayfayı hatırlamayabilir. Bu gibi durumlar, genelde forum sayfalarına girdiğimizde karşımıza gelir. İstediğimiz konuyu görmek istediğimizde üye giriş sayfasına yönlendiriliriz ve giriş yaptıktan sonra kaldığımız yerden devam ederiz. Giriş yapıldıktan sonra kaldığımız sayfaya yönlendirilmezsek, tekrar arama motoruna gidip konu linkine tıklamak zorunda kalabiliriz.

Kendi hazırladığımız sistemlerde de bu gibi bir durum başımıza geldiğinde, üye giriş yaptığında son kaldığı sayfaya yönlendirilmesi gerektiğinde ne yapmalıyız?

ASP.NET MVC ile hazırlanan sistemlerde login işlemi sonrası en son geldiğimiz sayfaya yönlendirme yapmak için, en son sayfa URL’i returnUrl adında bir değişkende tutulur.

public ActionResult LogOn(LogOnModel model, string returnUrl)
{
      if (Membership.ValidateUser(model.UserName, model.Password))
      {
         if (!string.IsNullOrEmpty(returnUrl))
         {
              return Redirect(returnUrl);
         }
         else
         {
              return RedirectToAction("Index", "Home");
         }
      }

      return View(model);
}

Eğer returnUrl parametresi boş değilse direk o sayfaya yönlendirme yapılır, boş ise Index sayfasına yönlendirilir.

Bu durumda akla gelen ilk soru “link üzerinde returnUrl parametresi nasıl ayarlanmalı?” olacaktır.

@Html.ActionLink(“Giriş”, “LogOn”, “Account”, new { returnUrl=Request.RawUrl }, null)

Request.RawUrl property’sini returnUrl olarak belirliyoruz. Örneğin ürünler sayfasında bir işlem yaparken, giriş yapılması istenirse, kullanıcı adı ve şifre girildikten sonra tekrar ürünler sayfasına yönlendirilmiş oluruz.

Eğer login işlemi sonrasında direk Index sayfasına yönlendirilsin istiyorsak link aşağıdaki gibi olmalıdır.

@Html.ActionLink(“Giriş”,”LogOn”, “Account”)

Tekrar görüşmek ümidiyler…

Asp.NET MVC 4 Beta Yayınlandı

22 Şub

Microsoft tarafında bir yandan .Net Framework 4.5 hazırlıkları sürerken Asp.Net MVC tarafında da yeni sürüm olan MVC4’te gözükmeye başladı. Bu sürümde MVC3 kullanıcılarının aşina olduğu bazı özellikler geliştirilmiş ve yeni uygulama ortamları eklenmiş durumda.

Başlıca Özellikler

  • ASP.NET Web API
  • Yenilenmiş ve geliştirilmiş proje şablonları
  • Yeni mobil proje şablonları
  • Mobil uygulamaları destekleyen yeni özellikler
  • Otomatik kod üretimleri için yeni tarifler
  • Asenkron metodlar için geliştirilmiş yöntemler

Yeni proje şablon görüntüsü aşağıdaki resimde göründüğü gibi daha şık olmuş.

Proje şablonuna javascrip destekli  yeni pencere açılarak login olabilme gibi özellikler eklenmiş durumda.

Proje ekleme sırasında yeni şablonlar göze çarpıyor. Bu şablonlar, mimariye eklenen yeni özellikleri de temsil etmektedir  aslında.

MVC3 sürümünden farklı olarak Mobile Application sayesinde mobile uygulamalar geliştirmek mümkün, Web API projesi sayesinde ise HTTP servislerini programatik bir şekilde geliştirmek mümkün hale gelmektedir.

Bu yazıda MVC4 için bir göz aşinalığı oluşturmaya çalıştık. İlerki yazılarımızda vakit buldukça diğer özellikleri derinlemesine incelemeye çalışacağız.

 

Asp.net MVC Razor Hepler ile Durum Çubuğu Yapmak

15 Eyl

Bu yazıda tamamen kendi emeklerimizle, jquery veya başka hazır kütüphane yardımı almadan bir ilerleme çubuğu(progres bar) yapmaya çalışacağız.

Yeni bir ASP.NET MVC projesi yaratarak işe başlıyoruz.

Projelerde genellikle ingilizce değişken adları kullanmaya çalışıyorum ki yazının ingilizcesini hazırlarsam çalışan örnek projeyi değiştimeye gerek kalmasın.

Örnek projede içiçe iki div etiketleri kullanıyoruz. Dıştaki div kapsül, içteki div ise durumu göstermeye yarayacak.

Çalışan uygulama görüntüsünü şu şeklide verelim ki daha fazla merak uyandırmadan düşünce geliştirmeye geçelim.

Yukarda görüldüğü gibi iç içe iki div etiketi içersinde son duruma ait yüzdelik gösterim yapılmış durumda.

İlk olarak css dosyamız içerisinde durum göstergesi için gerekli ayarlamalar şu şekilde belirliyoruz.

#progressBar{
   border:1px solid #4771a5;
   height:15px;
}

#progressInner{
   background-color:#96b1d2;
   height:15px;
}

Daha sonra razor view ortamında bir helper kodluyoruz. Helper ise aşağıdaki şekilde olacaktır.

@helper  ProgressBar(double BarWidth, double InnerWidth){

   BarWidth = BarWidth < 0 ? 100 : BarWidth;
   InnerWidth = InnerWidth < 0 ? 100 : InnerWidth;
   BarWidth = BarWidth > 400 ? 400 : BarWidth;

   InnerWidth = InnerWidth > BarWidth ? InnerWidth = BarWidth : InnerWidth;

   string barWidth = BarWidth.ToString() + "px";
   string innerWidth = InnerWidth.ToString() + "px";
   string persantage = ((int)(InnerWidth / BarWidth * 100)).ToString()+"%";

   <div id="progressBar"  style="width:@barWidth">
       <div id="progressInner" style="width:@innerWidth"></div>
   </div>
   <label>@persantage</label>

}

Yukarda ProgressBar adında bir helper oluşturulmuş durumda. Helper içerisinde kullanıcının abuk değerler verip gösterge çubuğunu sapıtmaması için bir dizi önlemler alıyoruz. Bar ve Inner değerinin sıfırdan küçük veremesin, dolan mavi kısım Bar değerini geçemesin v.s gibi. Duruma göre daha fazla önlem alınabilir.

Burada dikkat edilmesi gereken husus şudur ki, html etiketlerinin css özellikleri dinamik olarak değişmektedir.

Geriye kaldı ki bu helper nasıl kullanılacak. O da şu şekilde.


<h2>Razor Helper Progress Bar</h2>

@ProgressBar(300, 120)

Evet sadece bu kadar. ProgressBar helper’ı artık farklı denemelerle kullanılabilir duruma getirdik.

Nerelerde kullanılabilir?

Veritabanından çekilen değerleri göstermek için ideal bir yöntem olabilir. Örneğin bugün en çok satış yapan çalışanın yaptığı satış bugün yapılan toplam satışların yüzde kaçına tekabül ediyor.

Neden bununla uğraşalım?

Çünkü koda tamamen hakimiz. Yukardaki örnekte Bar yüksekliğini css içerisinde sabitledim. İstersek dışarı çıkarıp Width özelliği gibi dinamik hale getirebiliriz.

Soru: Sizce ProgressBar metodunda double parametreler string dönüşümü yapılmadan style içersine nasıl yerleştirilir?

Uygulamanın çalışan kodları buradadır.

Umarım faydalı olmuştur herkese iyi çalışmalar.

Asp.net Mvc Model Binding

5 Eyl

MVC mimarisinde açıklanması gereken bir konu da Model Binding konusudur. Bu yazıda Model Binding konusunu incelemeye çalışacağız.

Tanım: Model Binding HTTP request ile gelen verilerinin ayrıştırılarak ilgili Controller’da bulunan Action metod parametrelerine ve uygun .NET tiplerine dönüştürülmesi işlemidir.

Eğer bir action metod parametre almışsa Model Binding işlemi gerçekleşmektedir.

Arka planda gerçekleşen olaylar: Şimdi aşağıdaki gibi Index adında, string türünde bir tane parametre alan bir Action metod olduğunu varsayalım.

public ActionResult Index(string name)

Index Action’ı çağırılmadan önce arka plan işlemlerini yönetecek iki önemli bileşen vardır. Bunlar ValueProviderDictionary ve DefaultModelBinder’dır.

ValueProviderDictionary: HTTP request ile gelen parametreleri çekerek string halinde depolar. Bu işlem aşağıdaki sırayla gerçekleşir.

  • Request.Form[“name”] var mı diye bakar.
  • Yoksa RouteData.Values[“name”] var mı diye bakar.
  • Yoksa Request.QueryString[“name”] var mı diye bakar.
  • Yoksa null değer döndürür.

Böylece ValueProviderDictionary  HTTP request ile gelen değerleri elde etmiş olur.

DefaultModelBinder: ValueProviderDictionary sınıfının elde ettiği değerleri uygun .NET objelerine gönüştürme görevini üstlenir. Yukardaki örnekte “name” parametresi zaten string tipindedir. Bu durumda DefaultModelBinder dönüşüm yapmak durumunda değildir. Fakat aşağıdaki gibi int veya başka türde parametre alan Action metodlar olabilir.

Durum 1

public ActionResult Index(int id, decimal rate)

Bu durumda DefaultModelBinder sınıfı parametre tip dönüşümlerini otomatik olarak yapacaktır.

Durum 2

Şu ana kadar olan kısımda HTTP request ile gelen isteklerin ayrıştırılıp uygun .NET tiplerine dönüştürüldüğünü gördük. Ancak aşağıdaki gibi özel bir tipin modele bind edilmesi gereken bir durum sözkonusu olabilir.

public ActionResult Index(Person person)

DefaultModelBinder yukarıdaki gibi özel bir .NET tipi ile (Person) karşılaştığında Reflection yardımına koşar. Reflection sayesinde özel tipe ait tüm public özellikler ValueProviderDictionary sınıfı tarafından sağlanan değerleri alır.

Binding

Model binding işlemini bu noktadan itibaren bir örnek yardımıyla incelemeye çalışalım. Örneğimizde bir Person model nesnesinin nasıl bind edildiğini inceliyor olacağız.

public class Person
{
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
}

Autoproperty’ler yardımıyla oluşturulmuş Person model tipimiz yukarıda basit bir şekilde oluşturulmuş durumda.

View tarafında bir form oluşturularak, oluşturulan formun Controller tarafında bir action metoda post edilmesi gerekmektedir.

@model MvcModelApp.Models.Person 

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Kişi Bİlgileri</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

Controller tarafında ise DefaultModelBinder tarafından doldurulmuş person nesnesinin kullanılması gerekmektedir.

      [HttpPost]
        public ActionResult CreatePerson(Person person)
        {
            // person nesnesi form bilgileri ile doldurulmuş durumda
            // person nesnesi artık gerekli işlem için 
            // (database, xml kaydı v.s) kullanılabilir

            return RedirectToAction("Index", "Home");
        }

Yukarıdaki action metodunun parametresi olan Person nesnesi DefaultModelBiner tarafından doldurulmuştur.  @model MvcModelApp.Models.Person ile view sayfasının modelinin belirlendiğini görüyoruz. Controller tarafında CreatePerson metoduna post edilen form bilgileri Person nesnesine doldurulmaktadır.

Bu işlem şöyle gerçekleşmektedir: DefaultModelBinder action metodun aldığı parametre tipinden (Person) bir nesne oluşturarak Property’lerini view sayfasından gelen form bilgileri ile eşleştirerek doldurmaktadır.

Umarım faydalı bir yazı olmuştur. Herkese iyi çalışmalar. Bir sonraki yazıda görüşmek üzere.

Asp.net MVC 4 Yol Haritası

24 Ağu

Microsoft ASP.NET MVC 4 Yol haritasını yayınladı. Tabi bu yol haritası üzerinde değişklikler yapılabilmesi muhtemel geçici bir bakış açısı gibi duruyor. Bu yol haritasında dikkat çekici üç hazırlık mevcut. Bunlar yeni araçlar (tooling), genişletilmiş mobil platform desteği ve bulut tabanlı (cloud based) çözümler.

ASP.NET MVC takımındaki bir diğer çalışma ise Genişletilebilir Görev Tabanlı Araçlar (Recipes = (Extensible Task-Based Tooling). Örneğin view katamnında bir datagridi doldurup göstermek için önce data-access sınıfları hazırlanır, sonra controller tarafında veriler bir model doldurulur ve view katmanına sunulur. Bu çözümler (Recieps) denilen araçlar bu işlemlere yardım edecek. Kullanıcılar kendi çözümlerini de kendileri yazabilecekler. Yazılan çözümler bir arayüz vasıtası ile uygulamaya dahil edilebiliyor olacak.

Smartphone ve tablet tarafından olaya baktığımızda Microsoft ASP.NET MVC takımı template vb. yöntemlerle ASP.NET MVC 4 web sayfalarının mobil versiyonuna daha iyi destek vermeyi düşünüyor. Aşağıda MVC3 ve MVC4 ile yapılmış web sitelerinin mobil ortamda görüntülerini incelediğimizde farkı daha iyi anlamış olacağız.


Ele alınan bir başka değişiklik ise özellikle tablet ve mobil cihazlar için yeni bir proje şablonu. Bu şablonun da görünümü yine aşağıdaki gibi olacak:


Tabi bu kadarla da kalmayıp aygıta göre View oluşturma imkanı da gelen yeni yenilikler arasında. Örneğin mobil aygıtlar için oluşturulan bir View .mobile uzantılı olacak. Aşağıdaki resimde view katmanının görünümü verilmiştir:

Asp.net MVC ve ELMAH ile Hata Log Kaydı

22 Ağu

Web uygulamalarımızı yaparken tüm kontrol elimizde olsun isteriz. Kontrol etmek istediğimiz noktalardan birisi de süreç işlerken oluşan hataların neler olduğudur. Yani bir kullanıcı sistemde işlem yaparken bir hata ile karşılaştığında işleminin neden yarıda kesildiğinden haberdar olmak isteriz. Yazdığımız kodlardaki bug’lar olsun, sunucu kaynaklı sorunlar olsun sorunun ne olduğu, ne zaman meydana geldiği hatta hangi kullanıcının bu sorunu yaşadığı kaydedip incelemek istediğimiz durumlardır.

Bu noktada yazılmış hata yöneticilerinden biri de ELMAH (Error Logging Modules and Handlers) kütüphanesidir. Bu kütüphane süreç işlerken oluşan hataları detaylı olarak kaydeder.

Oluşan Log kayıtlarını birçok ortamda depolayabiliriz. Bu depolama ortamları:

Burada dikkat etmemiz gereken noktalardan biri oluşan hata loglarını nereye kaydedeceğimizi belirlemektir. Biz kayıtları XML dosyada tutacağız.

ASP.NET MVC projesine ELMAH kütüphanesini dahil etmek için Nuget Package Manager eklentisini kullanıyor olacağız.

Package Manager Console açarak Install-Package elmah.xml komutunu giriyoruz. Kurulum otomatik olarak gerçekleşiyor. Web.config dosyası içerisinde ayarlamalar otomatik olarak yapılıyor. Hata kayıtlarının nerede tutulacağını belirleyen kısım ise aşağıdaki gibidir.

  <elmah>   
    <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data/Elmah.Errors" />
  </elmah>

Yükleme işlemi gerçekleştikten sonra HomeController içersine About adında bir index belirliyoruz ve bu metodun hata fırlatmasını sağlıyoruz.

 

http://localhost:1735/Home/About şeklinde çağırdığımızda meşur sarı hata sayfasıyla karşılaşıyoruz. (1735 portu kendi makinamın verdiği port numarası, sizin denemenizde bu numara farklı olabilir.)

Bu hata kaydı  App_Data/Elmah.Errors dizininde XML dosya içersine kaydedilmiş olacak. Hata kaydını görmek için http://localhost:1735/elmah.axd yoluna bakıyoruz ve aşağıdaki şekilde başarılı sonuca ulaşmış oluyoruz.

Asp.net MVC ActionName Attribute

21 Ağu

ActionName attribute bir action metodunun farklı isimlerle adlandırılmasını sağlar. Bu işlem iki farklı yolla yapılır.

Birinci yöntem olarak overload edilmiş, yani aynı isimdeki iki metodu birbirinden ayırmak için kullanabiliriz. Örneğin Product controller Details() adında iki metoda sahip olsun. İlk Details() metodu integer tipinde id parametresi alıyor olsun. İkinci Details() metodu parametre almasın. Bu durumda iki metodu birbirinden ayırmak için ActionName attribute kullanılabilir.

İkinci yöntem olarak ise farklı isimdeki metodları aynı isimle çağırmak isteyebiliriz. Yani ilk yöntemin tam tersi bir durum olabilir.


[ActionName(“Edit”)]
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Edit_GET(Merchandise merchandiseToEdit)
{
     return View(merchandiseToEdit);
}

// POST: /Merchandise/Edit

[ActionName(“Edit”)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit_POST(Merchandise merchandiseToEdit)
{
     try {
           return RedirectToAction(“Edit”);
         }
     catch
         {
           return View();
         }
}

Dikkat: Bir sınıf içinde aynı isimde ve aynı parametreyi alan iki metod bulunamaz. Ancak aynı isimde ve aynı parametre alan iki action olabilir.

Örnekte gördüğümüz gibi iki Edit() metodundan biri HTTP GET operasyonu, diğeri ise HTTP POST operasyonu tarafından çağrılıyor.