RESTful Web API Tasarım Rehberi

REST prensiplerine uygun Web API tasarımında kaynak (resource) odaklı yaklaşım, HTTP metodlarının doğru kullanımı, durumsuz (stateless) iletişim, URI tasarımı, versiyonlama, güvenlik, hata yönetimi ve dokümantasyon (OpenAPI) gibi temel konular ele alınır; .NET ile uygulama örnekleri verilir.

RESTful Web API Tasarım Rehberi

Web API Tasarımı ve RESTful Mimariler: Sağlam API'lar İnşa Etmek

Günümüz yazılım dünyasında, uygulamalar arası iletişimin büyük bir kısmı HTTP tabanlı Web API'leri üzerinden gerçekleşir. REST (Representational State Transfer), bu API'leri tasarlamak için en yaygın kullanılan mimari stildir. REST, bir dizi kısıtlama (constraint) ile sistemin ölçeklenebilirliğini, performansını ve bakım kolaylığını artırmayı hedefler. Bu yazıda, RESTful bir Web API'si tasarlamanın temel prensiplerini, HTTP metotlarının doğru kullanımını, URI tasarım kurallarını, güvenlik, versiyonlama, hata yönetimi ve dokümantasyon gibi kritik konuları .NET üzerinden örneklerle ele alacağız.


1. REST Nedir? Temel Prensipler

REST, Roy Fielding tarafından 2000 yılında doktora tezinde tanımlanmıştır. RESTful bir API, aşağıdaki altı temel kısıtlamayı (constraints) karşılamalıdır:

  • Client-Server (İstemci-Sunucu): İstemci ve sunucu birbirinden bağımsızdır. Sunucu, yalnızca API'yi sağlar; istemci ise bu API'yi tüketir. Bu, her iki tarafın da ayrı ayrı geliştirilmesine olanak tanır.

  • Stateless (Durumsuz): Sunucu, istemcinin durumunu (session) saklamaz. Her istek, kimlik doğrulama ve yetkilendirme için gerekli tüm bilgileri içermelidir. Bu, ölçeklenebilirliği artırır.

  • Cacheable (Önbelleklenebilir): Sunucu yanıtları, önbelleklenebilir olup olmadığını belirtmelidir. Bu, ağ trafiğini azaltır ve performansı artırır.

  • Uniform Interface (Tutarlı Arayüz): Tüm kaynaklara erişim aynı kurallarla yapılır (URI, HTTP metotları, medya tipleri). Bu, API'nin anlaşılabilirliğini ve kullanılabilirliğini artırır.

  • Layered System (Katmanlı Sistem): İstemci, API ile doğrudan iletişim kurduğunu düşünür; ancak arada güvenlik, yük dengeleme veya önbellekleme katmanları olabilir.

  • Code on Demand (Opsiyonel): Sunucu, istemciye çalıştırılabilir kod (ör. JavaScript) gönderebilir.

RESTful bir API, bu prensiplere uygun olarak kaynakları (resources) HTTP üzerinden sunar.


2. Kaynak (Resource) Odaklı Tasarım

REST'in merkezinde kaynaklar (resources) vardır. Kaynak, bir varlığı (entity) veya bir hizmeti temsil eder. Her kaynağın bir URI (Uniform Resource Identifier) ile tanımlanan bir adresi vardır.

Kaynak İsimlendirme Kuralları:

  • Çoğul isimler kullanın: api/users, api/products, api/orders (Tekil kullanmayın: api/user).

  • Alt kaynaklar (nested resources): İlişkili kaynakları URI içinde gösterin. Örn: api/users/{userId}/orders.

  • Hiyerarşiyi koruyun: Fazla derin iç içe URI'lerden kaçının. Maksimum 3 seviye önerilir.

  • Sürüm bilgisini URI'ye ekleyin: api/v1/users, api/v2/products.

  • Eylemleri (action) URI'de kullanmayın: URI'ler fiil değil, isim içermelidir.

Kötü URI Örnekleri:

  • api/getUsers

  • api/createUser

  • api/updateUser/123

  • api/deleteOrder/456

İyi URI Örnekleri:

  • api/users (GET)

  • api/users (POST)

  • api/users/123 (PUT, PATCH, DELETE)

  • api/users/123/orders (GET)


3. HTTP Metotları (Verbs) ve Anlamları

REST, HTTP metotlarını standart anlamlarıyla kullanır:

