.NET'te API Versioning: Stratejiler ve Uygulama
# .NET'te API Versiyonlama Stratejileri
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:
class=class="code-string">"code-comment">// Controllerclass="code-string">'lar icin
dotnet add package Asp.Versioning.Mvc
dotnet add package Asp.Versioning.Mvc.ApiExplorer
class=class="code-string">"code-comment">// Minimal API'ler icin
dotnet add package Asp.Versioning.HttpTemel Yapilandirma
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(class="code-number">1, class="code-number">0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader(class="code-string">"X-Api-Version"),
new QueryStringApiVersionReader(class="code-string">"api-version")
);
})
.AddApiExplorer(options =>
{
options.GroupNameFormat = class="code-string">"class="code-string">'v'VVV";
options.SubstituteApiVersionInUrl = true;
});`ReportApiVersions = true` her yanita `api-supported-versions` ve `api-deprecated-versions` basliklarini ekler.
Controller'lar ile Versiyonlama
[ApiController]
[Route(class="code-string">"api/v{version:apiVersion}/[controller]")]
[ApiVersion(class="code-string">"class="code-number">1.0")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll()
{
return Ok(new[]
{
new { Id = class="code-number">1, Name = class="code-string">"Widget", Price = class="code-number">9.99m }
});
}
}
[ApiController]
[Route(class="code-string">"api/v{version:apiVersion}/[controller]")]
[ApiVersion(class="code-string">"class="code-number">2.0")]
public class ProductsV2Controller : ControllerBase
{
[HttpGet]
public IActionResult GetAll()
{
return Ok(new[]
{
new { Id = class="code-number">1, Name = class="code-string">"Widget", UnitPrice = class="code-number">9.99m, Currency = class="code-string">"USD" }
});
}
}Birden fazla versiyon ayni controller'i paylasabilir:
[ApiController]
[Route(class="code-string">"api/v{version:apiVersion}/[controller]")]
[ApiVersion(class="code-string">"class="code-number">1.0")]
[ApiVersion(class="code-string">"class="code-number">2.0")]
public class CustomersController : ControllerBase
{
[HttpGet]
[MapToApiVersion(class="code-string">"class="code-number">1.0")]
public IActionResult GetV1() => Ok(new { Name = class="code-string">"Acme" });
[HttpGet]
[MapToApiVersion(class="code-string">"class="code-number">2.0")]
public IActionResult GetV2() => Ok(new { Name = class="code-string">"Acme", Tier = class="code-string">"Enterprise" });
}Minimal API'ler ile Versiyonlama
var versionSet = app.NewApiVersionSet()
.HasApiVersion(new ApiVersion(class="code-number">1, class="code-number">0))
.HasApiVersion(new ApiVersion(class="code-number">2, class="code-number">0))
.ReportApiVersions()
.Build();
var v1 = app.MapGroup(class="code-string">"api/v{version:apiVersion}/products")
.WithApiVersionSet(versionSet)
.MapToApiVersion(new ApiVersion(class="code-number">1, class="code-number">0));
var v2 = app.MapGroup(class="code-string">"api/v{version:apiVersion}/products")
.WithApiVersionSet(versionSet)
.MapToApiVersion(new ApiVersion(class="code-number">2, class="code-number">0));
v1.MapGet(class="code-string">"/", () => Results.Ok(new[]
{
new { Id = class="code-number">1, Name = class="code-string">"Widget", Price = class="code-number">9.99m }
}));
v2.MapGet(class="code-string">"/", () => Results.Ok(new[]
{
new { Id = class="code-number">1, Name = class="code-string">"Widget", UnitPrice = class="code-number">9.99m, Currency = class="code-string">"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)
Kirici (yeni versiyon gerektirir)
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(class="code-string">"class="code-number">1.0", Deprecated = true)]
[ApiVersion(class="code-string">"class="code-number">2.0")]
public class ProductsController : ControllerBase { }Veya Minimal API'lerde:
var versionSet = app.NewApiVersionSet()
.HasDeprecatedApiVersion(new ApiVersion(class="code-number">1, class="code-number">0))
.HasApiVersion(new ApiVersion(class="code-number">2, class="code-number">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 == class="code-number">1)
{
context.Response.Headers[class="code-string">"Sunset"] = class="code-string">"Sat, class="code-number">01 Nov class="code-number">2025 class="code-number">00:class="code-number">00:class="code-number">00 GMT";
context.Response.Headers[class="code-string">"Link"] =
class="code-string">"</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(class="code-string">"v1", new OpenApiInfo
{
Title = class="code-string">"Products API",
Version = class="code-string">"v1",
Description = class="code-string">"Orijinal ürün endpointclass="code-string">'leri"
});
options.SwaggerDoc(class="code-string">"v2", new OpenApiInfo
{
Title = class="code-string">"Products API",
Version = class="code-string">"v2",
Description = class="code-string">"Para birimi destegi ile gelistirilmis ürün endpoint'leri"
});
});
class=class="code-string">"code-comment">// Middleware pipeline'inda
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint(class="code-string">"/swagger/v1/swagger.json", class="code-string">"Products API v1");
options.SwaggerEndpoint(class="code-string">"/swagger/v2/swagger.json", class="code-string">"Products API v2");
});Otomatik versiyon kesfi icin `IConfigureOptions
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 = class="code-string">"Products API",
Version = description.ApiVersion.ToString(),
Description = description.IsDeprecated
? class="code-string">"Bu versiyon kullanim disi birakilmistir."
: null
});
}
}
}Yeni versiyon eklemek artik otomatik olarak OpenAPI belgesini üretir.
Yaygin API Versiyonlama Hatalari
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ç