• 25.08.2026 09:26:35
  • Admin Admin

ASP.NET Core API’lerinde endpoint filter, ProblemDetails ve ölçümleme kullanarak doğrulamayı uç noktaya yakın, test edilebilir ve gözlemlenebilir kurma yaklaşımı. .net core eğitimi için üretim odaklı bir rehber.

ASP.NET Core Eğitimi: Endpoint Filter ile Sınırda Doğrulama Tasarımı

ASP.NET Core Eğitimi: Doğrulama Neden Controller’dan Önce Başlamalı?

Bir API’de boş müşteri numarası, geçersiz para birimi veya sınırı aşan sayfa boyutu gibi istek hatalarını handler içine taşırsanız, her handler aynı koruma kodunu yeniden uygular ve geçersiz istekler gereksiz bağımlılık çözümleme, veritabanı sorgusu ya da mesaj yayınlama riski doğurur. Minimal API tarafında endpoint filter, model bağlama tamamlandıktan sonra ama endpoint delegate’i çalışmadan önce devreye girer. Bu konum, HTTP sınırı kurallarını uygulama kullanım senaryolarından ayırmak için uygundur.

Örneğin aşağıdaki kayıt, sipariş oluşturma sözleşmesini route group düzeyinde bağlar. ValidationFilter<CreateOrderRequest>, yalnızca ilgili request tipi endpoint argümanlarında varsa çalışır; böylece health check veya dosya yükleme endpoint’lerine gereksiz yansıma maliyeti uygulanmaz.

var orders = app.MapGroup("/api/orders")
    .AddEndpointFilter<ValidationFilter<CreateOrderRequest>>();

orders.MapPost("/", async (
    CreateOrderRequest request,
    IOrderService service,
    CancellationToken ct) =>
{
    var id = await service.CreateAsync(request, ct);
    return Results.Created($"/api/orders/{id}", new { id });
});

public sealed record CreateOrderRequest(
    string CustomerId,
    string Currency,
    IReadOnlyList<CreateOrderLine> Lines);

public sealed record CreateOrderLine(string Sku, int Quantity);

Buradaki kritik incelik şudur: filter, iş kuralı doğrulamasının tamamının yeri değildir. Stokta ürün olup olmadığı veya müşterinin kredi limiti gibi bilgiler zamanla değişir ve transaction sınırında tekrar kontrol edilmelidir. Filter’da yalnızca deterministik sözleşme kurallarını; örneğin maksimum 100 satır, ISO para birimi biçimi ve pozitif adet kontrollerini tutun. Bu ayrım, aynı command’ın HTTP dışındaki bir consumer tarafından çalıştırılmasında da kuralların kaybolmasını engeller.

C# Eğitimi İçin Generic Endpoint Filter ve ProblemDetails Uygulaması

Aşağıdaki örnek FluentValidation’ın IValidator<T> arayüzünü kullanır ve başarısızlıkları RFC 7807 biçiminde döndürür. ToDictionary ile aynı alan için birden fazla hatayı dizi halinde korumak önemlidir; tek bir hata mesajına indirgemek, istemcinin bütün form hatalarını tek turda gösterememesine neden olur.

using FluentValidation;
using Microsoft.AspNetCore.Mvc;

public sealed class ValidationFilter<T> : IEndpointFilter where T : class
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next)
    {
        var request = context.Arguments.OfType<T>().FirstOrDefault();
        if (request is null) return await next(context);

        var validator = context.HttpContext.RequestServices
            .GetService<IValidator<T>>();
        if (validator is null) return await next(context);

        var result = await validator.ValidateAsync(
            request, context.HttpContext.RequestAborted);

        if (result.IsValid) return await next(context);

        var errors = result.Errors
            .GroupBy(x => x.PropertyName)
            .ToDictionary(
                x => x.Key,
                x => x.Select(e => e.ErrorMessage).Distinct().ToArray());

        return Results.ValidationProblem(errors,
            title: "Request contract validation failed",
            statusCode: StatusCodes.Status400BadRequest);
    }
}

public sealed class CreateOrderRequestValidator
    : AbstractValidator<CreateOrderRequest>
{
    public CreateOrderRequestValidator()
    {
        RuleFor(x => x.CustomerId).NotEmpty().MaximumLength(64);
        RuleFor(x => x.Currency).Matches("^[A-Z]{3}$");
        RuleFor(x => x.Lines).NotEmpty().Must(x => x.Count <= 100);
        RuleForEach(x => x.Lines).ChildRules(line =>
        {
            line.RuleFor(x => x.Sku).NotEmpty().MaximumLength(40);
            line.RuleFor(x => x.Quantity).InclusiveBetween(1, 999);
        });
    }
}

