Erhan Ballıeker

Resilient Network Services Bölüm 7 – Xamarin Projemizde Refit, Polly, Akavache, Fusillade ve ModernHttpClient Birlikte Kullanımı Bölüm 3

Selamlar,

Bu serinin son yazısına nihayet gelmiş olduğumuzu düşünüyorum 🙂 Bu seri boyunca çok faydalı 5 farklı kütüphane ve bunların nasıl kullanılacağını ayrı ayrı görmüştük. En son bir xamarin projemizde (ki xamarin olması şart değil herhangi bir .net platformunda da olabilir.) bunları nasıl kullanırız buna bakmaya başlamıştık.

En son yazımda, oluşturduğumuz ApiRequest sınıfı içerisinde refit, fusillade ve modernhttpclient ı kullanabilir olmuştul. Refit interface imizi oluşturmuştuk. Yani aslında api haberleşmesi yapmamız için ihtiyacımız olan tüm temel kısımları yazmıştık. Şimdi uygulama tarafında kullanılacak olan ve genelde BusinessLayer, Application Services vb isimlerle göreceğimiz bir servis katmanı ekleyelim. Bu katmana da haberleşmeyi uygulama bazında ne şekilde yapacağız buna bakalım.

Buna başlamadan önce, son olarak yardım bir sınıf daha oluşturmak istiyorum. Önyüzden gelen önceliklere göre (Fusillade öncelikleri – yani request in olmasını istediğimiz öncelikleri) arka planda ApiRequest ten bize doğru property yi dönecek ayrı bir sınıf yazalım.

projemize ApiRequestSelector adında yapacağı iş aslında basit olan bir sınıf ekleyelim.

içeriği aşağıdaki gibi olacak. Bu generic sınıfımız dışarıdan kendisine verdiğimiz refit interface ini ve istediğimiz önceliği alarak, ApiRequest sınıfında tanımladığımız ilgili Lazy sınıfı bize dönecek sadece.


public class ApiRequestSelector<T>
    {
        private readonly PriorityType _priority;
        private readonly IApiRequest<T> _apiRequest;

        public ApiRequestSelector(IApiRequest<T> apiEndpoint, PriorityType priority)
        {
            _priority = priority;
            _apiRequest = apiEndpoint;
        }

        public T GetApiRequestByPriority()
        {
            T apiRequest;

            switch (_priority)
            {
                case PriorityType.Speculative:
                    apiRequest = _apiRequest.Speculative;
                    break;
                case PriorityType.UserInitiated:
                    apiRequest = _apiRequest.UserInitiated;
                    break;
                case PriorityType.Background:
                    apiRequest = _apiRequest.Background;
                    break;
                default:
                    apiRequest = _apiRequest.UserInitiated;
                    break;
            }

            return apiRequest;
        }
    }

Bu yadımcı sınıfımızı da yazdıktan sonra gelelim UserService kısmımızı yazmaya. Bunun için işe öncelikle service kısmında yapılacak işlemleri bir interface içerisinde toplarlayalım. IUserService isminde bir interface oluşturuyoruz. İçerisine sadece geriye Task içerisinde UserResponseModel model dönen ve adı GetUserList olan bir metod ekliyoruz. Bu metod bizden sadece Priority alacak, ve kaç user istediğimiz bilgisi ile sayfa numarası alacak.

  public interface IUserService
    {
        Task<UserResponseModel> GetUserList(PriorityType priorityType, int results, int page);
    }

Interface imizi tanımladıktan sonra bunun concrete class ını yani UserService sınıfını yazalım. UserService, IUserService interface inden türüyor yani yukarıdaki metodu implemente edip bir UserResponseModel dönmeliyiz. Ama bu işlemleri yaparken bu servis katmanımıza cache mekanizmamızı ve servisi çağırırken kullanmak istediğimiz Policy lerimizide ekleyelim. Service dışarıdan bir IApiRequest interface i alıyor. Bu interface önceki yazımda belirttiğim gibi fusillade ve modernhttpclient ın messagehandler ları ile oluşturulmuş bir refit HttpClient objesi dönüyor. Bu constructor parametresine IRandomUserEndpoint ini generic parametre olarak kullanacağım IApiRequest i geçmem yeterli.

