Asp.NET Core – API Versiyonlama

Merhaba,

API geliştirilirken aklımızda tutmamız gereken bir realite vardır ki o da ‘Değişim Kaçınılmazdır’ ilkesidir. Gelişim sürecinde her ne kadar iş odaklı bir çalışma gerçekleştiriyor olsakta, kâh öngörülemeyen kâh sonradan doğan ihtiyaçlara binaen API’a daha fazla sorumluluk eklememiz gerekebilmektedir. İşte böyle bir durumda yapılan değişikliklere istinaden API’ların istemciler üzerindeki etkisini yönetebilmek ve operasyonel olarak gerçekleştirilen çalışmayı raporlayabilmek için her bir güncelleme neticesinde API’ları versiyonlamamız gerekmektedir.

API’ları versiyonlayabilmek için URL API Versioning, QueryString API Versioning ve Media/Header API Versioning gibi birbirlerinden farklı avantajlara sahip birkaç yaklaşım söz konusudur. Bizler bu içeriğimizde, bu yaklaşımlar eşliğinde, Microsoft’un Asp.NET Core MVC Versiyon Oluşturma(ASP.NET Core MVC Versioning) paketi ile Asp.NET Core RESTFul API’lerinin nasıl sürümlendirildiğini konuşuyor olacağız.

API’leri Neden Versiyonlamamız Gerekmektedir?

Esasında bu gereksinim yazılımsal bir geleneğin günümüzdeki tezahürüdür diyebiliriz. Bu gelenek ise sözleşmeli tasarım modeli(Design by contract) olan ‘Contract-Based Programming/Sözleşme Tabanlı Programlama’nın ta kendisidir. Eskiden -WebService / WCF- gibi hizmetler döneminde çok fazla tercih edilen sözleşme tabanlı programlama yaklaşımı günümüzün API teknolojisini modernize etme sürecinde de temel ihtiyaç olarak karşımıza çıkmakta ve teknik olarak belli başlı değişikliklerle birlikte yeni bir esas halini almaktadır.

Contract-Based Programming yaklaşımı, client ile server arasına yerleştirilen bir sözleşme gereğince veri transferinin gerçekleşmesini sağlayan bir işlevselliğe sahiptir.

Versiyonlamanın geleneksel etkilerden ziyade esas itibariyle sahasal bir ihtiyacı söz konusudur. Bu ihtiyaç esasında API’lar da ki fiziksel güncellemeler üzerine olan client-server ilişkisi üzerinedir. Belli başlı tüketiciler tarafından kullanılan API’lar da yapılan değişiklikler mevcut tüketiciler açısından kırılmalara/patlamalara sebep olacaktır. Dolayısıyla bir değişiklikte önemsenmesi gereken ilk faktör o API tüketicilerinin işlevsel akışlarında yaşanabilecek kesintilerdir. Böyle bir durumda client’ların API ile kurduğu etkileşim biçimini tanımlamakta çok büyük fayda vardır.

Asp.NET Core - API Versiyonlama
Client’ın API ile etkileşim kurma biçimi yandaki görselde olduğu gibidir. Burada ‘Employee’ bir sözleşmeyi temsil etmekte ve her iki hizmetin de gerekli verileri iletebilmesi ve doğru çalışabilmesi için sözleşme formatına uyması gerekmektedir. Sözleşmenin formatında yahut içeriğinde bir değişiklik yapıldığı zaman bu durum mevcut client’larda işlevsel hatalara ve hatta patlamalara sebebiyet verebilmektedir.

 
Bu hataların yahut patlamaların olabileceği genel durumlar;

  • API yollarının yeniden adlandırılması,
  • API metotlarının güncellenmesi(üretilen verilerin değişmesi),
  • Hata kodlarındaki değişiklikler,
  • Veri formatının değiştirilmesi

şeklinde tanımlanabilir.

Sözleşmenin bir diğer önemli hususu veri formatıdır. REST; json, xml, text vs. gibi birçok veri formatını desteklemektedir.

REST ile client’a asla gerçek nesne gönderilmez, gerçek nesne yerine temsili olan JSON yahut diğer formatlardaki değerler gönderilir.

Client’ın API ile kurduğu ilişki ve veri formatı yapılanmasından sonra esas soruya yani ‘API’leri Neden Versiyonlamamız Gerekmektedir?‘ sorusuna gelirsek eğer; bir API’da yapılan her bir değişiklik neticesinde, API ile o API’yi tüketen client arasındaki ilişkiyi zedelememek için ilgili client’ı haberdar etmemiz gerekmektedir. İşte burada versiyonlama yeterince efektif bir çözüm getirmekte ve url’de ki sayısal bir değer üzerinde sadece ufak bir aritmetik işlem neticesinde client’ı bilgilendirmekte ve çarkın kaldığı yerden devam etmesini sağlamaktadır.