builder.Services.AddValidatorsFromAssemblyContaining<Program>();

Filter içinde RequestServices.GetService kullanımı endpoint başına validator çözümlemesi yapar. Validator’lar stateless ise DI kaydını singleton yapma fikri cazip görünür; ancak validator içine scoped bir repository enjekte edilmişse bu yaşam döngüsü hatalıdır. FluentValidation kayıtlarını varsayılan transient yaşam döngüsünde bırakın veya bağımlılık ağacını gerçekten singleton-güvenli olacak şekilde ayrı bir validator tasarlayın. Bu ayrıntı, testte görünmeyip üretimde disposed scoped service hatasına dönüşebilen yaygın bir sorundur.

Microsoft Teknolojileri Eğitimi: Hata Sözleşmesini Tek Biçimde Tutmak

Validation hatası 400 iken domain’de bulunamayan kaynak 404, eşzamanlı güncelleme çakışması ise çoğu API’de 409 dönmelidir. Bunları her endpoint’te try/catch ile çevirmek yerine merkezi exception handler kullanın. ASP.NET Core’un IExceptionHandler mekanizması, loglama ve ProblemDetails üretimini tek bir yerde toplar; endpoint kodu yalnızca kullanım senaryosuna odaklanır.

public sealed class DomainExceptionHandler(
    ILogger<DomainExceptionHandler> logger,
    IProblemDetailsService problemDetails) : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(
        HttpContext http,
        Exception exception,
        CancellationToken ct)
    {
        var (status, title) = exception switch
        {
            OrderNotFoundException => (404, "Order was not found"),
            OrderStateConflictException => (409, "Order state conflict"),
            _ => (500, "Unexpected server error")
        };

        logger.LogError(exception, "Request {TraceId} failed", http.TraceIdentifier);

        return await problemDetails.TryWriteAsync(new ProblemDetailsContext
        {
            HttpContext = http,
            ProblemDetails = new ProblemDetails
            {
                Status = status,
                Title = title,
                Type = $"https://api.example.com/problems/{status}"
            },
            Exception = exception
        });
    }
}

builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
        ctx.ProblemDetails.Extensions["traceId"] = ctx.HttpContext.TraceIdentifier);
builder.Services.AddExceptionHandler<DomainExceptionHandler>();
app.UseExceptionHandler();

traceId istemciye verilebilir, fakat exception mesajını veya stack trace’i ProblemDetails içine eklemeyin: bunlar tablo adları, iç URL’ler ya da erişim belirteçleri sızdırabilir. Log tarafında aynı trace kimliğiyle exception tutulduğundan, operasyon ekibi istemcinin bildirdiği kimlik üzerinden arama yapabilir. Bu yapı, csharp eğitimi ve c# eğitimi içeriklerinde çoğu zaman atlanan HTTP hata semantiği ile tanılama arasındaki pratik bağdır.

.NET Core Eğitimi: Filter Maliyetini Varsaymak Yerine Ölçmek

Doğrulama filter’ı eklemek her endpoint’i otomatik olarak yavaşlatmaz; maliyet request gövdesinin bağlanması, validator kuralları, DI çözümleme ve hata serileştirmesinden gelir. Önce filter kapalı ve açık iki deploy edilebilir yapı üretin. Her iki yapıyı aynı container kaynak sınırları, aynı TLS sonlandırma noktası ve aynı örnek veriyle çalıştırın. K6 ile yalnızca başarılı istekleri değil, özellikle erken reddedilen hatalı istekleri de ayrı senaryoda ölçün.

import http from 'k6/http';
import { check } from 'k6';

export const options = {
  scenarios: {
    valid: { executor: 'constant-vus', vus: 40, duration: '60s' },
    invalid: { executor: 'constant-vus', vus: 40, duration: '60s',
               startTime: '70s' }
  }
};

export default function () {
  const invalid = __VU % 2 === 0;
  const payload = invalid
    ? JSON.stringify({ customerId: '', currency: 'tl', lines: [] })
    : JSON.stringify({ customerId: 'c-42', currency: 'TRY',
        lines: [{ sku: 'SKU-1', quantity: 2 }] });

  const response = http.post('http://localhost:8080/api/orders', payload,
    { headers: { 'Content-Type': 'application/json' } });
  check(response, { 'expected status': r => r.status === (invalid ? 400 : 201) });
}

