Resilient Network Services Bölüm 2 – REFIT – 2
Selamlar. Bir önceki yazımda Resilient Network Services için kullanacağımız kütüphanelerden olan Refit için incelemelere başlamıştık. Bu yazımızda bunu tamamlayıp bir sonraki yazılarımız da diğer kütüphanelere değineceğiz.
Önceki yazımızı da Refit ile GET ve POST işlemlerine bakmıştık. Farklı durumlar için incelemiş örnekler göstermiştik. Okumadıysanız bu yazıdan önce Refit ile ilgili ilk yazımı okumanızı tavsiye ederim.
Bu yazımızda refit ile request in Header, Authorization, Multpipart upload, response ve error handling kısımlarına değineceğiz.
Static ve Dynamic Header Tanımlamaları
Static bir header ı refit interface imize ister tüm Interface bazında istersek de metod bazında istediğimiz kadar eklemek için, kullanacağımız yöntem Headers attribute u olucaktır. Örnek olarak;
[Headers("Content-Type: application/json")] public interface IGitHubApi { [Get("/users/{user}")] Task<User> GetUser(string user); [Post("/users/new")] Task CreateUser([Body] User user); }
şeklinde global olarak(yani interface içerisindeki herbir metod için çalışacak şekilde) tanımlayabiliriz. Ya da aynı attribute u sadece istediğimiz metodun üzerine de yerleştirebiliriz.
Bu zaten çok önemli bir durum değil çok fazla da ihtiyacımız olucak değil, ama kullanımını yine de bilmekte fayda var. Daha önemlisi dinamik olarak değişecek olan headerlar için nasıl bir yöntem kullanacağız. Örneğin her bir request için Authorization header ı göndermek istiyorum. Bu header da dinamil bir header olucak tabii ki. Bunun için ise şu şekilde ilerliyoruz.
[Get("/users/{user}")] Task<User> GetUser(string user, [Header("Authorization")] string authorization); var user = await GetUser("erhanballieker", "mytoken.....");
Yukarıda göreceğiniz gibi interface içerisinde tanımlamış olduğumuz metodun header olarak göndermek istediğimiz parametresinin başında Header attribute u nu kullanıp parametre olarak header ın adının ne olacağını veriyoruz(örnekte “Auhtorization” dedik). bu metodu kullanırken de 2. satırda yazmış olduğumuz gibi “mytoken…” olan paraametre request in Headerlarına Authorization header ı olarak ekleniyor olacaktır.
Refit ile request in headerlerı ile oynamakta bu kdar basit hale gelmiş durumda. Son olarak header lar kısmını bitirmeden şundan bahsedelim, Headerları tanımalamak için 3 yer gördük interface üzerinde, metod üzerinde ve parametre üzerinde, peki bunlardan hangisi daha öncelikli. yani aynı header ismi ile üçünede bir değer koysak hangisi kazanır,
öncelik sırası şöyle;
- Parametre başına tanımlanan değer.
- metod başına tanımlanan değer
- interface seviyesinde tanımlanan değer.
yani aşağıdaki gibi bir örnek için request e “MyHeader” ismiyle eklenecek olan değer parametre olarak verilen değerinki olucaktır.
[Headers("MyHeader: MyHeaderInterface")] public interface IGithubApi { [Headers("MyHeader: MyHeaderMetod")] public Task GetUser(string username, [Header("MyHeader")] string parameter = "actaulValue"]); }
Multipart Upload
[Multipart] attribute u ile işaretlemiş olduğumuz metodlar multipart content type olarak submit olucaklar.desteklenen multipart methods parametre tipleri aşağıdaki gibidir.
- string (parameter name will be used as name and string value as value)
- byte array
- Stream
- FileInfo
parametrenin adı multipart datanın adı olarak set edilir default olarak. Yine AliasAs kullnarak bu durumu değiştirebiliriz.
public interface ISomeApi { [Multipart] [Post("/users/{id}/photo")] Task UploadPhoto(int id, [AliasAs("myPhoto")] StreamPart stream); }
Bu metoda multipart olarak data göndermek için aşağıdaki şekilde kullanabiliriz.
someApiInstance.UploadPhoto(id, new StreamPart(myPhotoStream, "photo.jpg", "image/jpeg"));
Response Handling
Refrofitin aksine Refit te synchronous api call yapamıyoruz. Tüm requestler Task ile veya IObservable ile async olarak yapılmalı. Generic parametresi verilmemiş bir Task olarak metodu tanımlamak sadece request in başarılı tamamlanıp tamamlanamadığını bize söyler o kadar.
[Post("/users/new")]
Task CreateUser([Body] User user);
await CreateUser(someUser);
Eğer parametre tipi HttpResponseMessage veya string ise content olduğu gibi geriye döner. Task objesi içerisine bir model verirsek dönen response Refit tarafından otomatik olarak istediğimi model e deserialize edilir ve o şekilde bize ulaşır.
//response string olarak geri döner. (ham json data gibi)
[Get("/users/{user}")]
Task<string> GetUser(string user);
//tüm response IObservable olarak Reactive Extensionlar ile kullanıma hazır şekilde döner.
[Get("/users/{user}")]
IObservable<HttpResponseMessage> GetUser(string user);
//response verilen generic tipe dönüştürülüp bize ulaşır.
[Get("/users/{user}")]
Task<User> GetUser(string user);
Genel olarak bir api ile haberleşirken herhangi bir modelin CRUD işlemlerini yapacak endpoint e request atmamız gerekecektir. Bunun için her model için ayrı ayrı interface veya metodlar tanımlamak yerine, Refit bize generic olarak interfaceleri tanımalama imkanı da sunuyor. Aşağıdaki örnekte ki gibi tüm Refit interface imizi generic bir parametre tipine bağlı olarakta tanımlayabiliriz.
public interface ICrudApi<T, in TKey> where T : class { [Post("")] Task<T> Create([Body] T payload); [Get("")] Task<List<T>> ReadAll(); [Get("/{key}")] Task<T> ReadOne(TKey key); [Put("/{key}")] Task Update(TKey key, [Body]T payload); [Delete("/{key}")] Task Delete(TKey key); }
Yukarıda göreceğiniz gibi projemizde ki herbir modelin crud işlemleri için bu şekilde bir Refit interface i tanımlayabiliriz. Herbir request için ilgili HttpVerb ü verilip, Task içerisinide geri dönen response un deseriazlize edilecek generic type veriliyor.
Son iki yazımızda Refit e oldukça değinmiş incelemiş olduk. Bir sonraki yazımızda Resilient Network Services serimizde Fusillade in kullanımı ile devam ediyor olacağız.
Görüşmek üzere.
Resilient Network Services Bölüm 2 – REFIT
Selamlar, bir önceki yazımda geçtiğimiz haflarda Microsoft Türkiye de yaptığımız meetup anlattığım konudan bahsetmiştim. bir önceki yazımda bununla ilgili kısmı okuyabilirisniz. Şimdi o yazıda bahsettiğim kütüphaneleri sırayla incelemeye başlayalım. Bu yazımızda Refit i detaylıca ele alacağız.
Refit Square’in Retrofit kütüphanesinden esinlenerek oluşturulmuş, type-safe ve Rest Api ınızı bir interface olarak tanımlamanızı sağlayan güçlü ve kullanımı basit bir kütüphane. B kütüphaneyi kullanarak, projenizin haberleşeceği API yada API ları isteiğinize göre bir veya birden çok Interface olarak tanımlayıp gerisini Refit e bırakıyorsunuz. O da sizin bu Interface inizi kullanarak arka tarafta tüm haberleşme ve obje dönüşümleri işlerini hallediyor. Basit bir örnek ile incelemeye başlayalım.
Aşağıdaki gibi bir Interface i ele alalım.
public interface IGitHubApi { [Get("/users/{user}")] Task GetUser(string user); }
Burada projemizde bir noktada github api ını kullanacağımızı düşünün. Bir user bilgilerini almak için ise;
GET /users/:username
şeklinde bir request atmamız gerekiyor. Yani özetle bir GET request ini users/erhanballieker şeklindeki url e post etmemiz gerekiyor. burada user/ dan sonraki kısım parametre olarak verilmeli. İşte tam olarak bu request e karşılık gelen haberleşmeyi yukarıda ki interface olan GetUser ismini verdiğimiz metod daki gibi tanımlayabiliriz. Interface e dikat ederseniz atılacak request in HttpVerb kısmı Attribute olarak metodun başına ekleniyor ve içerisine urli mizi giriyoruz. Bunu async çalıştırmak istediğimiz için geri dönüş tipimizi Task olarak verip url deki parametre yerine karşılık gelecek olan kısmı da metodun parametresi olarak veriyoruz. Url deki parametre ismi ile metotda tanımlanan parametre adı aynı olmasın istersek de sadece
GetUser([AliasAs("user")] string username)
şeklinde sadece ismini değiştirdiğimiz parametrenin başına AliasAs(“”) attribute u ile url deki hangi parametreye karşılık gelmesini istediğimizi söyleyebiliriz.
Peki bu interface i proje içerisinde nasıl kullanacağız? Yani bu interface gerçekten benim bu api ile haberleşmemi sağlayan hale nasıl gelecek? Bu da çok basit.
var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com"); var user = gitHubApi.GetUser("erhanballieker");
Yukarıda gördüğümüz gibi, RestService.For.. dedikten sonra generic parametre olarak yazdığımız Api ı vermek ve sonrasında haberleşeceği url i söylemek yeterli. daha sonrasında interface içerisinde yazmış olduğumuz metodlar zaten intellisense ile bile ulaşacabileceğimiz şekilde karşımıza seçenek olarak çıkıyor. Bizde yazdığımız GetUser metodunu kullanarak ilgili request i atıyoruz.
verdiğimiz parametrelerin eğer url tarafında bir karşılığı yok ise bu parametreler otomatik olarak querystring parametreleri olarak kullanılıyor. örnek olarak;
[Get("/group/{id}/users")] Task<List<User>> GroupList([AliasAs("id")] int groupId, [AliasAs("sort")] string sortOrder); GroupList(4, "desc"); >>> "/group/4/users?sort=desc"
metodumuzdaki ilk parametrenin yani “id” nin url de yeri var bu yüzden request url imiz /group/4/users oldu. sonrasında verdiğimiz parametre olan “sort” ise url de bir parametreye denk gelmediğinden url in sonuna querystring olarak eklendi. ve sonunda request in gideceği url in son hali yukarıdaki gibi oldu.
POST Request
Peki refit ile bir post request i ne şekilde göndeririz, bunu nasıl body e gömeriz yada body olarak değil de mesela formurlencoded olarak göndermek istersek ne yapmamız lazım buna bir bakalım.
[Post("/users/new")] Task CreateUser([Body] User user);
Yukarıda gördüğünüz gibi interface imiz içerisine bir CreateUser metodu ekledik. metodun POST verb ü ile çalışmasını istediğimizi relative gideceği url i verdik. Parametre olarak da bir user objesi verdik ve bu parametrenin de başına [Body] attribute u koyarak bu metodun canlı bir haberleşme sırasında ilgili url e giderken aldığı User parametresini Json a (defaul olarak) serialize edip body e koymasını belirtmiş olduk.
Post işlemi yaparken body data sını gönderebileceğimiz 4 ayrı seçeneğimiz var
bunlar şöyle;
- StreamContent: eğer tipimiz stream ise bu da StreamContent olarak gönderilir.
- StringContent: eğer tipimiz bir string ise ve Body attribute una ekstra bir parametre vermediysek, mesela [Body(BodySerializationMethod.Json)] string name demediysek body miz verdiğimiz string parametreyi otomatik olarak StringContent olarak yollar.
- FormUrlEncoded: Eğer paramtremizin başına koyduğumuz Body attribute una seçenek olarak şöyle dersek, [Body(BodySerializationMethod.UrlEncoded)] User user, o zaman bu obje form-url-encoded olarak gönderilir.
- Bunların dışında belirtilmemiş tüm opsiyonlar için RefitSettings kısmında belirttiğiniz (belirtmenize gerek yok) serializer ile content json a serialize edilir ve body e konur.
Refit interface inizi projede bir api a dönüştürürken aşağıdaki şekilde de özelliklerini değiştirebilirsiniz.
var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com", new RefitSettings { ContentSerializer = new JsonContentSerializer( new JsonSerializerSettings { ContractResolver = new SnakeCasePropertyNamesContractResolver() } )});
Bir de şuna değinelim. Eğer Stream olarak bir content göndermek istiyorsanız, refit default olarak buffering yapmıyor body i stream ederken. Bunun faydası şu, örneğin diskinizdeki bir dosyayı belleğe taşımaya gerek kalmadan doğrudan yollayabilmeniz. Aa eğer karşı taraf yani Api sizden Content-Length i de yollamanızı isterse request ile, bu durumda refit bu header ı set edemiyor request e. Bunun olmasını istemiyorsanız, Content-Length in ekleneceği ve buffering in kullanılacağı şekilde body i göndermeniz mümkün. Bunun için sadece buffered: true demeniz yeterli.
[Body(buffered: true)] User user
Xml Content
Refit in default olarak Json content serialization kullanacağını söylemiştik. XML gönderip almak için ne yapabliriz peki. Yuukarıda bahsettiğimiz gibi ilgili interface in ayarlarını yaparken bunu belirtebiliriz.
var gitHubApi = RestService.For<IXmlApi>("https://www.w3.org/XML", new RefitSettings { ContentSerializer = new XmlContentSerializer() });
Refit xml serialization ve deserialization işlemleri için System.Xml.Serialization.XmlSerializer api ını kullanır bilginiz olsun.
Sadece bu kadarında görebileceğimiz üzere, refit ile bir projenin haberleşeceği tüm apı ları istediğimiz kadar parçalayıp istediğimiz kadar interface olarak tanımlayabiliyoruz. Bunları parçalamak şu işe yarıyor;
- Farklı interfaceleri farklı özelliklerle çalışacak şekilde ayarlayabiliyoruz. Örneğin XML ve Json Serialization ı işlemleri gibi.
- Bu parçaladığımız interfaceleri projemizde kullanırken diğer kütüphaneler ile birleştirme kısmında da faydasını çok görüyor olacağız. Örneğin Bir Interface için Polly kullanıp bir başkası için devreye Cache mekanizması sokmak gibi.
Dışarıdan bir developer gelip projenizi açtığında da, sadece interface dosyasına bakarak tüm Api haberleşmesinin ne şekilde olacağını görmesi de mümkün oluyor böylece.
Refit detaylarına bir sonraki blog postta devam edeceğim.
Görüşmek üzere.