Sonrasında Cache mekanizması için bana yardımı olacak bir metod yazıyorum. DeleteCache<T> metodu. Bu metod verdiğimiz generic parametre tipinde ne kadar obje var ise cache de yani akavache ile oluşturmuş olduğum sqlite da, hepsini uçuracak.

Sonrasında ise interface  imden gelen GetUserList metodunu implemente etmeye başlıyorum.

 public class UserService : IUserService
    {
        private readonly IApiRequest<IRandomUserEndpoint> _userEnpoint;

        public UserService(IApiRequest<IRandomUserEndpoint> userEndpoint)
        {
            _userEnpoint = userEndpoint;
        }

        public async Task<Unit> DeleteCache<T>() where T : new()
        {
            var result = await BlobCache.LocalMachine.InvalidateAllObjects<T>();
            return result;
        }

        public async Task<UserResponseModel> GetUserList(PriorityType priorityType, int results, int page)
        {
            var cacheKey = $"userRespo321213nse_{page}";
            var cachedCorporateList = BlobCache.LocalMachine.GetOrFetchObject(cacheKey, async () => await GetUserListAsync(priorityType, results, page), DateTimeOffset.Now.AddDays(7));
            UserResponseModel userResponseList = await cachedCorporateList.FirstOrDefaultAsync();

            if (userResponseList == null)
            {
                await BlobCache.LocalMachine.InvalidateObject<UserResponseModel>(cacheKey);
            }

            return userResponseList;
        }

        private async Task<UserResponseModel> GetUserListAsync(PriorityType priorityType, int results, int page)
        {
            UserResponseModel userResponseList = null;

            var apiRequestelector = new ApiRequestSelector<IRandomUserEndpoint>(_userEnpoint, priorityType);

            //Policy.Timeout(20);
            //Policy.Bulkhead(10, 10);
            userResponseList = await Policy
                                    .Handle<ApiException>(exception => exception.StatusCode == HttpStatusCode.ServiceUnavailable)
                                    //.CircuitBreakerAsync(2,TimeSpan.FromSeconds(5))
                                    //.AdvancedCircuitBreaker(failureThreshold: 0.5,
                                    //                        samplingDuration: TimeSpan.FromSeconds(10),
                                    //                        minimumThroughput: 1,
                                    //                        durationOfBreak: TimeSpan.FromSeconds(30))
                                    //.FallbackAsync((t) => Task.FromResult(new UserResponseModel()))
                                    .WaitAndRetryAsync(5, sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
                                    .ExecuteAsync(async () => await apiRequestelector.GetApiRequestByPriority()?.GetUserList(results, page));

            return userResponseList;
        }
    }

Akavache nin Reqctive extension ile async-await mekanizmasını desteklediğiniz söylemiştik. Bu GetUserList metodunu implemente ederken, akavache nin, önceden bahsetmiş oduğumuz extension metodlarından biri olan GetOrFetchObject metodunu kullanıyorum. Bu metod benden 3 parametre alıyor ve şunları yapıyor.

  • verdiğim cache key parametresi ile, ilgili tipteki objeyi bu key ile db de arıyor. eğer bulursa, direkt olarak cache den bunu dönüyor, eğer bulamaz ise benden bir function istiyor ve bunu çalıştırıyor. bunun sonucunda aldığı result ı geri dönüp aynı zamanda da cache e yani db sine ekliyor.

Bu örnekte function delegate olarak GetUserListAsync adında aşağıda yazdığımız bir metodu gösterdik. Bu metod yine bir priortiy type alıyor,  ve aslında cache de data bulamadığımız durumda gerçekten api a request atacak olan metodumuz bu olduğu için, request i atmak için de ihtiyacımız olan page ve result parametlerini alıyor. Bu metod içerisinde hemen ilk olarak response tipimizi oluşturduktan sonra yaptığımız şey, yukarıda yazdığımız yardım metodu olan ApiRequestSelector sınıfımızı kullanarak hangi öncelikte giedecek olan request oluşturmak istediğimizi belirleyeceğimiz ve bize ilgili ApiRequest objesini dönecek olan metodu çağırıyoruz. Sonrasında iş tamamen Polly e kalıyor. Polly den elimden geldiğince uzunda bahsetmeye çalışmıştım ama bloglarda yazmakla bitmez yetenekleri. .burada sadece bu request i atmadan önce uygulamamızda istediğimiz senaryolara göre hangi Polly Policy lerini  kullanmak istediğimizi belirtip, en son ExecuteAsync içerisinde de, refit in bize vermiş olduğu GetUserList metodunu çağırıyoruzBu kısma biraz daha detaylı değinmek gerekirse;

  • var apiRequestelector = new ApiRequestSelector<IRandomUserEndpoint>(_userEnpoint, priorityType);
    • Bu satırda bize ApiRequest in önyüzden yolladığımız priorty ye göre karşılığı olan property si dönecek. HAtırlarsanız Lazy olarak tanımladığımız propertyler. Buradan bakarak hatırlayabilirsiniz.
  • userResponseList = await Policy .Handle<ApiException>(exception => exception.StatusCode == HttpStatusCode.ServiceUnavailable) //.CircuitBreakerAsync(2,TimeSpan.FromSeconds(5)) //.AdvancedCircuitBreaker(failureThreshold: 0.5, // samplingDuration: TimeSpan.FromSeconds(10), // minimumThroughput: 1, // durationOfBreak: TimeSpan.FromSeconds(30)) //.FallbackAsync((t) => Task.FromResult(new UserResponseModel())) .WaitAndRetryAsync(5, sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))) .ExecuteAsync(async () => await apiRequestelector.GetApiRequestByPriority()?.GetUserList(results, page));
    • Burada ise birçok şey dönüyor. Polly nin;
      • ResultHandler
      • ErrorHandler
      • CircuitBreaker-AdvanceCİrcuitBreaker
      • WaitAndRetryAsync
      • Policy Execution ,
    • başıkları ile bahsettiğimiz kısımlarını görebilirsiniz. Detaylarına buradan ve buradan  bakabilirsiniz.

Peki bu kadar yazdıktan sonra gelelim artık önyüze. Birde araya mvvm kısmını katmıyorum, doğrudan MainPage.xaml sayfamızın constructor unda bu işlemleri yapmaya başlıyorum 🙂 (Normalde xaml sayfaların .cs tarafında mümkün olduğu kadarı ile kod görmemek, sadece InitializComponent görmek isteriz, diğer herşeyi herbir sayfanın kendi ViewModel lerinde görmek isteriz, ama bu ayrı bir konu ve zaten mvvm de uyulanması zorunluk bşr pattern değil xamarin projeleriniz için.)

aşağıda constructor metodunda yazdığımız kod bu şekilde.


Task.Run(async () =>
            {
                var service = new UserService(new ApiRequest<IRandomUserEndpoint>());
                var result = await service.GetUserList(PriorityType.UserInitiated , 10, 1);

                Device.BeginInvokeOnMainThread(() =>
                {
                    UserList = new ObservableCollection();
                    if (result != null && result.results.Count > 0)
                    {
                        foreach (var item in result.results)
                        {
                            UserList.Add(item);
                        }
                    }
                    lstView.ItemsSource = UserList;
                });
            });
        }

