Erhan Ballıeker

Tüm .Net Client Projelerimizde Ortak Kullanabileceğimiz Bir API Client yazalım Bölüm 2

Selamlar,

Bir önceki yazımda hazırlığını yapmış olduğumuz projemizin asıl kısmına gelelim.

Projemize ApiClient adında bir sınıf ekliyorum ve içeriğini aşağıdaki gibi dolduruyorum. Bir göz gezdirip detaylarına değinelim.


 public class ApiClient
        {
            #region fields

            private static ApiClient shared;
            private static object obj = new object();

            #endregion

            #region properties

            internal static IServiceCollection Services { get; set; }
            internal static IServiceProvider ServiceProvider { get; private set; }
            private static ApiClient Shared
            {
                get
                {
                    if (shared == null)
                    {
                        lock (obj)
                        {
                            if (shared == null)
                            {
                                shared = new ApiClient();
                            }
                        }
                    }

                    return shared;
                }
            }

            #endregion

            #region api spesific properties

            private IRandomUserApi _IRandomUserApi { get => ServiceProvider.GetRequiredService(); }

            //Exposed public static props via ApiClient.Shared 
            public static IRandomUserApi RandomUserApi{ get => Shared._IRandomUserApi; }

            #endregion

            #region ctor

            private ApiClient()
            {
                if (Services == null)
                    Services = new ServiceCollection();

                Init();
            }

            #endregion

            #region internal methods

            private void Init()
            {
                ConfigureServices(Services);
                ServiceProvider = Services.BuildServiceProvider();
            }

            private void ConfigureServices(IServiceCollection services)
            {
                services.AddTransient<ITokenService, TokenService>();

                #region AnonymousApi Configuration

                services.AddRefitClient()
                .ConfigureHttpClient(c =>
                {
                    c.BaseAddress = new Uri("http://wordy.azurewebsites.net/");
                    c.Timeout = TimeSpan.FromSeconds(10);
                })
                .AddTransientHttpErrorPolicy(p => p.RetryAsync())
                .AddHttpMessageHandler(serviceProvider =>
                {
                    var token = serviceProvider.GetRequiredService().GetToken();
                    return new AuthTokenHandler(token);
                });

                #endregion

            }

            #endregion
        }

Yukarıdakilerle alakalı olarak şurayı da okumanızı öneririm.

Burada önceki yazımda projeme eklediğim kütüphaneleri kullanmaya başlıyorum artık.

Yukarıda dönen hikaye şu;

  • Singleton bir ApiClient objem var. Tüm refit interfacelerini birer property üzerinden dışarıya expose ediyorum.
  • ConfigureServices metodunu neredeyse Asp.Net Core daki gibi birebir aynı yapmaya çalıştım. IoC conteiner ı oluşturup, refit interfacelerimi ve polly policy lerimi ilgili enpoint e register ediyor
  • Son olarak aynı şekilde yazmış olduğum DelegatingHandler larıda client ıma ekliyorum ve herşey kullanıma hazır hale geliyor.

Bundan sonra herhangi bir clien projesinde şunu yapabilirim

ApiClient.RandomUserApi.GetUser(“3”);

dediğimde Client projem her ne olursa olsun ister bir core web app ister xamarin ister başka birşey,  httpclient factory üzerinden refit, polly, delegating handler lar da kullanarak güzel yapı kurmuş oluyorum.

Bunları istediğimiz gibi şekillendirip güncelleyip, istediğimiz gibi konfigüre edebiliriz ve hiçbir client projemize dokunmamış oluruz.

Ek olarak burada ITokenService ve TokenService diye bir service yazdım. Bunu da şuna örnek olarak kullanabiliriz. Örneğin kullanıcı mobil uygulama da login olduğunda aldığımız token Xamarin.Essentials s Preference paketi ile saklayarak AuthTokenDelegating handler a parametre olarak verebilmenin örneği olsun diye koydum.

Farkli client projeler de bu ITokenService implemenatasyonlarını ayrı yazarak başka türlü yerlerde saklayıp okuyabiliriz.

Xamarin projesi için bu işimizi görecektir.

   public interface ITokenService
    {
        string GetToken();
    }

Xamarin forms projesi için implementasyon;

    public class TokenService : ITokenService
    {
        public string GetToken()
        {
            if (Preferences.ContainsKey(Constants.AuthToken))
                return Preferences.Get(Constants.AuthToken, string.Empty);
            else
                return string.Empty;
        }
    }

Bunu muhtemelen bu ApiClient projesinde değil de client projelerde register etmek daha doğru olacaktır gerçek hayat senaryolarında.

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

Tüm .Net Client Projelerimizde Ortak Kullanabileceğimiz Bir API Client yazalım.

Selamlar,

Daha önceden bahsetmiştim böyle bir konuya girmek istediğimi.

Yeni gelen HttpClientFactory ile de beraber daha önceden yazmış olduğum Resilient Network Services serisinin daha kısasını ve güncellenmiş halini kütüphanelerin detayların da çok fazla boğumladan yeniden incelemek istiyorum.

Amacımız şu;

Bir ApiClient Standard kütüphanesi oluşturalım. Solution ne kadar Client projemiz var ise Xamarin, Web, Console, UWP, WPF farketmez hepsi bu kütüphane üzerinden network haberleşmelerini yapsın(Http üzerinden)

Bu işlem sırasında da önceki yazılarımızda kullandığımız refit ve polly hatta akavache yi de kullanalım. ModernHttpClient a artık çok gerek yok, çünkü proje özelliklerinden Default mu yoksa platform spesifik sınıflarımı kullanmak istediğimizi zaten belirtebiliyoruz aşağıdaki gibi.Capture.PNG

Burada HttpClient implementation ın Managed mı Default mu yoksa Android mi olduğunu seçebiliyoruz. iOS tarafında da durum aynı şekilde.

Peki çok uzatmadan bu kütüphanemizi yazmaya başlayalım ve Asp.Net Core Web App ve Xamarin projelerimizde de kullanalım bakalım.

Bir adet boş solution açıp içerisine önce bir adet Asp.Net Core Web App ve bir adet de xamarin.forms projeleri ekleyelim.

Daha sonra Add New Project diyerek Bir adet .netstandard class library ekleyelim.

Capture.PNG

Bu kütüphanenin diğer tüm client projeleri tarafından kullanılabileceğine eminiz çünkü .netstandard kütüphanesi ekledik.

Projede kullanmak istediğimiz ve resilient network services kısmında bize yardımcı olacak 3 temel şey var.

  • Polly
  • Refit
  • HttpClientFactory

Tüm bunların kullanımı için öncelikle aşağıdaki paketleri projeye teker teker ekliyoruz.

  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.Http.Polly
  • Refit.HttpClientFactory
  • Xamarin.Essentials (xamarin tarafında kullanmak üzere şimdilik çok önemi yok)

Projede kullanmak için bir api yazmaktansa yine open apilardan randomuser  kullanalım.

Bunun için daha önceki yazılarımda çokça detayına girdiğim için burada konuyu uzatmadan hemen refit interface imi oluşturucam. Sonrasında da örnek bir tane delegating Handler oluşturucam

Bunlar aşağıdaki gibi;

Random user api si ile haberleşecek olan Refit Interface i;
Projede Endpoints adında bir klasör açıp içerisine aşağıdaki interface i atıyorum

Burada amaç tüm farklı endpointleri ayrı ayrı interfaceler altında toplayıp hepsini ayrı ayrı konfigüre edebilmek.

 [Headers("Content-Type : application-json")]
    public interface IRandomUserApi
    {
        [Get("/")]
        Task<string> GetUser(string results);
    }

DelegatingHandler ım.

 public class AuthTokenHandler : DelegatingHandler
    {
        private readonly string _token;
        private const string TOKEN = "Authorization";
        public AuthTokenHandler(string token)
        {
            _token = token;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!request.Headers.Contains(TOKEN) && string.IsNullOrEmpty(_token))
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    Content = new StringContent("Missing auth token.")
                };
            }
            else if (!request.Headers.Contains(TOKEN) && !string.IsNullOrEmpty(_token))
            {
                request.Headers.Add(TOKEN, $"Bearer {_token}");
            }

            var response = await base.SendAsync(request, cancellationToken);

            return response;
        }
    }

Bu iki kod bloğu ile ilgili daha detaylara girmek isterseniz aşağıdaki yazılarımı okuyabilirsiniz.

Projenin temel kısımları hazır oldu. Bundan sonraki yazımda asıl sınıfımız olan ApiClient sınıfını yazıp client projelerimizde kullanacağız.

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

Asp.Net Core HttpClientFactory + Polly

Selamlar,

HttpClientFactory ile ilgili yazılarıma bir yenisini de Polly ile kullanımını inceleyerek devam etmek istedim.

Polly, açıkçası farkında olmasanız bile kullanmak zorunda olduğunuz bir kütüphane diyecek kadar ileri gidebilirim =) eğer ki benzer işlevleri zaten zamanında siz kendiniz farklı kütüphaneler olarak yazmadıysanız.

Polly ile ilgili yazılarıma şuradan ulaşabilirsiniz;

Polly sizelere network haberleşmesi sırasında uygulamanızda olması gereken bir çok pattern i extension metodlar halinde sunan ve çok daha fazlasını yapmanıza olanak tanıyan bir kütüphane.

Polly ile ;

  • Error ve Result Handling
  • Retry, WaitAndRetry, ForeverRetry gibi senarto ve patternleri
  • Circuit Breaker ve Advanced Circuit Breaker gibi senaryo ve patternleri
  • Cache, Bulkhead Isolation gibi

birçok şeyi çok basit şekilde uygulamalarınıza implemente edebiliyorsunuz.

Detaylarına yukarıda ki yazılarımdan ve kendi github reposundan ulaşabilirsiniz.

Asp.Net Core projelerimizde HttpClientFactory ile beraber kullanmak için yapacağımız ilk şey nugetten aşağıdaki paketi indirmek

Microsoft.Extensions.Http.Polly

Daha sonrasında kullanım şekli önceki yöntemler ile çok benzer oluyor.

Uygulamanın startup dosyasında aşağıdaki gibi AddHttpHandler dedikten sonra o Client ile ilgili uygulamak istediğiniz policy leri fluent bir şekilde ekleyebiliyorsunuz.

  public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddHttpClient("randomuserclient")
                .AddPolicyHandler(Policy.TimeoutAsync(TimeSpan.FromSeconds(10)))
                .AddTransientHttpErrorPolicy(p => p.RetryAsync(3));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Yukarıdaki örneğimizde projemize NamedClient olarak eklemiş olduğumuz bir Client ın Polly yi nasıl kullanabileceğini söylemiş olduk.

Nuget ten indirdiğimiz paket ile beraber bize birçok extension metod geliyor.

Bunlardan sadece iki tanesini yukarıdaki senaryoda kullanmış olduk.

  • Time out policy ekleyerek, gidecek requestlerin en fazla 10 sn bekledikten sonra eğer response alamazsa doğrudan polly tarafından hataya düşürülmesini sağladık
  • Haberleşme sırasında geçici sorunlar olduğunda 3 kere yeniden denenmesini istedik. Anlık internet kayıpları, server anlık olarak response verememesi available olamaması gibi durumlarda request 3 kere yeniden denenecek.(RetryPattern)

Kullanım şekline bakığımızda ise öncekilerden hiçbir farkı yok. NamedClient eklemiş olduğumuz için aşağıdaki şekilde kullandık. Typed veya Basic usage olarak ekleseydik de o şekilde kullanacaktık. Polly nin yazarken ki dikkat etmemiz gereken tek kısmı Startup tarafında Policyleri Clientlarımıza implemente ettiğimiz kısım.

 public class HomeController : Controller
    {
        private readonly IHttpClientFactory_randomUserApi;
        public HomeController(IHttpClientFactory randomUserApi)
        {
            _randomUserApi = randomUserApi;
        }

        public async Task Index()
        {
            var result = await IHttpClientFactory.CreateClient("randomuserclient").GetUser("10");
            return View();
        }

      ..........

Bir önceki yazımda Refit ile HttpClientFactory i nasıl kullanırız bundan bahsetmiştim. Bir de bu üçünü yani Refit + Polly + HttpClientFactory i nasıl kullanırız buna bakalım.

Aşağıda basit bir Refit interface i görüyoruz.

namespace HttpClientFactoryRefit
{
    [Headers("Content-Type : application-json")]
    public interface IRandomUserApi
    {
        [Get("/")]
        Task GetUser(string results);
    }
}

Nugetten aynı projemize ;

Refit.HttpClientFactory 

paketini de indirdikten sonra aşağıdaki şekilde hem refit i AddRefitClient diyerek services a ekleyebilir, hem de sonrasında yukarıda ki gibi Polly Policy lerini aynı Client ın devamında ekleyebiliriz.

  public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddRefitClient()
                .ConfigureHttpClient(client =>
                {
                    client.BaseAddress = new Uri("https://randomuser.me/api");
                })
                .AddPolicyHandler(Policy.TimeoutAsync(TimeSpan.FromSeconds(10))) 
                .AddTransientHttpErrorPolicy(p => p.RetryAsync(3)); ;

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Kullanım şeklinde de Refit ile HttpClientFactory i nasıl kullanıyor isek aynı şekilde kullanmaya devam ediyoruz.

public class HomeController : Controller
    {
        private readonly IRandomUserApi _randomUserApi;
        public HomeController(IRandomUserApi randomUserApi)
        {
            _randomUserApi = randomUserApi;
        }

        public async Task Index()
        {
            var result = await _randomUserApi.GetUser("10");
            return View();
        }
       

          .........

Bu yazımızda da hem Polly ile HttpClienFactory nasıl kullanacağımızı görmüş olduk hem de bir önceki yazımızda Refit ile kullandığımız gibi refit i de işin içine dahil ettik.

Refit + Polly + HttpClienFactory üçlüsü güçlü bir Api haberleşme altyapısı için çok büyük artılar sağlayacaktır projenize.

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

Asp.Net Core HttpClientFactory + Refit

Selamlar,

Son birkaç yazıdır üzerinde durduğum HttpClientFactory ile sevdiğim ve sıkça kullandığımı kütüphanelerden biri olan Refit in beraber kullanımı hakkında yazmak istedim.

Daha önceki konu ile alakalı yazılarıma aşağıdaki linklerden ulaşabilirsiniz.

 

Refit daha önce kullanmadıysanız hemen kullanmaya başlamanız için çok sebep var. Detaylarına girmeyeğim, önceki yazılarda değinmiştim, ama özetle söyleyebilirimki bir enpoint ile yapacağınız tüm haberleşmeyi tek bir interface üzerinde hazırlayıp geri kalanını refit e bırakıyorsunuz. O da size runtime bu interface nizin canlı ve çalışan bir halini sunuyor.

Refit ile;

  • Get,Post,Put,Delete .. her HttpVerb e karşılık gelen metodlar oluşturabilirsiniz
  • Querystring, dynamic url parameter s gibi birçok şekilde get request i atabilirsiniz
  • json ve formurlencoded gibi birçok farklı serialization yöntemi ile post lar yapabilirsiniz
  • multipart upload lar yapabilirsiniz
  • static veya dynamic header ları kolayca request lerinize ekleyebilirsiniz

ve daha nicesi..

Aşağıdaki basit bir refit interface i görüyoruz. Bu interface e baktığımız da şunu anlıyoruz.

Relative url i olmayan bir endpoint e GET request i atılacak string olarak results adında bir querystring parametresi geçilecek. Geriyede string bir result dönecek. Gidecek olan request e de Content-Type : application/json header ı eklenecek.

namespace HttpClientFactoryRefit
{
    [Headers("Content-Type : application-json")]
    public interface IRandomUserApi
    {
        [Get("/")]
        Task<string> GetUser(string results);
    }
}

Peki daha önceden 3 farklı yöntem de HttpClientFactory Kullanımı görmüştük. Hatta Typed Client kullanırken tam olmasada benzer bir şekilde Client sınıfımızı ayrıca yazmıştık. Peki Elimizde varolan bir proje var diyelim, önceden de refit kullanmıştık. Ama artık HttpClientFactoryde kullanmak istiyoruz dersek ne yapacağız.

Sadece projemize nugetten

Refit.HttpClientFactory paketini indirdikten sonra, Startup dosyamızdaki ConfigureServices metodunda aşağıdaki şekilde tanımlamamızı yapabiliriz.

Tek fark önceden olduğu gibi AddHttpClien değil, artık AddRefitClient metodunu kullanıyo olmamız. Bu extension metod, yukarıdaki paketi indirdiğimizde gelecek.

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

                services.AddRefitClient()
                .ConfigureHttpClient(client =>
                {
                    client.BaseAddress = new Uri("https://randomuser.me/api");
                });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Peki kullanımı nasıl?

Kullanımının TypedClient kullanımından hiçbir farklı yok. Refit interface imizi sanki bir TypedClient mış gibi Controller ımızda tanımlıyoruz, ve constructor de bize inject edilecek olan interface olarak atamamızı yaptıktan sonra, dosya içerisinde istediğimiz yerde interface içerisinden yazdığımız istediğimiz metodu kolayca çağırıyoruz.

 public class HomeController : Controller
    {
        private readonly IRandomUserApi _randomUserApi;
        public HomeController(IRandomUserApi randomUserApi)
        {
            _randomUserApi = randomUserApi;
        }

        public async Task Index()
        {
            var result = await _randomUserApi.GetUser("10");
            return View();
        }

         ....

Bu kadar basit. Zaten refit kullandığınız ama HttpClientFactory e geçmediyseniz kodlarınızda çok az değişiklik yaparak kullanmaya başlayabilirsiniz.

Yeni başlayacağınız projelerde her ikisinide kullanmanızı öneririm.

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ı Bölüm 2

Selamlar,

Bir önceki yazımda önhazırlığını tamamladığımız yazımızın asıl kısmına geçelim.  En son IApiRequest adında bir interface oluşuturarak içerisine 3 adet readonly property koymuştuk. Şimdi hızlı bu interface implemente edecek olan gerçek ApiRequest sınıfımızı oluşturalım ve ilgili kütüphaneleri kullanmaya başlayalım.

ApiRequest adında bir class ekleyelim projemize, içeriği aşağıdaki şekilde olacak.

public class ApiRequest : IApiRequest
    {
        private readonly Lazy _background;
        private readonly Lazy _userInitiated;
        private readonly Lazy _speculative;

        public string BaseApiAddress = "https://randomuser.me/api";

        public ApiRequest()
        {
            _background = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Background), BaseApiAddress));
            _userInitiated = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.UserInitiated), BaseApiAddress));
            _speculative = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Speculative), BaseApiAddress));
        }

        public T Background => _background.Value;

        public T Speculative => _speculative.Value;

        public T UserInitiated => _userInitiated.Value;

        public T CreateClient(HttpMessageHandler handler, string baseApiAddress = null)
        {
            var client = new HttpClient(handler)
            {
                BaseAddress = new Uri(baseApiAddress ?? BaseApiAddress)
            };

            var res = RestService.For(client);
            return res;
        }
    }

Peki gelelim yukarıda ki class ımızı satır satır inceleyelim. Bu class ımızda bu seri boyunca bahsettiğimiz kütüphanelerden hali hazırda 3 tanesini kullanmış olduk. Bunlar Fusillade, Refit ve ModernHttpClient. Sınıfımız incelersek şunları görüyoruz.

  • class ımız IApiRequest Interfaceinden türüyor, ve buradan alması gereken propertyler aşağıdaki gibi oluşturuldu.
    • public T Background => _background.Value;
    • public T Speculative => _speculative.Value;
    • public T UserInitiated => _userInitiated.Value;
  • Yukarıda bu propertylerin initalize edilme kısmını C# ın Lazy objesine bıraktık. 3 adet field ımızı class ilk başında tanımlandı. Bunların tipi Lazy olarak işaretlendi. Ve ApiRequest sınıfmızın constructor nda da bu Lazy field ların neyi ne şekilde initiazlize edileceklerini tanımladık;
    • 3 Lazy adet field ımız
      • private readonly Lazy _background;
      • private readonly Lazy _userInitiated;
      • private readonly Lazy _speculative;
    • Constructor da bunların yine Lazy sınıfının constructor ını kullanarak ne şekilde ne döneceklerini tanımladık. Aşağıda yazmış olduğumuz CreateClient metodu ile geriye sınıfı oluştururken verdiğimiz generic parametresini geri dönüyoruz.  Constructor da kullandığımız bu RateLimitiedHttpMessageHandler ve NativeMessageHandler a dikkat edelim.YAzmış olduğumuz CreateClient metodu aslında bizim için bir HttpClien oluşturacak bunu biliyoruz. Parametre olarak bu method bizden bir HttpMessageHandler istiyor.  Bu metoda Fusillade in RateLimitedHttpMessageHandler ını veriyoruz ve priority oralark da her bir interface imizden gelen her bir property karşılık olan Fusillade Priortiy lerini veriyoruz.  RateLimitedHttpMessageHandler  içerisine contructor parametresi olarak bir de ModernHttpClient ın  NativeMessageHandler ını veriyoruz. Bu sayede en alttaki CreateClient metodumuza geçmiş olduğumuz HttpMessageHandler, ModerHttpclient ın handler ını kullanan Fusillade in RateLimited handler ı olmuş oldu. Bu ikisini bir arada bu şekilde kullanabiliriz.
      • _background = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Background), BaseApiAddress));
      • _userInitiated = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.UserInitiated), BaseApiAddress));
      • _speculative = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Speculative), BaseApiAddress));
    • Interface den gelen Propertylerimiz class ımız üzerinden seçildiğinde, LAzy sınıfı bizim ona verdiğimiz delegate i kullanarak bu sınıfı oluştururken verdiğimiz generic T objesini bizim için oluşturacak.
  • CreatClient metoduna bakalım.
    • var client = new HttpClient(handler)
    • { BaseAddress = new Uri(baseApiAddress ?? BaseApiAddress) };
      • Burada yeni HttpClient objesi oluşturup bunun runtime da bizim istediğimiz yaşayan Refit interface ini alıp haberleşme yapısı olarak geri vermesi için Refit in RestService.For generic metodu nu kullanıp içeriye client ımızı veriyoruz.
    • var res = RestService.For(client); return res;