HTTP Metodu Açıklama Idempotent? Güvenli? Örnek Kullanım
GET Kaynağı okur (sorgular). ✅ Evet ✅ Evet GET /api/users/123
POST Yeni bir kaynak oluşturur. ❌ Hayır ❌ Hayır POST /api/users
PUT Kaynağın tamamını günceller (veya yoksa oluşturur). ✅ Evet ❌ Hayır PUT /api/users/123
PATCH Kaynağın belirli alanlarını günceller. ❌ Hayır (genelde) ❌ Hayır PATCH /api/users/123
DELETE Kaynağı siler. ✅ Evet ❌ Hayır DELETE /api/users/123
HEAD GET ile aynı, ancak yanıt gövdesi (body) yok. ✅ Evet ✅ Evet Kaynak varlığını kontrol etmek
OPTIONS Kaynak için desteklenen metotları döner. ✅ Evet ✅ Evet CORS preflight

Idempotent (Yinelenebilir): Aynı isteğin birden fazla kez gönderilmesinin, tek bir kez gönderilmesiyle aynı sonucu doğurmasıdır. GET, PUT, DELETE, HEAD, OPTIONS idempotenttir. POST ve PATCH genelde idempotent değildir.

Güvenli (Safe): Kaynağın durumunu değiştirmeyen metotlardır. GET, HEAD, OPTIONS güvenlidir.


4. Stateless (Durumsuz) İletişim

Sunucu, istemci durumunu (session) saklamaz. Her istek, gerekli tüm kimlik doğrulama bilgilerini (token, API key) içermelidir. Bu, sunucunun ölçeklenebilirliğini artırır.

JWT (JSON Web Token) ile Kimlik Doğrulama:

csharp

// JWT token üretimi
public string GenerateToken(string userId)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Secret"]);
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, userId) }),
        Expires = DateTime.UtcNow.AddHours(1),
        Issuer = _configuration["Jwt:Issuer"],
        Audience = _configuration["Jwt:Audience"],
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
    };
    var token = tokenHandler.CreateToken(tokenDescriptor);
    return tokenHandler.WriteToken(token);
}

// Her istekte token'ı doğrula (Middleware ile)
app.UseAuthentication();
app.UseAuthorization();

5. Versiyonlama (Versioning)

API'ler zamanla değişir. Versiyonlama, eski istemcilerin çalışmaya devam etmesini sağlar.

Yaygın Versiyonlama Stratejileri:

  1. URI Path: api/v1/users ve api/v2/users.

  2. Query Parameter: api/users?version=1.

  3. Header: api-version: 1.0.

  4. Content Negotiation: Accept: application/vnd.myapi.v1+json.

.NET'te URI Path Versiyonlama (En Yaygın):

csharp

// Program.cs
builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true;
});

// Controller
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class UsersController : ControllerBase
{
    [HttpGet("{id}")]
    [MapToApiVersion("1.0")]
    public IActionResult GetV1(int id) { ... }

    [HttpGet("{id}")]
    [MapToApiVersion("2.0")]
    public IActionResult GetV2(int id) { ... }
}

6. Hata Yönetimi (Error Handling)

Hata durumlarında, anlamlı HTTP status kodları ve hata detayları döndürülmelidir.

Standart HTTP Status Kodları:

Status Code Anlamı Açıklama
200 OK Başarılı İstek başarıyla tamamlandı.
201 Created Oluşturuldu POST başarılı, yeni kaynak oluşturuldu.
204 No Content İçerik yok Silme işlemi başarılı, body yok.
400 Bad Request Geçersiz istek İstek formatı veya veri doğrulama hatası.
401 Unauthorized Yetkisiz Kimlik doğrulama gerekli veya başarısız.
403 Forbidden Yasak Kimlik doğrulandı ancak yetkisi yok.
404 Not Found Bulunamadı Kaynak mevcut değil.
405 Method Not Allowed Metot izin verilmiyor HTTP metodu desteklenmiyor.
409 Conflict Çakışma Kaynak zaten var (unique constraint).
422 Unprocessable Entity İşlenemez varlık İsteğin formatı doğru ancak iş mantığı başarısız.
500 Internal Server Error Sunucu hatası Beklenmedik hata.

.NET'te Global Exception Handler ile Hata Yönetimi:

csharp

// Middleware
public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Beklenmedik hata!");
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var response = new
        {
            status = "error",
            message = exception.Message,
            traceId = context.TraceIdentifier
        };

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = exception switch
        {
            NotFoundException => StatusCodes.Status404NotFound,
            ValidationException => StatusCodes.Status400BadRequest,
            UnauthorizedException => StatusCodes.Status401Unauthorized,
            _ => StatusCodes.Status500InternalServerError
        };

        return context.Response.WriteAsync(JsonSerializer.Serialize(response));
    }
}

7. Dokümantasyon (OpenAPI / Swagger)