Microsoft Xamarin Istanbul Development Meetup “Xamarin Resilient Network Services Bölüm 1”
Herkese Selamlar, biraz geç olsa da 25 ekim günü Microsoft Türkiye‘de yaptığımız etkinlikteki konuyla ilgili yazma fırsatı ancak bulabiliyorum. Güzel bir etkinlik günü geçirdik, 3 farklı değerli arkadaşımla beraber aşağıdaki konulara değinmiştik.
• .Net Core ile Dependency Injection (Özgür Kaplan)
• Xamarin ile Dependency Injection (Yiğit Özaksüt)
• Xamarin ile Resilient Network Services (bunu ben anlattım ve burada da uzunca değineceğim detaylarına.)
• C# hakkında doğru bilinen yanlışlar ve performans ipuçları (Cihan Yakar)
Benim anlattığım Xamarin ile Resilient Network Services sunumum ile ilgili proje ve dosyaları buradan inceleyebilirsiniz.
Katılan dinleyici ve konuşmacı tüm arkadaşlara teşekkür ederek kendi konumun detaylarına giriş yapıyorum.
Bu konu ile ilgili aslında yazmak istediğim birkaç blog serisi var. Aslında genel bakış açısını burada belirtip detaylarına diğer blog postlarımda değineceğim.
İlk olarak şu Resilient(Esnek) kelimesinden başlayalım. Ne demek bir yapının esnek olması. Bu aslında şu demek;
- Kurduğumuz yapı, belli hatalar karşısında nasıl davranacığını önceden bilen, öncelikleri belli ve değiştirilebilir esnek kırılmaz bir yapı olmalı.
Bu yapı, diğer tüm yazılım projelerinde, uygulamayı mümkün olduğunca sağlıklı bir şekilde ayakta tutmak için kullanılan mimari bölümlerin(Ölçeklenebilir olması, esnek olması, hızlı olması, güvenilir olması vs vs..) küçük bir parçası aslında. Bu sistemin ben yazılım projelerinin olmaz ise olması Networking üzerinde anlattım, çünkü danışmanlıklarımda gördüğüm ve eğitimler de hissettiğim en büyük açık burada mevcut idi.
using (var client = new HttpClient()) { client.BaseAddress = new Uri("https://randomuser.me/api/"); var content = await client.GetStringAsync("?results=10&page=1"); var result = JsonConvert.DeserializeObject<ResponseModel>(content); }
Yıkarıda ki kod bloğunu bir bakalım. Birçok projede bir API haberleşmesi için gördüğüm kod bu kadar da kalıyor. Daha üzerine düşünülmüş olanlarda ise bu şekilde bir kullanım generic bir class içerisine yerleştirilmiş ve onun içerisinden haberleşme çağırılıyor oluyor. Fakat içeriği pek de değişmiyor. Peki bu koddaki yanlış nedir? Yada var mıdır?
Benim gördüğüm kadarı ile şöyle;
Koda ilk bakışta bir yanlış gözükmüyor olabilir. Evet derlenir ve çalışır da, hatta herşey iyi giderse response u alıp gerekli çevirme işlemlerini de yapar. Ama işte bu kod bloğunda birçok şey yolunda gider varsayılmış. Http client objesinin static tanımlanmamış olması ve bunun sıkıntılarına başka zaman değineceğim bunu şimdilik göz ardı edelim diğer konulara bakalım.
- Internette bir sorun olduğunda bu request ne olacak
- Server dan 500 aldığında request im ne yapmalı, 503 olduğunda ne olmalı.
- Time out yersem ne yapmalıyım? yeniden göndermeli miyim requesti? Eğer gönderecek isem kaç kere göndermeliyim, ne kadar ara vermeliyim?
- Response cachlenebilir bir response mu eğer öyle ise bunu da devreye koysam güzel olmaz mı?
- Bu request uygulama içerisinde gidecek olan diğer requestlere göre bir farkı önceliği var mı?
gibi gibi sorular aklıma geliyor. İşte tüm bu konular üzerinden bir xamarin projesinde kullanmamız ve güzel bir networking altyapısı kurmanız için size 5 farklı ve değerli kütüphaneden bahsedeceğim. Ve bunların hepbirden nasıl uyumlu olarak kullanabiliriz buna değineceğim.
Kütpühaneleri şunlar;
- Refit: The automatic type-safe REST library for .NET Core, Xamarin and .Net
- Fusillade: An opinionated Http library for mobile development
- ModernHttpClient: Platform spesific networking libraries to Xamarin applications
- Akavache: An Asynchronous Key-Value Store for Native Applications
- Polly: .Net resilience and transient-fault-handling library
kısaca açıklamlarını direk kendi github adreslerindeki gibi yazdım kenarlarına. Detaylarına ve örneklerine sırası ile diğer postlarımda başlayacağım. Hepsinden bahsettikten sonra da bir arada bir xamarin projesinde nasıl kullanılırız buna bakacağız.
Bir sonraki yazımda görüşmek üzere.