Peki hemen bir de madem Refit i de kullandık, ilk yazıda belirttiğim bu endpoint ile haberleşecek ve userları bize getirecek olan refit Interface imizi oluşturalım.

Ben genelde bu refit interfacelerini proje içerisinde Endpoints diye bir klasör altında toplarım. Burada da öyle yapalım Endpoints diye bir klasör açalım ve içerisine

IRandomUserEndpoint diye bir interface ekleyelim

    [Headers("Content-Type : application/json")]
    public interface IRandomUserEndpoint
    {
        [Get("/")]
        Task GetUserList(int results, int page);
    }

Interfaceimizin içeriği yukarıdaki gibi olacak. Refit ile ilgili postlarımda refit kullanarak e şekilde requestleri nasıl metod haline dönüştürdüğümüzün detaylarına girmiştim. Burada özetle;

  • her bir request e “Content-Type : application/json” header ı ekliyoruz.
  • Get metodu ile direk olarak verilen base url gittiğimizi Get metodunu kullanarak söylüyoruz.
  • results ve page parametreleri de url de karşılıkları olmadığı için otomatik olarak querystring olarak request url ine eklenecekler.

Bu şekilde bahsettiğmiz url ile haberleşecek interface ide oluşturmuş olduk. gerisi refit e kalıyor.

Tüm bu yazdıklarımızdan sonra bir özetlemek istersek ne şekilde bunları kullanabiliriz ve neler olmuş olur şöyle bir üzerinden geçelim.

Uygulamada şu kodu yazdığımız anda;

IApiRequest<IRandomUserEndpoint>.UserInitiated

dediğimiz de Refit bizim için yazdığımız interface in yaşayan bir httpclient halini üretecek. Bu HttpClient a verdiğimiz Fusillade nin RateLimitedHttpMessageHandler ve ModernHttpClient ın NativeMessageHandler  ları refit oluşturacağı HttpClient ın HttpMessageHandler ları olarak kullanılacak. Yani Refit e oluşturmasını istediğimiz HttpClient nasıl bir haberleşme yağacağını söylüyoruz, bu esnada hangi handlerları kullanmasını istediğimizi söylüyoruz ve sonrasında ortaya Refit, Fusillade ve ModernHttpClient ın bir arada kullanılmış olduğu güzel bir client objesi elimze geçiyor. Ve bunun üzerinden kullanımımıza devam ediyoruz.

Bu post u burada bırakıyorum. Bir sonraki post umda bu serinin son yazısını yani kullanıma Polly e Akavache yi de nasıl dahil edeceğimize bakarak, tamamlamış olmayı umut ediyorum 🙂

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.