Erhan Ballıeker

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.