Önce-sonra karşılaştırmasında p50 tek başına yeterli değildir: http_req_duration p95/p99, 400 ve 201 oranı, işlem başına allocation ve GC sayısını birlikte kaydedin. Uygulama çalışırken dotnet-counters monitor --process-id <pid> System.Runtime ile alloc-rate, Gen0/Gen2 GC ve thread pool queue length izlenebilir; ayrıntılı CPU/allocation kök nedeni için dotnet-trace collect --process-id <pid> --providers Microsoft-DotNETCore-SampleProfiler alın ve PerfView veya Visual Studio’da açın. Filter sonrası yalnızca geçersiz trafikte p95 düşerken geçerli trafikte allocation belirgin artıyorsa, LINQ ile her istekte sözlük üretmek yerine hata sözlüğünü yalnızca başarısız dalda oluşturduğunuzu doğrulayın; yukarıdaki kod bu nedenle GroupBy işlemini result.IsValid kontrolünün sonrasına koyar.

Entity Framework Eğitimi ve Blazor Eğitimi Bağlamında Sözleşme Sınırları

Entity Framework eğitimi sırasında sık görülen hata, HTTP request DTO’sunu doğrudan EF entity’sine bağlayıp DbContext.Add(request) çağırmaktır. Bu yaklaşım istemcinin navigation property veya yönetimsel alanları göndermesine kapı açar. Endpoint filter yalnızca DTO’nun biçimini doğrular; handler ise izinli alanları açıkça entity’ye eşlemelidir. Örneğin CreatedAt değerini istemciden almamak ve satırları whitelist ile dönüştürmek, over-posting riskini somut olarak azaltır.

var order = new Order
{
    CustomerId = request.CustomerId,
    Currency = request.Currency,
    CreatedAtUtc = TimeProvider.System.GetUtcNow()
};

foreach (var line in request.Lines)
    order.Lines.Add(new OrderLine { Sku = line.Sku, Quantity = line.Quantity });

await db.Orders.AddAsync(order, ct);
await db.SaveChangesAsync(ct);

Blazor eğitimi tarafında aynı FluentValidation kurallarını doğrudan istemciye taşımak yerine API’nin 400 ProblemDetails yanıtını alan bazında eşleyin; istemci doğrulaması yalnızca hızlı geri bildirimdir, otorite değildir. Özel bir HttpClient çağrısında ValidationProblemDetails.Errors alanını deserialize edip ValidationMessageStore içine ekleyin. Böylece UI, sunucunun kabul ettiği sözleşmeyle tutarlı kalır. Bu yaklaşım .net core kursu, csharp kursu, c# kursu, asp.net core eğitimi ve blazor eğitimi programlarında aynı API sınırı ilkesini farklı katmanlara uygulamak için kullanılabilir.

İlgili Eğitim

.NET Core Eğitimi

Sık Sorulan Sorular

asp.net core eğitimi kapsamında endpoint filter mı middleware mi kullanmalıyım?

Kimlik doğrulama, correlation ID veya tüm HTTP trafiğine uygulanan header politikaları için middleware kullanın. Request DTO’su bağlandıktan sonra çalışan alan doğrulaması için endpoint filter seçin. Middleware, Minimal API handler argümanlarını görmez; filter ise EndpointFilterInvocationContext.Arguments üzerinden CreateOrderRequest gibi bağlanmış nesneye erişir.

c# eğitimi için FluentValidation hatalarını ProblemDetails olarak nasıl döndürürüm?

Validator sonucundaki Failure kayıtlarını PropertyName’e göre gruplayıp Results.ValidationProblem(errors) çağırın. Aynı property için birden fazla mesajı string dizisi olarak koruyun. İstemci 400 yanıtında errors.customerId veya errors.lines[0].quantity anahtarlarını doğrudan form alanlarına eşleyebilir.

entity framework eğitimi sırasında API DTO’sunu EF Core entity’sine neden doğrudan bağlamamalıyım?

İstemci entity üzerinde dışarı açılmaması gereken CreatedAtUtc, tenant kimliği veya navigation collection alanlarını gönderebilir. DTO’dan yeni entity üretip yalnızca CustomerId, Currency, Sku ve Quantity gibi izinli alanları atayın; tenant ve zaman damgasını sunucu tarafında belirleyin.

.net core kursu projelerinde endpoint filter gecikmesini hangi araçla ölçebilirim?

İki aynı yapılandırmalı sürümü k6 ile 60 saniyelik sabit VU senaryosunda karşılaştırın; p95/p99 ve hata oranını kaydedin. Ardından dotnet-counters ile allocation rate ve GC sayaçlarını, CPU kök nedenini görmek için dotnet-trace ile profiler örneklerini toplayın. Geçerli ve geçersiz payload’ları ayrı k6 senaryoları olarak çalıştırın.

AI / LLM Discovery

Bu makale Opendart Akademi Microsoft / C# eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt