.NET Core
.NET'te API Versioning: Stratejiler ve Uygulama
API'ler zamanla degisir. Alanlar yeniden adlandirilir, endpoint'ler yeniden yapilandirilir, yanit yapilari degisir. Birden fazla istemcinin kullandigi API'lerde -- mobil uygulamalar, partner entegrasyonlari, dahili frontend'ler -- versiyonlama ileriye gitmenizi saglayan ama geride kalanlari kirmayan bir sozlesmedir.
API Versiyonlama Neden Onemlidir
Bir API yayinladiginizda tuketiciler yanit yapilariniza, durum kodlariniza ve alan adlariniza gore kod yazar. Bunlardan birini degistirdiginiz an sozu bozarsiniz. Versiyonlama olmadan ekipler ya API'yi dondurur ya da sessiz kirilmalara yol acar. Her iki sonuc da güveni asindirır.
Versiyonlama Yaklasimlari Karsilastirmasi
URL Yolu ile Versiyonlama
GET /api/v1/products
GET /api/v2/productsEn görünür ve yaygin yaklasimdir. Kesfetmesi, yonlendirmesi ve önbellek yönetimi kolaydir. API gateway'ler yol segmentlerine gore yonlendirme yapabilir. Dezavantaji URL kirliligidir.
Sorgu Dizgisi ile Versiyonlama
GET /api/products?api-version=1.0
GET /api/products?api-version=2.0URL'leri daha temiz tutar ve uygulamasi kolaydir ama dokümantasyonda gözden kaçabilir. Önbellek proxy'leri sorgu parametrelerini yok sayabilir.
Header ile Versiyonlama
GET /api/products
X-Api-Version: 1.0URL'yi tamamen temiz tutar ve versiyonlamayi kaynak tanimlamasindan ayirir. Dahili API'ler icin iyi calisir. Dezavantaji azalan kesfedilebilirliktir.
Medya Tipi ile Versiyonlama
GET /api/products
Accept: application/vnd.myapp.v2+jsonTeoride en RESTful yaklasim olsa da, zayif araç destegi ve ek bilissel yük nedeniyle pratikte nadiren tercih edilir.
Asp.Versioning Kurulumu
Asp.Versioning kütüphanesi (eski adiyla Microsoft.AspNetCore.Mvc.Versioning) .NET'te API versiyonlama icin standarttir. Ilgili paketleri yükleyin:
// Controller'lar icin
dotnet add package Asp.Versioning.Mvc
dotnet add package Asp.Versioning.Mvc.ApiExplorer
// Minimal API'ler icin
dotnet add package Asp.Versioning.HttpTemel Yapilandirma
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("X-Api-Version"),
new QueryStringApiVersionReader("api-version")
);
})
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});ReportApiVersions = true her yanita api-supported-versions ve api-deprecated-versions basliklarini ekler.
Controller'lar ile Versiyonlama
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll()
{
return Ok(new[]
{
new { Id = 1, Name = "Widget", Price = 9.99m }
});
}
}
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("2.0")]
public class ProductsV2Controller : ControllerBase
{
[HttpGet]
public IActionResult GetAll()
{
return Ok(new[]
{
new { Id = 1, Name = "Widget", UnitPrice = 9.99m, Currency = "USD" }
});
}
}Birden fazla versiyon ayni controller'i paylasabilir:
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class CustomersController : ControllerBase
{
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1() => Ok(new { Name = "Acme" });
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2() => Ok(new { Name = "Acme", Tier = "Enterprise" });
}Minimal API'ler ile Versiyonlama
var versionSet = app.NewApiVersionSet()
.HasApiVersion(new ApiVersion(1, 0))
.HasApiVersion(new ApiVersion(2, 0))
.ReportApiVersions()
.Build();
var v1 = app.MapGroup("api/v{version:apiVersion}/products")
.WithApiVersionSet(versionSet)
.MapToApiVersion(new ApiVersion(1, 0));
var v2 = app.MapGroup("api/v{version:apiVersion}/products")
.WithApiVersionSet(versionSet)
.MapToApiVersion(new ApiVersion(2, 0));
v1.MapGet("/", () => Results.Ok(new[]
{
new { Id = 1, Name = "Widget", Price = 9.99m }
}));
v2.MapGet("/", () => Results.Ok(new[]
{
new { Id = 1, Name = "Widget", UnitPrice = 9.99m, Currency = "USD" }
}));Versiyon sayisi arttikça rota kayitlarini ayri extension method'lara çikarmayi düsünün.
Versiyon Müzakeresi ve Varsayilan Versiyonlar
AssumeDefaultVersionWhenUnspecified = true ayari, versiyonsuz isteklerin varsayilan versiyonunuza düsmesini saglar. Geriye dönük uyumluluk icin kullanislidir ama sorunlari maskeleyebilir. Üretim ortaminda daha güvenli yaklasim:
options.AssumeDefaultVersionWhenUnspecified = false;Desteklenmeyen versiyonlarda 400 Bad Request döndürür ve tüketicileri bilinçli olmaya zorlar.
Kirici ve Kirici Olmayan Degisiklikler
Kirici Olmayan (versiyonlama olmadan güvenle eklenebilir)
- Yanita yeni opsiyonel alanlar ekleme
- Yeni bir endpoint ekleme
- Opsiyonel sorgu parametreleri ekleme
- Bir dogrulama kisitlamasini gevsetme
Kirici (yeni versiyon gerektirir)
- Bir alani kaldirma veya yeniden adlandirma
- Bir alanin veri tipini degistirme
- Yanit yapisini degistirme (örn. yeni bir zarf ile sarma)
- Hata yanit formatini degistirme
- Daha önce opsiyonel olan bir alani zorunlu yapma
- HTTP metodu veya durum kodu anlamini degistirme
Birden fazla istemcinin kullandigi API'lerde gri alanlar bariz kirmalardan daha fazla hasar verir. string'den string | null'a degismek teknik olarak eklemseldir ama null yönetimi yapmayan tüketiciyi çökertir. Emin degilseniz, versiyonlayin.
Kullanim Disi Birakma Stratejisi ve Sunset Basliklari
Eski versiyonlari emekliye ayirmak icin de bir plana ihtiyaciniz vardir. Versiyonlari kullanim disi olarak isaretleyin:
[ApiVersion("1.0", Deprecated = true)]
[ApiVersion("2.0")]
public class ProductsController : ControllerBase { }Veya Minimal API'lerde:
var versionSet = app.NewApiVersionSet()
.HasDeprecatedApiVersion(new ApiVersion(1, 0))
.HasApiVersion(new ApiVersion(2, 0))
.Build();Kullanim disi versiyonlar çalismaya devam eder ama api-deprecated-versions: 1.0 basligini içerir. Bunu RFC 8594 Sunset basligiyla tamamlayin:
app.Use(async (context, next) =>
{
await next();
var apiVersion = context.GetRequestedApiVersion();
if (apiVersion?.MajorVersion == 1)
{
context.Response.Headers["Sunset"] = "Sat, 01 Nov 2025 00:00:00 GMT";
context.Response.Headers["Link"] =
"</api/v2/products>; rel=\"successor-version\"";
}
});Bu, istemcilere makine tarafindan okunabilir bir son tarih ve yerine geçen versiyona isaretçi saglar.
Versiyonlu API'ler icin OpenAPI Dokümantasyonu
Swagger/OpenAPI'yi versiyon basina ayri belgeler üretecek sekilde yapilandirin:
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Products API",
Version = "v1",
Description = "Orijinal ürün endpoint'leri"
});
options.SwaggerDoc("v2", new OpenApiInfo
{
Title = "Products API",
Version = "v2",
Description = "Para birimi destegi ile gelistirilmis ürün endpoint'leri"
});
});
// Middleware pipeline'inda
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Products API v1");
options.SwaggerEndpoint("/swagger/v2/swagger.json", "Products API v2");
});Otomatik versiyon kesfi icin IConfigureOptions<SwaggerGenOptions> kullanin:
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
private readonly IApiVersionDescriptionProvider _provider;
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider)
=> _provider = provider;
public void Configure(SwaggerGenOptions options)
{
foreach (var description in _provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, new OpenApiInfo
{
Title = "Products API",
Version = description.ApiVersion.ToString(),
Description = description.IsDeprecated
? "Bu versiyon kullanim disi birakilmistir."
: null
});
}
}
}Yeni versiyon eklemek artik otomatik olarak OpenAPI belgesini üretir.
Yaygin API Versiyonlama Hatalari
- Asiri agresif versiyonlama -- her küçük degisiklik icin yeni versiyon olusturmak API yüzeyini parçalar ve bakim maliyetini katlar. Yeni versiyonlari gerçek kirici degisiklikler icin saklayin.
- Kullanim disi birakma takvimi belirlememek -- kullanim disi versiyonlar sonsuza kadar kalirsa, her versiyonu süresiz bakim altinda tutarsiniz. Açik sunset tarihleri belirleyin.
- Endpoint'ler arasi tutarsiz versiyonlama -- tek tek endpoint'leri versiyonlamak, dokümante edilmesi neredeyse imkansiz bir matris olusturur.
- Istemci tarafindaki etkiyi unutmak -- SDK'lar, istemci kütüphaneleri ve entegrasyon testleri de hangi versiyonu hedeflediklerini takip etmelidir.
- Versiyon numaralarini deploy döngüsüne baglamak -- API versiyonlari sözlesme degisikliklerini temsil eder, sürüm döngülerini degil.
- Versiyon kesfini ihmal etmek -- istemciler mevcut versiyonlari programatik olarak bulamazsa, kaçinilmaz olarak yanlis versiyonu çagiracaklardir.
Sonuc
API versiyonlama güvenle ilgilidir. Teknik uygulama, degisiklikleri net iletme ve versiyonlari sorumlu bir sekilde emekliye ayirma konusundaki kurumsal bagliliktan daha az önemlidir. Ekosistemine uyan bir sema seçin, dokümantasyonu otomatiklestirin, sunset tarihleri belirleyin ve uygulatin.
API'leriniz icin versiyonlama stratejisi tasarliyorsaniz, uygun yaklasimi bulmanizda yardimci olabilirim.
İlgili Makaleler
ASP.NET Core ile RESTful API Geliştirme
ASP.NET Core ile production-ready REST API geliştirmenin temellerini öğrenin. Controller, routing ve best practice'ler.
.NET Minimal APIs: Hafif ve Hızlı Endpoint'ler
.NET Minimal API ile hızlı ve hafif endpoint'ler oluşturun. Controller-free yaklaşım ve kullanım senaryoları.
ASP.NET Core Middleware Pipeline: Derinlemesine Rehber
ASP.NET Core middleware pipeline'ını derinlemesine öğrenin. Custom middleware yazma, sıralama, exception handling ve rate limiting.
Flutter Projeniz mi Var?
iOS, Android ve web için yüksek performanslı Flutter uygulamaları geliştiriyorum.
İletişime Geç