# 2026'da ASP.NET Core Minimal API'ler: Mimari, Performans ve Mülakat Soruları > ASP.NET Core Minimal API'lere kapsamlı rehber - mimari, route grupları, endpoint filtreleri, Native AOT ve .NET geliştiricileri için kritik mülakat soruları. - Published: 2026-07-24 - Updated: 2026-07-24 - Author: SharpSkill - Reading time: 5 min --- ASP.NET Core Minimal API'ler, geleneksel MVC controller'larının gerektirdiği seremoniyi ortadan kaldırarak HTTP endpoint'leri oluşturmak için önemli ölçüde daha az boilerplate kod ile basitleştirilmiş bir yaklaşım sunar. .NET 6'da tanıtılan ve .NET 8, 9 ve şimdiki .NET 10 boyunca geliştirilen Minimal API'ler, mikroservisler, serverless fonksiyonlar ve hafif web servisleri için üretime hazır bir seçenek haline gelmiştir. > **Minimal API'ler vs Controller'lar** > > Minimal API'ler, doğrudan Program.cs içinde tanımlanan üst düzey route handler'larını kullanırken, MVC controller'ları sınıf tanımları, attribute'lar ve konvansiyon tabanlı routing gerektirir. Basit CRUD işlemleri veya 20'den az endpoint içeren mikroservisler için Minimal API'ler genellikle kodu %40-60 oranında azaltır. ## Minimal API Mimarisi ve İstek Pipeline'ı ASP.NET Core istek pipeline'ı, HTTP isteklerini endpoint handler'larına ulaşmadan önce middleware bileşenleri aracılığıyla işler. Minimal API'ler bu pipeline ile sorunsuz entegre olurken route tanımı için daha deklaratif bir sözdizimi sunar. ```csharp // Program.cs var builder = WebApplication.CreateBuilder(args); // Dependency injection için servisleri kaydet builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddScoped(); var app = builder.Build(); // Middleware pipeline yapılandırması app.UseExceptionHandler("/error"); app.UseHttpsRedirection(); app.UseAuthorization(); // Minimal API endpoint tanımları app.MapGet("/products", async (IProductRepository repo) => Results.Ok(await repo.GetAllAsync())); app.MapGet("/products/{id:int}", async (int id, IProductRepository repo) => await repo.GetByIdAsync(id) is Product product ? Results.Ok(product) : Results.NotFound()); app.Run(); ``` Bu pattern, dependency injection container'ına tam erişimi koruyarak route tanımlarını merkezileştirir. `IProductRepository` parametresi, handler delegate'lerine doğrudan constructor'sız enjeksiyonu gösterir. ## Route Grupları ve Endpoint Organizasyonu Uygulamalar büyüdükçe endpoint'lerin organizasyonu kritik hale gelir. .NET 7'de tanıtılan route grupları, minimal yaklaşımdan vazgeçmeden namespace'leme ve paylaşılan yapılandırma sağlar. ```csharp // ProductEndpoints.cs public static class ProductEndpoints { public static void MapProductEndpoints(this WebApplication app) { var group = app.MapGroup("/api/products") .WithTags("Products") .RequireAuthorization(); group.MapGet("/", GetAllProducts); group.MapGet("/{id:int}", GetProductById); group.MapPost("/", CreateProduct) .Accepts("application/json") .Produces(StatusCodes.Status201Created); group.MapPut("/{id:int}", UpdateProduct); group.MapDelete("/{id:int}", DeleteProduct) .RequireAuthorization("AdminOnly"); } private static async Task GetAllProducts( IProductRepository repo, CancellationToken ct) { var products = await repo.GetAllAsync(ct); return Results.Ok(products); } private static async Task GetProductById( int id, IProductRepository repo, CancellationToken ct) { var product = await repo.GetByIdAsync(id, ct); return product is not null ? Results.Ok(product) : Results.NotFound(); } private static async Task CreateProduct( CreateProductRequest request, IProductRepository repo, IValidator validator, CancellationToken ct) { var validation = await validator.ValidateAsync(request, ct); if (!validation.IsValid) return Results.ValidationProblem(validation.ToDictionary()); var product = await repo.CreateAsync(request.ToProduct(), ct); return Results.Created($"/api/products/{product.Id}", product); } } ``` Program.cs'de `app.MapProductEndpoints()` çağrısı, paylaşılan yetkilendirme gereksinimleri ve OpenAPI metadata'sı ile tüm ürün route'larını kaydeder. Bu yapı, yüzlerce endpoint içeren uygulamalar için iyi ölçeklenir. ## Parametre Bağlama ve Validasyon Minimal API'ler birden fazla bağlama kaynağını destekler: route parametreleri, query string'ler, header'lar, request body'leri ve DI'dan servisler. Bağlama önceliğini anlamak yaygın mülakat tuzaklarını önler. ```csharp // Program.cs - Parametre bağlama örnekleri app.MapGet("/search", ( [FromQuery] string? query, // Açık query string [FromQuery] int page = 1, // Varsayılan değer [FromQuery] int pageSize = 20, // Varsayılan değer [FromHeader(Name = "X-Correlation-Id")] string? correlationId, ILogger logger) => { logger.LogInformation("Arama isteği: {Query}, Sayfa: {Page}, CorrelationId: {CorrelationId}", query, page, correlationId); return Results.Ok(new { query, page, pageSize, correlationId }); }); // Validasyon ile karmaşık model bağlama app.MapPost("/orders", async ( [FromBody] CreateOrderRequest request, [FromServices] IValidator validator, [FromServices] IOrderService orderService, HttpContext context, CancellationToken ct) => { var validationResult = await validator.ValidateAsync(request, ct); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier); var order = await orderService.CreateOrderAsync(request, userId!, ct); return Results.Created($"/orders/{order.Id}", order); }); ``` `[FromBody]` attribute'u karmaşık tipler için isteğe bağlıdır ancak okunabilirliği artırır. FluentValidation, dependency injection aracılığıyla doğal olarak entegre olur ve validasyon mantığını endpoint handler'larından ayrı tutar. > **Bağlama Kaynağı Önceliği** > > Hiçbir attribute belirtilmediğinde, Minimal API'ler bağlama kaynaklarını çıkarsar: önce route parametreleri, ardından basit tipler için query string'ler ve karmaşık tipler için request body. [FromQuery] veya [FromBody] gibi açık attribute'lar bu davranışı geçersiz kılar. ## Native AOT ile Performans Optimizasyonu .NET 8, Minimal API'ler için Native AOT (Ahead-of-Time) derleme desteğini tanıttı ve milisaniyenin altında başlangıç süreleri ile bağımsız çalıştırılabilir dosyalar üretir. Bu yetenek, soğuk başlatma gecikmesinin önemli olduğu serverless dağıtımları için Minimal API'leri ideal kılar. ```csharp // Program.cs - AOT uyumlu yapılandırma var builder = WebApplication.CreateSlimBuilder(args); // AOT dostu JSON serileştirme builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default); }); var app = builder.Build(); app.MapGet("/health", () => Results.Ok(new HealthResponse("Healthy", DateTime.UtcNow))); app.Run(); // Kaynak tarafından üretilen JSON serileştirici bağlamı [JsonSerializable(typeof(HealthResponse))] [JsonSerializable(typeof(Product))] [JsonSerializable(typeof(List))] internal partial class AppJsonContext : JsonSerializerContext { } public record HealthResponse(string Status, DateTime CheckedAt); ``` `CreateSlimBuilder` metodu gereksiz framework özelliklerini hariç tutarken, kaynak tarafından üretilen `JsonSerializerContext` JSON serileştirme için çalışma zamanı reflection'ını ortadan kaldırır. Basit API'ler için yayınlanan AOT binary'leri genellikle standart bağımsız dağıtımlar için 80+ MB'a kıyasla 10-15 MB ölçer. ## Typed Results ve Yanıt Metadata'sı .NET 7, yanıt tiplerinin derleme zamanı doğrulaması için `TypedResults`'ı tanıttı, OpenAPI dokümantasyonu doğruluğunu artırdı ve geliştirme sırasında tip uyumsuzluklarını yakalamayı sağladı. ```csharp // OpenAPI metadata'sı ile güçlü tipli sonuçlar app.MapGet("/products/{id:int}", async Task, NotFound, ProblemHttpResult>> ( int id, IProductRepository repo, CancellationToken ct) => { try { var product = await repo.GetByIdAsync(id, ct); return product is not null ? TypedResults.Ok(product) : TypedResults.NotFound(); } catch (Exception ex) { return TypedResults.Problem( detail: "Ürün alınırken bir hata oluştu", statusCode: StatusCodes.Status500InternalServerError); } }) .WithName("GetProductById") .WithOpenApi(operation => { operation.Summary = "ID'ye göre ürün getirir"; operation.Description = "Ürün detaylarını döndürür veya bulunamazsa 404"; return operation; }); ``` `Results` birleşim tipi, Swagger/OpenAPI üreticilerinin doğru dokümantasyon üretmek için kullandığı tüm olası yanıt tiplerini bildirir. Bu pattern, API tasarımı hakkındaki mülakat sorularına hazırlanırken özellikle değerlidir. ## Kesişen İlgi Alanları için Endpoint Filtreleri Endpoint filtreleri, belirli endpoint'lere veya gruplara özgü middleware benzeri işlevsellik sağlar ve loglama, önbellekleme ve istek dönüşümü gibi konuları ele alır. ```csharp // ValidationFilter.cs public class ValidationFilter : IEndpointFilter where T : class { public async ValueTask InvokeAsync( EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var validator = context.HttpContext .RequestServices .GetService>(); if (validator is null) return await next(context); var argument = context.Arguments .OfType() .FirstOrDefault(); if (argument is null) return await next(context); var validationResult = await validator.ValidateAsync(argument); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } return await next(context); } } // Program.cs'de kullanım app.MapPost("/products", CreateProduct) .AddEndpointFilter>(); // Route grubu aracılığıyla global filtre kaydı var api = app.MapGroup("/api") .AddEndpointFilter(async (context, next) => { var logger = context.HttpContext .RequestServices .GetRequiredService>(); var stopwatch = Stopwatch.StartNew(); var result = await next(context); stopwatch.Stop(); logger.LogInformation( "Endpoint {Method} {Path} {ElapsedMs}ms'de tamamlandı", context.HttpContext.Request.Method, context.HttpContext.Request.Path, stopwatch.ElapsedMilliseconds); return result; }); ``` Filtreler kayıt sırasına göre çalışır, en içteki filtre endpoint handler'ına en yakın olanıdır. Bu mimari, validasyon, loglama ve yetkilendirme mantığının temiz ayrılmasını sağlar. ## Kimlik Doğrulama ve Yetkilendirme Pattern'leri Minimal API'ler, MVC controller'larıyla aynı kimlik doğrulama ve yetkilendirme mekanizmalarını daha deklaratif bir yapılandırma sözdizimi ile destekler. ```csharp // Program.cs - JWT kimlik doğrulama kurulumu builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)) }; }); builder.Services.AddAuthorizationBuilder() .AddPolicy("AdminOnly", policy => policy.RequireRole("Admin")) .AddPolicy("PremiumUser", policy => policy.RequireClaim("subscription", "premium", "enterprise")); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // Korumalı endpoint'ler app.MapGet("/admin/users", async (IUserService userService) => Results.Ok(await userService.GetAllUsersAsync())) .RequireAuthorization("AdminOnly"); app.MapGet("/profile", async (ClaimsPrincipal user, IUserService userService) => { var userId = user.FindFirstValue(ClaimTypes.NameIdentifier); var profile = await userService.GetProfileAsync(userId!); return Results.Ok(profile); }) .RequireAuthorization(); // Korumalı grup içinde anonim endpoint var protectedGroup = app.MapGroup("/api/secure") .RequireAuthorization(); protectedGroup.MapGet("/public-info", () => Results.Ok("Bu herkese açıktır")) .AllowAnonymous(); ``` `RequireAuthorization` uzantı metodu politika adlarını kabul eder veya herhangi bir kimlik doğrulanmış kullanıcı gerektirmek için argümansız çağrılabilir. Bu pattern'leri anlamak kimlik doğrulama ve yetkilendirme mülakat soruları için gereklidir. ## Mülakat Soruları: Yaygın Pattern'ler Teknik mülakatlar sıklıkla Minimal API'ler ile geleneksel controller'lar arasındaki farkları araştırır. Aşağıdaki tablo temel farklılıkları özetler: | Yön | Minimal API'ler | MVC Controller'lar | |-----|-----------------|--------------------| | Boilerplate | Düşük - doğrudan handler delegate'leri | Yüksek - sınıf + metod + attribute'lar | | Routing | `MapGet`, `MapPost` ile inline | Attribute veya konvansiyon tabanlı | | Model Bağlama | İsteğe bağlı attribute'larla otomatik | Konvansiyon + attribute'lar | | Filtreler | Endpoint filtreleri | Action filtreleri + middleware | | AOT Desteği | .NET 8'den beri tam | Sınırlı, reflection ağırlıklı | | Test Edilebilirlik | Fonksiyon tabanlı, kolay unit test | Controller instantiation gerektirir | | En İyi Kullanım | Mikroservisler, basit API'ler | Büyük uygulamalar, karmaşık iş akışları | > **Mülakat İpucu** > > "Minimal API'ler yerine controller'ları ne zaman tercih edersiniz?" sorusu sorulduğunda, action filtreleri, model bağlama özelleştirmesi veya yerleşik MVC pattern'leri olan mevcut kod tabanlarını gerektiren karmaşık uygulamalardan bahsedin. Minimal API'ler sıfırdan başlayan mikroservisler ve serverless fonksiyonlar için mükemmeldir. ## Minimal API Endpoint'lerini Test Etme `WebApplicationFactory` ile entegrasyon testi, uygulamayı dağıtmadan gerçekçi endpoint doğrulaması sağlar. ```csharp // ProductEndpointsTests.cs public class ProductEndpointsTests : IClassFixture> { private readonly HttpClient _client; private readonly WebApplicationFactory _factory; public ProductEndpointsTests(WebApplicationFactory factory) { _factory = factory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { // Gerçek repository'yi mock ile değiştir services.RemoveAll(); services.AddScoped(); }); }); _client = _factory.CreateClient(); } [Fact] public async Task GetProducts_ReturnsOkWithProductList() { // Act var response = await _client.GetAsync("/api/products"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var products = await response.Content .ReadFromJsonAsync>(); products.Should().NotBeNull(); products.Should().HaveCountGreaterThan(0); } [Fact] public async Task GetProductById_WithInvalidId_ReturnsNotFound() { // Act var response = await _client.GetAsync("/api/products/99999"); // Assert response.StatusCode.Should().Be(HttpStatusCode.NotFound); } [Fact] public async Task CreateProduct_WithValidRequest_ReturnsCreated() { // Arrange var request = new CreateProductRequest("Test Ürünü", 29.99m, "Test Açıklaması"); // Act var response = await _client.PostAsJsonAsync("/api/products", request); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); response.Headers.Location.Should().NotBeNull(); } } ``` .NET uygulamalarında temiz mimari pattern'lerinin daha derin incelenmesi için servis katmanı unit testlerle bağımsız olarak test edilmeli, entegrasyon testleri ise tam istek pipeline'ını doğrulamalıdır. ## Sonuç Minimal API'ler, ASP.NET Core'da HTTP servisleri oluşturmak için modern yaklaşımı temsil eder: - Route grupları ve endpoint organizasyonu basit mikroservislerden karmaşık uygulamalara ölçeklenir - Endpoint filtreleri validasyon ve loglama gibi kesişen ilgi alanlarının temiz ayrılmasını sağlar - TypedResults derleme zamanı yanıt tipi doğrulaması ve doğru OpenAPI dokümantasyonu sağlar - Native AOT derlemesi serverless dağıtımları için milisaniyenin altında başlangıç süreleri sunar - WebApplicationFactory ile entegrasyon testi gerçekçi endpoint doğrulaması sağlar Mülakat hazırlığı için, Minimal API'lerin geleneksel controller'lara kıyasla ne zaman uygun olduğunu ifade etmeye odaklanın ve istek pipeline'ı, dependency injection ve kimlik doğrulama pattern'leri hakkındaki anlayışınızı gösterin. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/dotnet/aspnet-core-minimal-apis-architecture-performance-interview