Asp.Net Core 2.0’a Giriş – Bölüm 4 (Startup.cs, Configure, Middleware hakkında)
Selamlar, Asp.Net Core 2.0 Giriş seri postlarından bu bölümde Startup.cs deki Configure metodundan ve Middleware yapısından bahsedeğim. Middleware yapısı çok Asp.Net Core için çok önemli bir değişiklik ve bunun detaylarına ilerleyen postlarda gireceğiz.
Startup.cs deki Configure metodunun yaptığı iş şudur; Asp.Net Core projenizdeki Http Request pipeline nını konfigüre etmek. Peki ne demek bu Pipeline ı konfigüre etmek bunu aşağıdaki resimle bir inceleyelim. Klasik Asp.Net projelerinde Request in izlediği yol çok uzun ve karmaşıkça idi.Sizin belkide hiç kullanmayacağınız birçok yapıdan request geçiyor ve sizin elinize o şekilde ulaşıyordu. Ama aşağıdaki resimde göreceğiniz gibi, Asp.Net Core pipeline nına siz ne eklerseniz middleware olarak sadece o yapılardan geçecek request. Hiç bir middleware eklenmediğinde hiç birşey olmayacak. Bir önceki örneğimizdeki tek bir middleware eklenirse sadece o middleware den geçecek.
Asp.Net Core projelerinde resimde görebileceğiniz gibi, koca bir MVC framework pipeline ı bile ayrıca projeye middleware olarak ekleniyor eğer istenirse. Size kalmış, MVC framework ün imkanlarından faydalanacak olanlar pipeline a bunu ekleyebilir, Bunun öncesinde kendi yazdıkları yada built-in gelen middleware lerden Authentication middleware ini koyup, static file ları serve etmek için (css,javascript,images vs..) Static Files Middleware ini pipeline a ekleyebilirler.
HttpRequest, eklediğiniz sırada middleware lerden geçerek, son middleware den sonra aynı şeklide geri dönerek tüm middleware lerden geçip browser a ulaşır. Bu requestin middleware lerdeki akışı sırasında WebServer feature larına ulaşma imkanı veriyor Asp.Net Core uygulamamız. Bir de daha büyük resimde request in browserdan uygulamamıza gelip tekrar browser a geri döndüğü akışa bakalım.Bu resmi adım adım izleyecek olursak, sırası ile şunlar oluyor.
- Request browserdan External Web Server a (bu resimde IIS kullanıldığı varsayıldı, ama bu Nginx, Apache olabilir.) ulaşır.
- External Web Server (IIS) dotnet runtime çalıştırır.
- dotnet runtime, CLR ı load edip, sizin Application nınızdaki entry point (Program.cs deki Main() matodu ) i arar ve çalıştırır.
- dotnet runtime Application ı çalıştırdıktan sonra Internal Web Server olan Kestrel ayağa kalkar.(Kestrel olmak zorunda değil, ama internal bir web server için çok çok iyi bir seçenek çünkü hem cross platform bir web server, hem performansı çok iyi).
- Main metodunuz çalıştıktan sonra, WebHostBuilder ilgili ayarlar ile ayağa kalkar, Startup dosyası ile beraber uygulamanız konfigüre edildikten sonra, HttpRequest, IIS den Kestret’e yönlendirilir.
- Startup dosyasındaki eklenen middleware lerinizden geçen request tekrar Kestrel e ulaşır, Kestrel, External Web Server a response u iletir ve sonunda response browser a gelir.
Bu eski System.Web yaklaşımından çok daha verimli ve çok daha resource friendly dir. Klasik Asp.Net projelerinin Windows bağımlı olma sebebi zaten System.Web idi. System.Web assemly IIS e bağlı, IIS de, Windows a bağlıydı. Bu yüzden asp.Net projelerimiz windows dışında işletim sistemlerinde çalışamıyordu.
Şunu da belirtmekte fayda var. External bir web server a mutlaka ihtiyacınız yok aslında. Ama Kestrel çok performanslı ve cross platform bir web server olsada, IIS,Nginx, Apache gibi tam donanımlı web serverlar kadar yetenekli değil, bu yüzden zorunlu olmamakla beraber yüksek ihtimalle bir external web server kullanıyorsunuz. 🙂
Peki özet teorik bilgiden sonra uygulamamıza geri dönelim. ikinci bir middleware ekleyip, requestin oradan geçişlerini loglayarak, middleware yapısının çalışma şeklini iyice anlamaya çalışalım.
Startup.cs dosyasında ki Configure metodunda aşağıdaki değişiklikleri yapalım.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger logger) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.Use(async (context, next) => { logger.LogInformation("First middleware working BEFORE second middleware"); var serviceProvider = context.RequestServices; var userService = serviceProvider.GetRequiredService(); var users = await userService.GetUsers(); await context.Response.WriteAsync($"Hello World! - UserCount: {users.Count}"); await next(); logger.LogInformation("First middleware working AFTER second middleware"); }); app.Run(async (context) => { logger.LogInformation("second middleware working"); await context.Response.WriteAsync("Second middleware done!"); }); }
Kod tarafında yaptığımız değişiklikler şunlar;
- Configure metoduna ILogger<Startup> objesini inject ediyoruz. Bu daha Default WebHostBuilder konfigüre edilirken oluşturulup uygulamamıza bizim için register edilmiş oluyor Asp.Net Core tarafından
- İlk middleware deki app.Run metodunu app.Use olarak değiştiriyoruz. Çünkü Run metodu gelen request i shortcut yapıp sonraki middleware lere iletmeden geri döner. Use metodunda ise, context dışında ikinci bir parametre olarak RequestDelegate objesi gelir(bir sonraki middleware delegesi olarak). buna next adını verdik, tüm işlemlerimiz bittikten sonra bu delegate i await ediyoruz. Tam bu noktada ikinci middeware miz çalışmaya başlar.
- Logları koyduğumuz yerlere dikkat ediniz. Biliyoruz ki middleware ler, uygulamaya eklendiği sırada request leri karşılar, tam tersi sırada da response ları karşılarlar. Projeyi çalıştırdığımızda logların nasıl bir sırayla basılacağına bakalım.
Bu sefer External web server kullanmadan sadece Kestrel i yani Internal Web Server ı kullanarak bu işlemi yapalım. Bunun için Visual Studio da, Run butonun yanındaki oku tıklayıp, IISExpress yerine projenin adının olduğu seçeneği seçmemiz yeterli (örneğimizde MeetupAspNetCore). Bu IIS Express i değil doğrudan Kestrel üzerinden requestleri karşılamamızı ve browser a dönmemizi sağlayacak. Uygulamayı çalıştırıp çıktımıza bakalım.
resimde de göreceğiniz gibi, önce birinci middleware çalıştı, await next() dediğimiz anca ikinci middleware in logu basıldı, ikinci middleware de işini tamamladıkan sonra response geri dönerken,birinci middleware de await next(), dediğimiz yerden sonraki kod bloğunda yazılan log çalışmış oldu.
Bu yazımızı da burada tamamlayabiliriz. Middleware lerden kısaca bahsetmiş olduk, bir AspNet Core projesine gelen request in tüm akışını inceledik ve middleware lerin çalışma şeklini örnekledik.
Asp.Net Core 2.0 yazı serimizde artık temel noktaları gördük diyebiliriz. Ama henüz Introduction ı tamamen yaptık dememiz için çok şey var 🙂
Bir sonraki yazıda görüşmek üzere.

