Asp.Net MVC JSON Model Bining

7 May

Asp.Net MVC mimarisinde yer alan model binding konusunu daha önceki yazılarda ele almıştık. Bu yazıda ise model binding işlemini JSON nesneleri ile uygylamaya çalışacağız. Web projelerinde ihtiyaç duyduğumuz  javascript tabanlı sorguları gerçekleştirmek için JSON nesnelerini .NET nesnelerine dönüştürmemiz gerekebilir. Request data olarak JSON nesnelerini POST edebilmeliyiz.

Örnek Uygulama

Örnek uygulama olarak bir ürün arama işlemini JSON formatındaki verileri bir Action metoda POST edeceğiz.

Öncelikli olarak Request ile gelen JSON fromatındaki veriyi deserialize ederek .Net nesnelerine dönüştürecek olan ModelBinder sınıfımızı oluşturmalıyız.

public class JsonModelBinder : IModelBinder
{
     public object BindModel(ControllerContext controllerContext,
                             ModelBindingContext bindingContext)
     {
          if (controllerContext == null)
             throw new ArgumentNullException("controllerContext");

          if (bindingContext == null)
             throw new ArgumentNullException("bindingContext");

          var serializer = new DataContractJsonSerializer(bindingContext.ModelType);
          var deSerialized = serializer.ReadObject(
                             controllerContext.HttpContext.Request.InputStream);

          return deSerialized;
    }

}

DataContractJsonSerializer sınıfının ReadObject metodu Request üzerinden ile gelen veriyi (InputStream), ModelBindingContext sınıfının ModelType parametresine göre uygun .Net tipine dönüştürür.

Şimdi de ModelBindingContext sınıfına gönderilecek olan tipi hazırlamalıyız.

[DataContract]
[ModelBinder(typeof(JsonModelBinder))]
public class JsonProductSearchRequest
{
     [DataMember]
     public string Name { get; set; }
     [DataMember]
     public string Color { get; set; }
}

System.Runtime.Serialization isim alanına dahil olan DataContract ve DataMember attribute tipleri sınıfları ve sınıf üyelerini serileştirilebilir hale getirmek için kullanılırlar. (Yeri gelmişken söylemekte fayda var; Bir tipi serileştirmek JSON, XML veya Binary şekillerde olabilmektedir. Biz örneğimizde JSON serileştirme üzerinde duracağız). JSonProductSearchRequest sınıfımızı, model binding sırasında ihtiyaç duyulan attribute olan ModelBinder ile imzalıyoruz, parametre olarak ise daha önceden hazırladığımız JsonModelBinder tipini gönderiyoruz.

Artık Controller tarafında ihtiyaç duyulan Action Metodları hazırlayabiliriz.

public class ProductController : Controller
{
   [HttpPost]
   public JsonResult JsonSearch(JsonProductSearchRequest request)
   {
       return null;
   }
}

JsonSearch Action metodu, daha önceden hazırladığımız JsonProductSearchRequest tipinde bir parametre almaktadır.

Şimdi hazırladığımız bu metoda fiddler yardımıyla bir istek (request) göndererek durumu incelemeye çalışalım.

Fiddler Json POST
Fiddler Json POST

Fiddler aracında Composer sekmesinden Request Tipini POST olarak seçiyoruz. Sunucudaki uygulama linkimizi girerek Request Body kısmına uygun JSON verisini giriyoruz. Execute butonuyla çalıştırdığımızda kodumuza geçip “break point” ile takip edersek:

Json Model Binder
Json Model Binder

Gönderdiğimiz verinin POST edildiğini rahatlıkla görebiliriz.

Bu şekilde etkili sonuç veren bir çözüme ModelBinding nimetlerinden faydalanarak ulaşmayı başardık. JSON tabanlı arama işlemlerini rahatlıkla bu şekilde gerçekleştirebiliriz. Ben bu örnekte View sayfalarıyla uğraşmamak adına fiddler aracından faydalanmayı uygun gördüm.

Bir sonraki yazıda görüşmek üzere.

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.