API Versioning — URL, header, query string versioning

~20 daq · .NET Backend · API Sifati va Dizayn

Muammodan boshlaylik

Mahsulot javobida narx — oddiy raqam edi. Endi ko'p valyuta kerak:

// eski
{ "id": 5, "nomi": "Telefon", "narx": 3500000 }

// yangi
{ "id": 5, "nomi": "Telefon", "narx": { "summa": 3500000, "valyuta": "UZS" } }

O'zgarishni deploy qildingiz — 200 000 foydalanuvchidagi eski mobil ilova narx ni raqam deb o'qiydi va qulaydi. Ilovani App Store orqali yangilash — kunlar, ko'p foydalanuvchi esa umuman yangilamaydi.


Hayotiy o'xshatma

Elektr rozetkalar. Yangi standart chiqsa, eski rozetkalarni bir kunda sindirib tashlashmaydi — eski va yangi bir muddat yonma-yon ishlaydi, adapterlar sotiladi, va bir necha yildan keyin eskisi asta-sekin yo'qoladi. Hech kimning muzlatkichi bir kechada o'chib qolmaydi.


Qachon yangi versiya kerak

Buzuvchi (breaking) — yangi versiya Buzmaydigan — shu versiyada
Maydonni o'chirish yoki nomini o'zgartirish Yangi ixtiyoriy maydon qo'shish
Maydon turini o'zgartirish Yangi endpoint qo'shish
Majburiy so'rov parametri qo'shish Yangi ixtiyoriy parametr
Status kodlar yoki xato formatini o'zgartirish Ishlashni tezlashtirish
Endpoint ni o'chirish Yangi enum qiymati (agar mijozlar noma'lumga tayyor bo'lsa)

[!TIP] Eng yaxshi versiya — chiqarilmagan versiya. Ko'p o'zgarishlarni buzmasdan qilish mumkin: yangi maydonni qo'shing (narxTafsilot), eskisini bir muddat qoldiring. Yangi versiya — haqiqatan buzuvchi o'zgarish uchun.


Versiyalash usullari

Usul Misol Xususiyati
URL segment /api/v2/mahsulotlar Eng aniq, brauzer va log da ko'rinadi, keng tarqalgan
Query string /api/mahsulotlar?api-version=2.0 URL tuzilmasi o'zgarmaydi
Header X-Api-Version: 2.0 URL toza; cache va brauzerda sinash qiyinroq
Media type Accept: application/json;v=2 "REST-ga eng to'g'ri", amalda kam

Asp.Versioning paketi

dotnet add package Asp.Versioning.Mvc
dotnet add package Asp.Versioning.Mvc.ApiExplorer
using Asp.Versioning;

builder.Services
    .AddApiVersioning(o =>
    {
        o.DefaultApiVersion = new ApiVersion(1, 0);
        o.AssumeDefaultVersionWhenUnspecified = true;
        o.ReportApiVersions = true;                         // api-supported-versions sarlavhasi
        o.ApiVersionReader = new UrlSegmentApiVersionReader();
    })
    .AddMvc()
    .AddApiExplorer(o =>
    {
        o.GroupNameFormat = "'v'VVV";
        o.SubstituteApiVersionInUrl = true;
    });

Controller lar

[ApiController]
[ApiVersion(1.0, Deprecated = true)]
[Route("api/v{version:apiVersion}/mahsulotlar")]
public class MahsulotlarV1Controller(MahsulotService s) : ControllerBase
{
    [HttpGet("{id:int}")]
    public async Task<MahsulotV1Dto?> Ol(int id, CancellationToken ct) =>
        (await s.OlAsync(id, ct))?.ToV1();          // eski format: narx — raqam
}

[ApiController]
[ApiVersion(2.0)]
[Route("api/v{version:apiVersion}/mahsulotlar")]
public class MahsulotlarV2Controller(MahsulotService s) : ControllerBase
{
    [HttpGet("{id:int}")]
    public async Task<MahsulotDto?> Ol(int id, CancellationToken ct) => await s.OlAsync(id, ct);
}
GET /api/v1/mahsulotlar/5  → { "narx": 3500000 }
GET /api/v2/mahsulotlar/5  → { "narx": { "summa": 3500000, "valyuta": "UZS" } }

Ikkala versiya — bitta servis va biznes mantiq; farq faqat DTO va mapping da.

Bitta controller da

[ApiVersion(1.0)]
[ApiVersion(2.0)]
[Route("api/v{version:apiVersion}/buyurtmalar")]
public class BuyurtmalarController : ControllerBase
{
    [HttpGet]
    public IActionResult Royxat() => ...;              // ikkala versiyada

    [HttpGet("statistika")]
    [MapToApiVersion(2.0)]
    public IActionResult Statistika() => ...;          // faqat v2
}

Minimal API

dotnet add package Asp.Versioning.Http
var versiyalar = app.NewApiVersionSet()
    .HasApiVersion(new ApiVersion(1, 0))
    .HasApiVersion(new ApiVersion(2, 0))
    .ReportApiVersions()
    .Build();

var g = app.MapGroup("/api/v{version:apiVersion}/mahsulotlar").WithApiVersionSet(versiyalar);
g.MapGet("/{id:int}", OlV1).MapToApiVersion(1, 0);
g.MapGet("/{id:int}", OlV2).MapToApiVersion(2, 0);

Eski versiyani bekor qilish

  1. Deprecated = true — javobda api-deprecated-versions: 1.0 sarlavhasi
  2. Hujjatda va mijozlarga e'lon: "v1 2027-yil 1-iyungacha ishlaydi"
  3. Log/metrikalarda v1 ishlatilishini kuzatish
  4. Muddat tugagach — 410 Gone yoki o'chirish

[!INFO] RFC 8594 Sunset sarlavhasi (Sunset: Tue, 01 Jun 2027 00:00:00 GMT) — mijozlarga endpoint qachon o'chirilishini mashina o'qiy oladigan shaklda bildiradi. Asp.Versioning da sunset siyosatini sozlash mumkin.


Tez-tez uchraydigan xatolar

Har kichik o'zgarishga yangi versiya — v1...v9, hammasini qo'llab-quvvatlash imkonsiz.

Biznes mantiqni versiyalar bo'yicha nusxalash — xato bir versiyada tuzatilib, boshqasida qoladi; mantiq — bitta, faqat kontrakt (DTO) versiyalanadi.

Versiyasiz boshlash — keyin qo'shish og'riqli; birinchi kundan /api/v1/.


Xulosa

Versiyalash — buzuvchi o'zgarishlarni eski mijozlarni sindirmasdan chiqarish. Buzuvchi: maydonni o'chirish/turini o'zgartirish, majburiy parametr; qo'shish — buzmaydi. Usullar: URL segment (eng keng tarqalgan), query, header, media type.

Asp.Versioning: AddApiVersioning, [ApiVersion], v{version:apiVersion} route, MapToApiVersion; Minimal API da NewApiVersionSet. Biznes mantiq — bitta, versiyalanadi faqat DTO. Eskisini bosqichma-bosqich: Deprecated, e'lon, kuzatish, o'chirish.


Keyingi dars

74-dars: Health Checks — /health endpoint, baza holati