Ana içeriğe geç

API Gateway

API Gateway, microservice mimarisinde istemci ile servisler arasında tek giriş noktası sağlar; yanlış yapılandırma performans darboğazı ve tek nokta hatalarına yol açar.


1. Gateway İçinde İş Mantığı

Yanlış Kullanım: API Gateway’de iş mantığı yazmak.

app.MapGet("/api/orders/{id}", async (int id, HttpClient orderClient, HttpClient customerClient) =>
{
    var order = await orderClient.GetFromJsonAsync<Order>($"api/orders/{id}");
    var customer = await customerClient.GetFromJsonAsync<Customer>($"api/customers/{order.CustomerId}");

    // İş mantığı gateway'de
    if (order.Total > 10000 && !customer.IsPremium)
        return Results.BadRequest("Limit aşıldı");

    order.CustomerName = customer.Name;
    return Results.Ok(order);
});

İdeal Kullanım: Gateway sadece yönlendirme ve cross-cutting concern yönetsin.

// YARP reverse proxy konfigürasyonu
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

app.MapReverseProxy();

// appsettings.json
{
    "ReverseProxy": {
        "Routes": {
            "orders-route": {
                "ClusterId": "orders-cluster",
                "Match": { "Path": "/api/orders/{**catch-all}" }
            }
        },
        "Clusters": {
            "orders-cluster": {
                "Destinations": {
                    "orders-service": { "Address": "https://order-service/" }
                }
            }
        }
    }
}

2. Rate Limiting Yapmamak

Yanlış Kullanım: Gateway’de rate limiting olmadan servisleri açık bırakmak.

app.MapReverseProxy(); // Sınırsız istek alabilir, downstream servisler ezilir

İdeal Kullanım: Rate limiting ile servisleri koruyun.

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("api-limit", limiter =>
    {
        limiter.PermitLimit = 100;
        limiter.Window = TimeSpan.FromMinutes(1);
        limiter.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        limiter.QueueLimit = 10;
    });

    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});

app.UseRateLimiter();

app.MapReverseProxy().RequireRateLimiting("api-limit");

3. Response Aggregation Yapmamak

Yanlış Kullanım: İstemciden birden fazla servis çağrısı yaptırmak.

// İstemci tarafında
var order = await httpClient.GetAsync("/api/orders/1");
var customer = await httpClient.GetAsync("/api/customers/5");
var shipping = await httpClient.GetAsync("/api/shipping/order/1");
// 3 ayrı HTTP çağrısı, yüksek latency

İdeal Kullanım: Gateway’de BFF pattern ile aggregation yapın.

app.MapGet("/api/bff/order-details/{orderId}", async (
    int orderId,
    IOrderServiceClient orderClient,
    ICustomerServiceClient customerClient,
    IShippingServiceClient shippingClient) =>
{
    var orderTask = orderClient.GetAsync(orderId);
    var shippingTask = shippingClient.GetByOrderAsync(orderId);

    var order = await orderTask;
    var customerTask = customerClient.GetAsync(order.CustomerId);

    await Task.WhenAll(customerTask, shippingTask);

    return Results.Ok(new OrderDetailsResponse
    {
        Order = order,
        Customer = customerTask.Result,
        Shipping = shippingTask.Result
    });
});

4. Health Check Proxy Yapmamak

Yanlış Kullanım: Downstream servislerin sağlık durumunu kontrol etmemek.

app.MapHealthChecks("/health"); // Sadece gateway'in durumunu kontrol eder

İdeal Kullanım: Downstream servislerin sağlığını da izleyin.

builder.Services.AddHealthChecks()
    .AddUrlGroup(new Uri("https://order-service/health"), name: "order-service",
        failureStatus: HealthStatus.Degraded)
    .AddUrlGroup(new Uri("https://customer-service/health"), name: "customer-service",
        failureStatus: HealthStatus.Degraded)
    .AddUrlGroup(new Uri("https://payment-service/health"), name: "payment-service",
        failureStatus: HealthStatus.Unhealthy);

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

5. Authentication’ı Her Serviste Tekrarlamak

Yanlış Kullanım: Her microservice’de ayrı ayrı JWT doğrulaması yapmak.

// Order Service
builder.Services.AddAuthentication().AddJwtBearer(/* config */);
// Customer Service
builder.Services.AddAuthentication().AddJwtBearer(/* aynı config */);
// Payment Service
builder.Services.AddAuthentication().AddJwtBearer(/* aynı config */);

İdeal Kullanım: Gateway’de merkezi authentication yapın, servislere identity bilgisini header ile iletin.

// API Gateway
builder.Services.AddAuthentication().AddJwtBearer(options =>
{
    options.Authority = "https://identity-server/";
    options.Audience = "api-gateway";
});

app.Use(async (context, next) =>
{
    if (context.User.Identity?.IsAuthenticated == true)
    {
        context.Request.Headers["X-User-Id"] = context.User.FindFirst("sub")?.Value;
        context.Request.Headers["X-User-Role"] = context.User.FindFirst("role")?.Value;
    }
    await next();
});

// Downstream servislerde sadece header kontrol edilir
public class UserContext : IUserContext
{
    private readonly IHttpContextAccessor _accessor;
    public string UserId => _accessor.HttpContext?.Request.Headers["X-User-Id"];
    public string Role => _accessor.HttpContext?.Request.Headers["X-User-Role"];
}