Asp.Net Core Mvc Request Life Cycle (Customize Pipeline )
Merhaba arkadaşlar, bugünkü yazımda Asp.Net Core Mvc Request pipeline içersinde hangi aşamalarda nasıl araya girebileceğimizden, yine bu aşamalarda request-response’ a nasıl müdahale edebileceğimize değiniyor olacağım.
Bu yazı bir önceki yazımın “Asp.Net Core Mvc Request Life Cycle” devamı niteliğindedir. Bu yüzden başlarken Asp.Net Core Mvc Request Life Cycle ile ilgili bilgi sahibi olduğunuzu varsayacağım.
Not: Customize edeceğimiz componentler için, nedir, ne değildir gibi detaya girmeyeceğim. Bunun için bir önceki yazımı okumanızı şiddetle öneriyorum. Not: Örnek uygulamayı Visual Studio 2019 üzerinde .Net 3.1 Web Api template ile geliştireceğim.
Custom Middleware
Middlaware asp.net core’un en önemli yapı taşlarındandır. Mvc Request Life cycle başlamadan araya girmek istersek bunu middleware vasıtasıyla yapabiliriz. Pipeline içerisine yeni bir aşama ekliyoruz gibi düşünebiliriz. Pipeline müdahil olmak için en çok kullanılan yöntemdir desem yanılmam sanırım.
Pipeline içerindeki diğer yapıların(componentlerin) aksine middleware tanımlamak için miras almanız gereken bir class veya imlemente etmeniz gereken bir Interface bulunmamaktadır.
Şimdi gelen request işlenirken beklenmeyen bir hata (unexpected errors) var ise onu yakalayıp genel bir result dönelim.
Proje ana dizinine Middlewares adında bir folder ekliyorum. Akabinde UserFrendlyResultMiddleware adından bir class oluşturalım.
UserFrendlyResultMiddleware ile ilgili kodları aşağıda paylaşıyorum. Sonrasında neler yaptığımıza bakalım.
Middleware, diğer .net bileşenlerin aksine oluşturulurken bir base class miras almak veya interface implemete etmek zorunda değildir. Dikkat etmemiz gereken 2 konu var. Pipeline akışını devam ettirmek adına, bir sonraki Middleware’i tetiklemek için Constructor injection aracılığıyla RequestDelegate’i almak ve Invoke methodu ile de HttpContext nesnesini almak. Ki burada da dependecy injection söz konusu.
.Net core Middleware’ler için constructor injection yanında, Invoke methodu için de injection desteği sağlamaktadır. Middleware’ler uygulama ayağa kalkarken pipeline’a dahil olmaktadırlar. Durum böyle olunca pipeline’a dahil olurken ihtiyaç olunan yardımcı servisler Contructor (register olurken bu servislerin lifetime’larının Singleton olmasına dikkat ediniz) aracılığı ile inject olurken, request işlenirken ihtiyaç olunan servislerde Invoke methodu aracılığı ile inject olur.
Şimdi gelelim neler yaptığımıza. Öncelikle akışı devam ettirebilmek için bir sonraki RequestDelegate nesnesini, akabinde Middleware pipeline dahil olurkenki süreçte log almak için ILogger servisini constructor injection vasıtasıyla aldık. Sonrasında Invoke medhodu ile ihtiyacımız olan HttpContext nesesini aldık. Ve beklenmedik hataları handle etmek için tüm pipeline’ı Try cath bloğuna dahil ettik.. Bir hata ile karşılaştığımızda aldğımız hatayı loglamak ile birlikte statuscode:500 olarak belirleyip ortak bir result set ediyoruz.
Middleware’imiz hazır şimdi bunu pipeline’ nasıl ekleyeceğimiz ile ilgili kısma geldik. Middleware pipeline’a dahil etme işlemini Startup.cs içerisideki Configure methodundan vasıtasıyla yapıyoruz. Configure methodunda parametre olarak geçilen IApplicationBuilder için yazılmış olan UseMiddleware extension methodundan yararlanıyoruz.
Not:Middleware’ler Configure methodu içerisindeki sıra ile pipeline’a dahil olmaktadır.
Test işlemi yapmak için Features adında bir controller ekliyorum. Get adında bir action method tanımlayıp beklenmedik bir hata fırlatıyorum :)
Uygulamamızı ayağa kaldırıyorum. Uygulamamıza ayağa kaldırdığımızda console loglarında middlewere pipeline’a dahil olduğunu görebiliyoruz.
Projeyi aya kaldırdığımıza göre test edbiliriz. Bunun için postman tool’undan yararlanıyorum.Ben uygulamayı 5000 portun üzerinden ayağa kaldırdım. http://localhost:5000/api/Features url’sini postaman üzerinden çalıştırdığımızda sonuç aşağıdaki gibi olacaktır.
Gördüğünüz gibi Status Code 500 olarak dönmek ile birlikte result olarak belirlediğimiz mesaj döndü.
İkinci adım olarak gelen hatayı da loglamıştık. Console’dan loglarımıza baktığımızda fırlatmış olduğumuz hatayı da ayrıca görüyoruz.
Custom Routing Middleware
Roiting Middleware .net core 3.0 ile hayatmıza giren bir bileşendir. Asp.Net core 3.0 öncesi Endpoint Middleware’e gelmeden yapılan requestin path eşleştirmesi yapılmıyordu. Github’da açılan issue vs sonrası, Microsoft Endpoint sürecini 2'ye ayırdı. Rouing Midddleware ile eşleştirme yapıp Endopoint middleware ile reqestin processing’i işleniyor diyebiliriz.
Tanımlama yapısı ile oluşturduğumuz custom middleware’den bir farkı bulunmamaktadır. Routing Middleware’den sonra tanımladığınız middleware’lerde yapılan requestin eşleştirilmiş path’ine ulabilirsiniz. Bu sayede path’e göre yapacağınız işlemlerde endpoint sürecine gelmeden requesti sonlandırabilir yada müdahale edebilirsiniz.
Uygulama içinde modüllerimizin aktif ve pasif durumunu appsettings.json içerisinden yonettiğimizi varsayalım. Request yapılam modül pasif ise mvc pipeline’nına dahil olmadan requesti sonlandıralım.
Öncelikle Actionlarımızı oluşturup, request için pathlerini belirleylim. Sms ve Email için action tanımlamaları aşağıdaki gibidir.
Daha sonra modüllerin aktif/pasif durumlarını appsetting.json içerisinde tanımlıyoruz.
Middewares dizinin için ModuleStateControlMiddleware adında bir class ekliyorum. ModuleStateControlMiddleware içeriği aşağıdaki gibidir.
Gördüğünüz gibi aslında custom middleware tanımladan bir farkı yok. Eşleşen bir endpoint var ise, IConfiguration vasıtasıyla appsetting. içerisindeki value ile kontrol edip, pipeline’a devam ediyoruz veya sonlandırıyoruz.
Son olarak tanımlamış olduğumuz middleware’i pipeline dahil ediyoruz. Startup.cs Configure methodunun son hali aşağıdaki gibidir.
Dikkat: Exception ile ilgili middeware’imiz RoutingMiddleware’den önce tanımlanırken, ModuleStateControlMiddleware ise, routing’ten sonra tanımlanmıştır.
Postman ile test işlemimizi gerçekleştirelim. Öncelikle Email için testimizi yapalım. Sonuç aşağıdaki gibidir.
Şimdide Sms için Request’te bulunalım. Sonuç aşağıdaki gibidir.
Gördüğünüz gibi asp.net core 3.0 ve sonrasında mvc pipeline’nına gelmeden routing ile ilgili işlemlerimizi gerçekleştirebiliyoruz.
Custom Action Filter
Action invoke olmadan hemen önce ve sonrasında devreye giren bileşenlerdir. Action invoke öncesi ve sonrası yapmak istediğinizi bu aşamada yapabilirsiniz.
Not:Daha detaylı bilgi için önceki yazıma bakabilirsiniz.
Specifik bir action için mobile’den gelen requestlere farklı cevap verme gereksinimimiz olduğunu varsayalım.
Action Filter’ımızı tanımlayalım. Bunun için ActionFilterAttribute class’ından türetmemiz gerekmektedir.Naming Convention’dan anlayacağımız gibi bu bir attribute’dir.
Ana dizine Filters adında bir klasör açıp, MobileRedirectFilter adında bir class oluşturuyoruz. MobileRedirectFilter class’ımızın içeriği aşağıdaki gibidir.
Mobile requstleri, belirlenen path’e yönlendirmek için Controller ve Action adında 2 property tanımlıyoruz. Akabinde gelen request’in header’ında x-mobile adında bir key var ise belirlenen action’a yönlendiriyoruz.
Actionlarımızı tanımlayıp, pathlerini belirleyebilim.
Info için MobileActionFilter’ımızı tanımladık. Artık Postman ile test işlemimizi gerçekleştirebiliriz. Öncelikle x-mobile olmadan request’imizi gerçekleştirelim. Sonuç aşağıdaki gibi olacaktır.
Şimdi ise x-mobile key’imizi requeste dahil edelim. Sonuç aşağıdaki gibi olacaktır.
Action filter’lar mvc pipeline içerisinde en çok kullanılan bileşendir diyebiliriz.
Custom Model Binder
Çok sık kullanılmasa da, bazen hayat kurtarır diyebilirim. Action için belirlediğiniz parametreler için custom binder yazmak istediğimiz kullanacağımız özelliktir. Bu sayede kendi custom data formatımız ile request yapıp, result alabiliriz.
Model Binder nedir ne değildir bakmak isteseniz buraya alalım.
Senaryo: List<T> olarak olarak belirttiğimiz parametrenin datasını csv formatında gönderdiğimizi varsayalım. Örneğimizde T, Product olacak.
Custom Model Binder tanımlamak için IModelBinderProvider ve IModelBinder interface’inden yararlamamız gerekiyor. IModelBinderProvider’ı, bind işlemi için hangi provider’ı kullacağımızı belirtmek için, IModelBinder interface’ini de bind işlemini gerçekleştirmek için kullanıyoruz.
public class Product{public string ProductName { get; set; }public string Description { get; set; }public int Count { get; set; }}
Öncelikle, Product contract’ımızı tanımlayıp, akabinde ModelBinder adında bir klasör oluşturup, CSVModelBinder ve CSVModelBinderProvider adında 2 class oluşturuyoruz.
ModelBinder’ımızı tanımlayalım.
ModelBinderProvider’ımızı tanımlayım.
ModelBinder’ımızı mvc pipeline’ına dahil etmek ile birlikte Contract resolving için, NewtonsoftJson’ı default olarak belirledik.
Son olarak post edilecek Action’ı da tanımlıyoruz
[Route("/products", Name = "Products")][HttpPost]public IActionResult Post(List<Product> orders){return Ok(orders);}
Sonuç itibari ile csv formatında post ettiğimiz datayı CsvModelBinder vasıtası ile List’e çevirip result olarak bu list’i dönüyoruz.
Artık test etmeye başlayabiliriz. Postman ile yapılan request ve sonuçları aşağıdaki gibi olacaktır.
Post Edilen Data
notebook,20,for developer
phone,10, for salesman
ball,25,for futboller
Sonuç
Aspnet.Core Mvc ile proje geliştirilirken en çok başvurulan bileşenleri nasıl custumize edeceğimizi konuştuk. Değinmediğimiz bir çok konu olmak ile birlikte, gerisi merakınıza ve ihtiyacınıza kalıyor. Umarım keyif aldığınız bir yazı olmuştur.
Projenin kodların github adresimden ulaşabilirsiniz.
Bir sonraki yazıda görüşmek dileğiyle, sağlıcakla kalın.