Aslında kaç yazıdır yazdığımız herşeyi kullanmaya kalktığımız da kapladıkalrı satır sayısı sadece 2 🙂

  • var service = new UserService(new ApiRequest<IRandomUserEndpoint>());
    • (evet hardcoded initialization var, bir IoC container bağlayabiliriz ama kalsın şimdilik – o zaman o kadar interface i niye yazdın demeyin buraya odaklanın 🙂 .. ) UserService objemizi ve bizden istediği ApiRequest sınıfımızı kullanacak olduğumuz refit interfaceini generic parametre göstererek veriyoruz.
  • var result = await service.GetUserList(PriorityType.Speculative, 10, 1);
    • sonrasında ise sadece refit in bize yaşayan bir metod olarak sunduğu bizim interface de tanımladığımız GetUserList metodunu UserInitiated önceliği ile çağırıyoruz. 

Biz bu iki satırı yazarken arkada dönen devran ile ilgili artık ciddi anlamda bir fikrimiz var, ve ilk postta dediğim gibi, sadece bir using bloğu içerisinde bir httpclient oluşturup, url i verip GetStringAsync vb.. metodlar ile bu işleri yapmak ile aramızda dağlar ovalar var artık. Gerçek anlamda Resilient bir Network katmanız var diyebiliriz. Daha gelişememiz mi, tabii ki gelişir sonu yok, hatası var mıdır illa ki vardır bunun da sonu yok :). ilerlyen günlerde, bu yapının HttpClient ile değilde Asp.Net Core 2.1 ile gelen HttpClientFactory ile nasıl yaparız detaylıca bahsetmek istiyorum.

Umarım faydalı olmuştur.

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

Resilient Network Services Bölüm 7 – Xamarin Projemizde Refit, Polly, Akavache, Fusillade ve ModernHttpClient Birlikte Kullanımı

Selamlar,

Bu seri boyunca bahsettiğim bu güzel kütüphaneleri artık iş üzerinde görme vakti geldi 🙂 Tüm bu kütüphanelerin bir xamarin projesinde Resilient Network services oluşturulması için nasıl bir arada kullanıcağımızı göreceğimiz bu yazımızda, kütüphanelerin detaylarını görmek için önceki yazıları da okumanızı öneririm. Buradan başlayabilirsiniz.

Refit, Fusillade, ModernHttpClient, Akavache ve Polly  kütüphanelerinin herbirinin birbirinden farklı ve aynı zamanda aynı işlevi gören özellikleri mevcut.
Öncelikle hangi kütüphaneden hangi özellikleri alıp nasıl bir yapı kuracağımızı ve bunu xamarin projelerinde nasıl kullanacağımıza bakacağız. Sırası ile kütüphanelerden alacağımız özellikler şunla olacak.

  1. Refit: Tüm Api haberleşmesini, tek bir interface altında toplayıp, HttpClient ve HttpMessageHandler yönetimini runtime da bu arkadaşa bırakacağız. Herhangi bir yerde biz HttpClient sınıfı ile ilgilenmeyeceğiz.
  2. Akavache: Api dan gelen responlarımız için tüm objelerimizi Akavache ile sqlite3, lokal veritabanında saklayacağız. Request atmaya ne zaman gitmeli, cache de varsa önce oradan okuma işlemi yada cache de olsa bile cache den sonucu hemen dönüp, yine de arka planda Api ye gidip cache deki dataları yenileme gibi işlemleri akavache ile yapacağız.AKavache de bize performanslı ve kolay bir SQLite yönetimi sunacak
  3. Fusillade: Bu güzel kütüphaneyi requestleri önceliklendirme işlemlerimiz için ve Request Limit işlemi için kullanacağız. Kullanıcı tetiklemesiyle mi birşeyler oldu, bir arka plan request imi atıyoruz, yoksa speculative bir request atıp verdiğimiz sınır boyunca belli bir data mı çekiyoruz bunu söyleyeceğiz.
  4. ModerHttpClient: En basit kullanımı olan bu kütüphanenin amacı zaten belli, daha performanslı http haberleşmesi sağlamak. kendi ardaka kullandığı native framework ler ile, bize sunduğu NativeMessageHandler ile daha performanslı network haberleşmesi sağlayacak.
  5. Polly: Bunu kullanın kullanabildiğiniz kadar 🙂 Çok fazla özelliği olan bu kütüphane de genellikle kullanılacak özellikler, api hanerleşmesinde istediğimiz senaryolara bağlı olduğundan, biz default olarak, Transient yani anlık hatalar için Retry pattern ini requestlerimizde uygulamak için kullanacağız.
    Daha da detaylarını eski yazılarımdan ve kütüphanelerin kendi github repo larından edinebilirsiniz.

Örnek olması açısından public api lardan biri randomuser.me  kullanalım. adrese gittiğinizde api ın neler sağladığını bulabilirsiniz. Biz basit bir get request i ile işe başlayalım. REquest atacağımız adres ;

https://randomuser.me/api/?results=5

Bu adres bize results querys string ile verdiğimiz sayı kadar user geriye dönecektir. Bu user a bağlı birden çok property ve ayrı class lar var. Postman üzerinden yada direk browserdan bu adrese request attığınızda karşınıza çıkacak olan response şu şekilde olacaktır.