Ek ilave olarak API’da yapılan değişiklikleri raporlamak ve en son değişikliğe istinaden API’ın güncelliğini vurgulamak amaçlı versiyonlama gerçekleştirilmektedir.

Başlarken

İlk olarak örneklendirmede bize eşlik edebilmesi için elimizin altında hali hazırda bir Asp.NET Core Web API uygulamasının bulunması gerekmektedir. Haliyle öncelikle bir Web API projesi oluşturalım ve içerisine aşağıdaki controller’ı ekleyelim.

    [ApiController]
    [Route("api/[controller]")]
    public class EmployeesController : ControllerBase
    {
        public IActionResult Get()
        {
            string[] employees = { "Nevin", "Fatih", "Hacer", "İbrahim" };
            return Ok(employees);
        }
    }

Bu süreçte örneklendirme yapacağımız controller’dır.

Ardından ilgili projede versiyonlama operasyonlarını yürütebilmek için Microsoft.AspNetCore.Mvc.Versioning paketini yükleyelim.

Asp.NET Core - API Versiyonlama

İlgili paketi yükledikten sonra uygulamanın ‘Startup.cs’ dosyasında versiyonlama işlemini yürütecek ‘AddApiVersioning’ servisini çağıralım.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddApiVersioning();
        }

        .
        .
        .
    }

Bu işlem neticesinde artık uygulamamıza versiyonlama servisi yüklenmiştir. Şimdi bir kaç ufak tefek temel konfigürasyona değinerek temeli derinleştirelim;

Varsayılan(Default) Versiyon Belirleme

Mevcut haliyle API’a bir GET isteği gönderildiğinde aşağıdaki hatayla karşılaşılmaktadır;
Asp.NET Core - API Versiyonlama
Hatanın metinsel hali;
{“error”:{“code”:”ApiVersionUnspecified”,”message”:”An API version is required, but was not specified.”,”innerError”:null}}

Bu hatayla varsayılan sürüm bilgisi girilmediğinden dolayı karşılaşmaktayız. Request url’inde API’a dair bir sürüm bilgisi belirtilmediği durumlarda tarafımızca varsayılan bir versiyonun belirtilmesi gerekmektedir. Bunun için aşağıdaki gibi ‘AssumeDefaultVersionWhenUnspecified’ özelliği kullanılabilir.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddApiVersioning(_ =>
            {
                _.DefaultApiVersion = new ApiVersion(1, 0);
                _.AssumeDefaultVersionWhenUnspecified = true;
            });
        }

        .
        .
        .
    }

Yapılan bu konfigürasyon neticesinde; ‘AssumeDefaultVersionWhenUnspecified’ özelliğine true değeri set edilerek, endpoint’te herhangi bir sürüm belirtilmediği taktirde 1.0 versiyonu varsayılan olarak kabul edilmektedir.

Haliyle son durumda aşağıdaki gibi API’a yapılan istekte url’de bir versiyon bilgisi bulunmadığından dolayı compiler versiyonu 1.0 varsaymakta ve isteği başarıyla döndürmektedir.
Asp.NET Core - API Versiyonlama

Client’ı Desteklenen Tüm Sürümlerden Haberdar Etme

Bir API’ın versiyonlarını belirtebilmek için ilgili controller’ı ‘ApiVersion’ attribute’u ile işaretlememiz gerekmektedir.

    [ApiController]
    [Route("api/[controller]")]
    [ApiVersion("1.0")]
    [ApiVersion("1.1")]
    [ApiVersion("1.2")]
    [ApiVersion("2.0")]
    public class EmployeesController : ControllerBase
    {
        public IActionResult Get()
        {
            string[] employees = { "Nevin", "Fatih", "Hacer", "İbrahim" };
            return Ok(employees);
        }
    }

Yukarıdaki gibi birden fazla versiyona sahip olan bir controller’ın desteklediği tüm versiyonları client’a bilgilendirmek için ‘ReportApiVersions’ özelliğinin ‘true’ olması gerekmektedir.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddApiVersioning(_ =>
            {
                _.DefaultApiVersion = new ApiVersion(1, 0);
                _.AssumeDefaultVersionWhenUnspecified = true;
                _.ReportApiVersions = true;
            });
        }

        .
        .
        .
    }

Asp.NET Core - API Versiyonlama
Görüldüğü üzere response header’da api-supported-versions key’ine karşılık desteklenen tüm versiyon bilgileri client tarafından edinilmektedir.