Asp.Net Core 2.0’a Giriş – Bölüm 3 (Startup.cs, Built-In Dependency Injection hakkında)
Selamlar, Asp.Net Core serimize kaldığımız yerden devam ediyoruz. Önceki iki yazımızda, projemizi oluşturduk, solution daki tüm dosyaları inceledik, projenin nasıl ayağa kalktığına değinmeye çalıştık. WebHostBuilder objemizin Run() metoduyla artık Web application a dönüşen projemiz, tam olarak hayatına başlamadan önce son ve önemli ayarlamalarını yapacağımız kısım Startup.cs dosyası olacak. Buna başka bir isim de verebilirdik tabii isme çok takılmayın, ama default gelen ve genelde göreceğiniz ismi ile Startup.cs, DI container ını ve Middleware leri ayarlayacağımız önemli bir dosya olduğunu bilelim 🙂
Serinin başında oluşturduğumuz ve halen boş haliyle duran projemizde ki bu dosyanın içeriğine baktığımızda aşağıdaki gibi 2 temel metod görüyoruz.
public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } }
Şimdi bu iki metodu detaylıca inceleyelim.
Configure Services
Bu metod .Net Core ile birlikte gelen, built-in Dependency Injection Container ının ayarlarını yapacağımız kısım olacak. DI klasik Asp.net tarafında optional iken, artık bilmemiz gereken bir durum haline geliyor. Çok kısaca özetlemek gerekirse Dependency Injection için şunu diyebiliriz:
“Uygulama içerisinde ben senden bu (ICriticalService) tipte bir şey istediğimde bana şu (MainCriticalService) tipi ver, bir de şu (MainCriticalService) tipin ne kadar süre hayatta kalacağını ben sana en başında söyleyeyim gerisini sen yönet.”
Birde aşağıdaki resim üzerinden bakalım. Dependency Injection Pattern i, genelde IoC denen (Inversion Of Control) Containerları kullanılarak yapılır. Container’ı uygulama start olduktan sonra ilk iş olarak initialize edilir, ilgili service registration larını yapar uygulama içerisinde bu tipler çağırıldığı zaman gerçek sınıfları Container ın bize verilmesi beklenir. Bu yazının temel amacı DI ı anlatmak olmadığından fazlaca buraya takılmadan Asp.Net Core tarafında bu işler nasıl dönüyor, Farklı Lifetime tipleri neler onlara bakalım.
(Sadece DI konusu tek başına çok derin, en son şunu yazmak isterim güzel bir örnektir.Dependency Injection 5 yaşındaki bir çocuğa nasıl anlatılır? Cevap şuna benzer: Eğer 5 yaşındaki çocuk acıktığı zaman, doğrudan buzdolabına gidip istediği şeyi almaya kalkarsa, yanlış şeyi alabilir, şeklinden, görüntüsünden farklı şey zannedip yanlış şeyleri alabilir, tarihi geçmiş bir içeceği-yiyeceği alabilir, ortalığı batırabilir 🙂 Bunun için aslında yapması gereken, acıktığında istediği şeyi ailesine söylemesi, ailesinin de onun istediği şeyi ona vermesidir. Burada tahmin edeceğiniz üzere çocuk yazılan app, aile IoC Container dır.)
Resimde gördüğümüz gibi bir taraftan genel olarak Interface i yolluyoruz, çağırdığımızda diğer taraftan bize isteiğimiz sınıf geliyor. En başında da bu istediğimiz söylüyoruz tabii. Asp.Net Core tarafında 3 tipte lifetime option var. bunlar
- Transient: Bu yaşam tipi ile bir objeyi register ettiğimiz zaman, Container dan bu obje her istendiğinde yenisi verilir. Her defasında dolaptan yeni bir içecek almak gibi düşünelim.
- Scoped: Bu yaşam tipi ile bir objeyi register ettiğimizde, Container bize ilgili Request sonlana kadar aynı objeyi verir, yeni bir request geldiğinde yeni bir obje oluşturulur. Tek bir öğünde 5 yaşında ki çocuğumuz aynı şey isteyip durursa, dolaptan yenisini almak yerine önünde yarım kalmışı ona geri itelediğimizi düşünebiliriz 🙂
- Singleton: Bu yaşam tipi ile bir obje register ettiğimizde ise, Container dan obje istenildiğinde sadece ilk sefer için oluşturulur, sonrasında da her zaman aynı obje verilir. Dolap örneğine dönersek, çocuğumuz her susadığında ve bizden su istediğinde ona hep aynı bardaktan vermek gibi düşünebiliriz (Bu biraz zorlama oldu sanki 🙂 )
Lifetime ların önemi büyük, özellikle birbirini çağıran metodlar olduğunu düşünürsek (ki bu durum bir uygulama için gayet normal bir durum), singleton register edilmiş bir metot içerisinde scoped bir servis çağırdığımızda çarşı karışacaktır, yaşam döngüleri birbirini etkileyemeye başlayacaktır. Bunlarla ilgili daha derinlemesine detayları başka bir yazıda değineceğim.
Peki bu durum bize ne sağladı?
- Objeler arasındaki sıkı bağlar ortadan kalkmış oldu. Dolayısı ile Unit Testing de kolaylaşmış oldu
- Objelerin lifetime larını yönetme zorluluğu ortadan kalmış oldu (Ama registraion lara, ve birlikte kullanımlara dikkat ederek :))
Şimdi kod tarafına girelim. Boş olan projemize, bir NetStandard kütüphanesi ekleyelim, ve oraya uygulamanın kullanacağı modelleri ve uygulama servislerini (Application Service) lerimizi koyalım. Örneğimiz basit olsun, Sitemizin kullanıcılarını listeleyelim ve onların sitemizdeki rollerini görelim. O halde Solution a bir adet NetStandard2.0 projesi ekliyorum ve içini aşağıdaki gibi dolduruyorum.
Solution ın son hali yukarıdaki resimde göründüğü gibi oldu. Senaryonun son derece basit, User ve Role ler var. bir de bunları bize dönen Application Servicelerimiz var. kodlar şu şekilde;
public class Users { public Users() { UserRoles = new List(); } public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public IEnumerable UserRoles { get; set; } } public class Roles { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } } public interface IUserService { Task GetUsers(); Task GetRoles(); } public class UserServiceInMemory : IUserService { public UserServiceInMemory() { RoleList = new List { new Roles { Id = 1, Name = "Admin", Description = "Admin" }, new Roles { Id = 2, Name = "Editor", Description = "Content Editor" } }; UserList = new List { new Users { Age = 31, Id = 1, Name = "Erhan Ballıeker", UserRoles = RoleList}, new Users { Age = 30, Id = 2, Name = "Meltem Ballıeker", UserRoles = RoleList.Take(1) } }; } public List UserList { get; set; } public List RoleList { get; set; } public Task<List<Roles>> GetRoles() { return Task.Run(() => RoleList); } public Task<List<Users>> GetUsers() { return Task.Run(() => UserList); } }
Peki hızlıca özetleyelim. 2 adet basit class var User ve Role class ları. bir interface miz var. Gerçek hayat senaryosunda bu UserInterface inin neler yapmasını istiyorsak ilgili metodları buraya koyuyoruz. Sonra sadece test amaçlı kullanacağımız bu interface den türeyen In-Memory çalışacak bir UserServiceInMemory class ı oluşturuyoruz. Şimdi gelelim bunu Asp.Net Core tarafındaki Container a anlatmaya.
Startup.cs dosyasındaki ConfigureServices metodunun bu Container ı konfigüre etmek amaçlı olduğunu söylemiştik. Şimdi registration işlemimizi yapalım. Core projesine NetStandard projesini referans olarak gösterdikten sonra ConfigureServices metoduna aşağıdaki kodu ekliyoruz.
services.AddSingleton<IUserService, UserServiceInMemory>();
Burada dediğimizşey şu: Ben ne zaman senden bir IUserService istersem, bana UserServiceInMemory getir, ve bu service singleton (yani uygulama boyunca 1 kere oluşturulsun ve sonra hep aynı obje kullanılsın) olsun.
Peki bu register ettiğmiz service i nasıl kullanacağız? bu blog post içerisinde Configure metoduna ve dolayısı ile Middleware yapısına çok girmeyeceğim, fakat şu şekilde test edelim. Configure metodu içerisindeki app.Run(…) metodu ile uygulamamıza tek bir middleware eklenmiş. Yani request uygulamamıza geliyor, response a “Hello World!” yazıp geri dönüyoruz. Çok işe yarar bir uygulama değil gibi 🙂 Şimdilik, daha projemize hiçbir Controller vs eklememişken, bu service’i , bu tek middleware içerisinde test edelim. Asp.Net Core projelerinde, register edilen service’leri çağırmanın birkaç yolu var. Bunlardan biri request Context i içerisinden bu service’i almak. Bunun da detayına sonra gireceğim, ama şimdilik sadece görmüş olalım ve service imizi web uygulamamız içerisinde kullanmış olalım amacı ile, tek middleware i aşağıdaki gibi düzenleyebiliriz.
app.Run(async (context) => { var serviceProvider = context.RequestServices; var userService = serviceProvider.GetRequiredService<IUserService>(); var users = await userService.GetUsers(); await context.Response.WriteAsync($"Hello World! - UserCount: {users.Count}"); }); }
Uygulamamızı çalıştırdığımızda browserdaki çıktının aşağıdaki gibi olduğunu görüyoruz.
Evet! servisimiz Container dan istediğimiz gibi bize ulaştı, ilgili metodu çağırıp eklediğimiz userları service den aldık. Ekrana da Count u basmış olduk. Bu yazıda çok şey yapık aslında, WebHostBuilder oluştuktan sonra, Startup dosyamızı kullan diyerek, önce ConfigureServices metodu ile DI a biraz değinip, Asp.Net Core projesi içerisinde nasıl konfigüre ediyoruz onu görmüş olduk, ve tek bir middleware üzerinde register ettiğimiz servisimizi alıp kullanabildik.
Bir sonraki yazımda, yine startup dosyasındaki ikinci bir önemli metod olan Configure ile beraber middleware yapısına değineceğim.
Görüşmek üzere.