API dokümantasyonu, hem geliştiriciler hem de otomasyon araçları (Postman, SDK üreteçleri) için hayati önem taşır. OpenAPI (eski adıyla Swagger) bu konuda standarttır.

.NET 8+ ile Swagger Entegrasyonu:

csharp

// Program.cs
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Description = "Please enter token",
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        BearerFormat = "JWT",
        Scheme = "bearer"
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty<string>() }
    });
});

app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"));

8. Güvenlik (Security)

API güvenliği, aşağıdaki katmanlarda sağlanır:

  • Kimlik Doğrulama (Authentication): Kullanıcının kim olduğunu doğrular. (JWT, API Key, OAuth2)

  • Yetkilendirme (Authorization): Kullanıcının hangi kaynaklara erişebileceğini belirler. (RBAC, Policy-based)

  • HTTPS: Tüm istekler HTTPS üzerinden yapılmalıdır.

  • Rate Limiting: Aşırı kullanımı önlemek için istek sınırlaması.

  • CORS: Sadece belirli kaynaklara (origins) erişim izni verin.

.NET'te Policy-based Yetkilendirme:

csharp

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
    options.AddPolicy("UserOrAdmin", policy => policy.RequireClaim("permission", "can_read_users"));
});

[Authorize(Policy = "AdminOnly")]
[HttpGet("admin-data")]
public IActionResult GetAdminData() { ... }

9. Performans (Performance)

  • Pagination (Sayfalama): Büyük veri kümelerini sayfalara bölerek gönderin.
    GET /api/products?page=1&pageSize=20

  • Filtering (Filtreleme): Belirli kriterlere göre veri döndürün.
    GET /api/products?category=electronics&minPrice=100

  • Sorting (Sıralama): Sıralama seçeneği sunun.
    GET /api/products?sort=price_desc

  • Selecting (Alan Seçimi): Sadece istenen alanları döndürün.
    GET /api/products?fields=id,name,price

  • Caching (Önbellekleme): Sık değişmeyen verileri önbelleğe alın.
    Cache-Control: public, max-age=300

  • GZIP/Deflate Sıkıştırma: Yanıt boyutunu küçültün.

.NET'te Pagination Örneği:

csharp

[HttpGet]
public async Task<IActionResult> GetProducts([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
{
    var query = _context.Products.AsNoTracking();
    var totalCount = await query.CountAsync();
    var items = await query
        .Skip((page - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();

    var response = new PagedResponse<Product>
    {
        Items = items,
        Page = page,
        PageSize = pageSize,
        TotalCount = totalCount,
        TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
    };

    return Ok(response);
}

10. En İyi Pratikler ve Kaçınılması Gerekenler

Pratik Açıklama
Kaynaklara Uygun HTTP Metodu Kullanın POST yerine PUT/PATCH, GET yerine POST kullanmayın.
Status Kodlarını Doğru Kullanın 200, 201, 400, 404, 500... Anlamlarına uygun kullanın.
API Versiyonlamasını İhmal Etmeyin Yeni değişiklikler eski istemcileri bozmasın.
Dokümantasyonu Güncel Tutun Swagger/OpenAPI ile otomatik dokümantasyon sağlayın.
Validasyonu (Doğrulama) Yapın Gelen veriyi her zaman doğrulayın.
Hata Mesajlarını Anlamlı Kılın Sadece "Hata oluştu" demeyin, detay verin.
Loglama ve İzleme (Monitoring) Yapın API'yi sürekli izleyin, hataları loglayın.
Güvenlik Açıklarını Kapatın HTTPS, CORS, Rate Limiting, Input Validation.
Performansı Ölçün ve Optimize Edin Yavaş sorguları tespit edip optimize edin.
API'nizi Tüketilebilir Hale Getirin Açık, anlaşılır ve tutarlı bir tasarım yapın.

Sonuç:

RESTful Web API tasarımı, disiplinli bir yaklaşım ve standartlara bağlı kalmayı gerektirir. Kaynak odaklı URI tasarımı, HTTP metotlarının doğru kullanımı, durumsuz iletişim, etkili hata yönetimi, versiyonlama, güvenlik ve performans optimizasyonu, başarılı bir API'nin temel yapı taşlarıdır.

.NET Core ve ASP.NET Core, bu prensipleri uygulamak için güçlü araçlar ve kütüphaneler sunar (Swagger, JWT, API Versioning, Global Exception Handling, Pagination). Doğru tasarlanmış bir API, hem geliştirme sürecini hızlandırır hem de uzun vadeli bakım maliyetini düşürür.

Unutmayın: İyi bir API, kullanıcılarının (geliştiricilerin) hayatını kolaylaştırır; kötü bir API ise onları başka çözümlere yönlendirir.

Tüm yazılar