Ayriyetten controller seviyesinde kullanımdan kaldırılan versiyonlar ‘Deprecated’ özelliğine verilen ‘true’ değeri ile bildirilebilmekte ve böylece istek neticesinde client’a bildiri olarak gönderilmemektedir.

    [ApiVersion("1.0", Deprecated = true)]

Asp.NET Core - API Versiyonlama

Action Seviyesinde Sürüm Oluşturmayı Etkinleştirme

Action seviyesinde sürüm oluşturabilmek için ‘MapToApiVersion’ attribute’u kullanılmaktadır.

    [ApiController]
    [Route("api/[controller]")]
    [ApiVersion("1.0")]
    [ApiVersion("1.1")]
    [ApiVersion("2.0")]
    public class EmployeesController : ControllerBase
    {
        string[] employees = { "Nevin", "Fatih", "Hacer", "İbrahim" };
        [MapToApiVersion("1.0")]
        public IActionResult Get()
        {
            return Ok(employees);
        }
        [MapToApiVersion("1.1")]
        [HttpGet("{id}")]
        public IActionResult GetById(int id)
        {
            return Ok(employees[id]);
        }
        [MapToApiVersion("2.0")]
        public IActionResult GetLast()
        {
            return Ok(employees[employees.Length - 1]);
        }
    }

Böylece hangi action’ın hangi versiyona karşılık geldiğini görebilir ve karışıklılığı engelleyebilirsiniz.

Evet, böylece bir Asp.NET Core uygulamasında API versiyonlayabilmek için olması gereken temeli atmış bulunmaktayız. Şimdi sırasıyla versiyonlama stratejilerini ele alabiliriz;

Asp.NET Core Versiyonlama Stratejileri

QueryString API Versioning

Bu stratejide her kaynak için url’e aşağıdaki gibi bir versiyon bilgisi eklenmesi gerekmektedir.
https://localhost:5001/api/employees?api-version=1.0
Asp.NET Core - API Versiyonlama
Görüldüğü üzere query string versiyonlamada herhangi bir konfigürasyona gerek kalmaksızın versiyon bilgisiyle eşleşen action direkt sonuç döndürmektedir.

Media/Header API Versioning

Versiyonlama için bir başka teknik ise header’a yerleştirilen sürüm bilgisidir. Bu yöntemi kullanabilmek için ‘ApiVersionReader’ konfigürasyonunun aşağıdaki gibi gerçekleştirilmesi gerekmektedir.

.
.
.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddApiVersioning(_ =>
            {
                _.DefaultApiVersion = new ApiVersion(1, 0);
                _.AssumeDefaultVersionWhenUnspecified = true;
                _.ReportApiVersions = true;
                _.ApiVersionReader = new HeaderApiVersionReader("api-version");
            });
        }
.
.
.

‘HeaderApiVersionReader’ nesnesine verilen ‘api-version’ değeri, header’da sürüm bilgisini taşıyacak olan key’e karşılık gelmektedir. Bu konfigürasyondan sonra aşağıdaki gibi ilgili header key’ine karşılık verilen sürüm bilgisiyle eşleşen action tetiklenmektedir.
Asp.NET Core - API Versiyonlama

URL API Versioning

Versiyonlama işleminin bir başka ve son stratejisi ise url versiyonlamadır. Bu yöntemde versiyon bilgisi route parametresi olarak url’de taşınmaktadır. Haliyle query string versiyonlamaya nazaran, url fiziksel olarak versiyon bilgisiyle bütünleşiktir. Dolayısıyla yeni versiyon yayınlandığında client’ın elindeki url’i güncellemesi gerekecektir. Nihai olarak, bu yaklaşım, bir REST ilkesi olan -belirli bir kaynağın url’sinin asla değişmemesi gerekliliği-ni ihlal eden bir istisnai durum taşımaktadır.

Bu versiyonlama stratejisini uygulayabilmek için ilgili controller’ın rotasında aşağıdaki parametrelerin bildirilmesi gerekmektedir;
[Route("api/v{version:apiVersion}/[controller]")]

Sözgelimi;

    [ApiController]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiVersion("1.0")]
    [ApiVersion("1.1")]
    [ApiVersion("2.0")]
    public class EmployeesController : ControllerBase
    {
        .
        .
        .
    }

şeklinde olacaktır.
Asp.NET Core - API Versiyonlama

Nihai olarak;
Makalemizin başlarında da değindiğimiz gibi iş gereksinimlerindeki öngürülemez durumların yahut süreçteki ihtiyaçların farklılaşmasının API’lara sürekli değişiklik olarak yansımasından dolayı versiyonlamanın öneminden, client’ları bozmadan sürümlemenin nasıl gerçekleştirildiğinden belli başlı stratejiler eşliğinde bahsetmiş olduk.

Okuduğunuz için teşekkür ederim.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*