Capture.PNG

bu json ın c# tipinde nasıl bir class a karşılık geldiğini anlamak ve yazmak için uzun uzun bakmak yerine sıkça kullandığım jsonutils.com adresine json ı yapıştıralım ve bize bu json olarak aldığımız response C# ile oluşturulmuş class lar olarak versin.

jsonutils.com a gidip aşağıdaki gibi json text alanına json string imizi girdiğimizde istediğimiz isimle ana sınıfı oluşturup buna bağlı sınıflarıda kendisi oluşturuyor.

Capture.PNG

bu endpoint e attığımız request in C# cası jsonutils in bize söylediğine göre tam olarak aşağıdaki gibi;

   public class Name
    {
        public string title { get; set; }
        public string first { get; set; }
        public string last { get; set; }
    }      
 
    public class Coordinates
    {
        public string latitude { get; set; }
        public string longitude { get; set; }
    }

    public class Timezone
    {
        public string offset { get; set; }
        public string description { get; set; }
    }

    public class Location
    {
        public string street { get; set; }
        public string city { get; set; }
        public string state { get; set; }
        public object postcode { get; set; }
        public Coordinates coordinates { get; set; }
        public Timezone timezone { get; set; }
    }

    public class Login
    {
        public string uuid { get; set; }
        public string username { get; set; }
        public string password { get; set; }
        public string salt { get; set; }
        public string md5 { get; set; }
        public string sha1 { get; set; }
        public string sha256 { get; set; }
    }

    public class Dob
    {
        public DateTime date { get; set; }
        public int age { get; set; }
    }

    public class Registered
    {
        public DateTime date { get; set; }
        public int age { get; set; }
    }

    public class Id
    {
        public string name { get; set; }
        public string value { get; set; }
    }

    public class Picture
    {
        public string large { get; set; }
        public string medium { get; set; }
        public string thumbnail { get; set; }
    }

    public class Result
    {
        public string gender { get; set; }
        public Name name { get; set; }
        public Location location { get; set; }
        public string email { get; set; }
        public Login login { get; set; }
        public Dob dob { get; set; }
        public Registered registered { get; set; }
        public string phone { get; set; }
        public string cell { get; set; }
        public Id id { get; set; }
        public Picture picture { get; set; }
        public string nat { get; set; }
    }

    public class Info
    {
        public string seed { get; set; }
        public int results { get; set; }
        public int page { get; set; }
        public string version { get; set; }
    }

    public class User
    {
        public IList results { get; set; }
        public Info info { get; set; }
    }

Bu class ları projemizde Models klasörüne (yoksa bir tane Model adında bir klasör açarak) içerisine koyalım. Diğer bir önz hazırlığımız da Fusillade tarafında bize önyüzde ve ortak kod tarafında yardımcı olması için, PriorityType adında bir enum oluşturalım.

public enum PriorityType
{
   Background,

   Speculative,

   UserInitiated
}

Hazırlığımıza devam edelim. Tüm request tiplerimizi bir arada tutacağımız bir ApiRequest interface i oluşturuyorum. Daha sonra uygulama üzerinden gidecek olan tüm requestleri bu interface i implemente eden ve başka türlü çalışmasını istediğimiz bir şekle çevirmek istersek bu şekilde ilerleek bize fayda sağlıyacaktır.

 public interface IApiRequest
    {
        T Speculative { get; }
        T UserInitiated { get; }
        T Background { get; }
    }

Interface in sadece üç adet readonly propertysi mevcut. Bundan sonra bu Interface i implemente edecek, concrete ApiRequest sınıfımızı oluşturacağız ve Fusillade,Refit ve ModernHttpClient kütüphanelerini kullanmaya başlayacağız.

Bu yazıyı burada bırakıyorum. Hazırlık kısmı olarak biraz fazla uzadı. Buradaki adımları yaptığımızda bir sonraki yazımda asıl implementasyonu benimle beraber siz de yapabilirsiniz.

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

 

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;

  1. Parametre başına tanımlanan değer.
  2. metod başına tanımlanan değer
  3. 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.

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
  • ModernHttpClientPlatform 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.