ASP.NET Core Pipeline'ını Özelleştirmek: Middleware ve Action Filter'lar
ASP.NET Core'un en güçlü yanlarından biri, HTTP isteklerinin işlenişine müdahale edebilme esnekliğidir. Bu iki temel mekanizma vardır: Middleware (tüm istekler için genel) ve Action Filters (Controller/Action özelinde). Bunları doğru kullanmak, kod tekrarını önler, cross-cutting concern'leri (loglama, validasyon, hata yönetimi) temiz bir şekilde ayrıştırır.
1. Middleware: Pipeline'ın İlk Kapısı
Middleware, istek (request) ve yanıt (response) akışında her istek için çalışan ardışık (pipeline) bileşenlerdir. Her middleware, bir sonraki middleware'i çağırabilir (next) veya pipeline'ı kısa devre yaparak doğrudan yanıt dönebilir.
Basit Bir Middleware Örneği (Loglama):
csharp
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// İSTEK GELİRKEN - ÖN İŞLEM
_logger.LogInformation($"Request: {context.Request.Method} {context.Request.Path}");
// Bir sonraki middleware'i çağır (pipeline'da ilerle)
await _next(context);
// YANIT DÖNERKEN - SON İŞLEM
_logger.LogInformation($"Response: {context.Response.StatusCode}");
}
}
// Program.cs / Startup.cs'ye ekleme:
app.UseMiddleware<RequestLoggingMiddleware>();
// veya extension metot ile:
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
=> builder.UseMiddleware<RequestLoggingMiddleware>();
Middleware Sıralaması Çok Önemlidir!
-
UseExceptionHandler()en üstte olmalı (tüm hataları yakalamak için). -
UseAuthentication()kimlik doğrulama için. -
UseAuthorization()yetkilendirme için. -
UseEndpoints()en sonda.
Kısa Devre (Short-Circuit) Örneği:
Diyelim ki /health endpoint'ine gelen isteklerin pipeline'ın geri kalanına gitmesini istemiyorsunuz.
csharp
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/health"))
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("Healthy");
return; // next çağrılmaz, pipeline burada biter!
}
await _next(context);
}
2. Action Filter'lar: Controller/Action Özelinde Müdahale
Action Filter'lar, middleware'den farklı olarak MVC/WebAPI katmanında çalışır ve yalnızca belirli Controller veya Action'lara uygulanabilir. 5 tür filter vardır:
-
Authorization Filter (En önce çalışır, yetkilendirme)
-
Resource Filter (Model binding öncesi/sonrası, cache için)
-
Action Filter (Action metodu öncesi/sonrası)
-
Exception Filter (Action içinde fırlayan hataları yakalar)
-
Result Filter (ActionResult oluşturulduktan sonra, yanıt yazılmadan önce)
Örnek: Performans Loglama Action Filter (IAsyncActionFilter)
csharp
public class PerformanceLoggingFilter : IAsyncActionFilter
{
private readonly ILogger<PerformanceLoggingFilter> _logger;
public PerformanceLoggingFilter(ILogger<PerformanceLoggingFilter> logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var stopwatch = Stopwatch.StartNew();
// Action öncesi
_logger.LogInformation($"Action {context.ActionDescriptor.DisplayName} başlıyor.");
// Bir sonraki filter veya action'ı çalıştır
var executedContext = await next();
// Action sonrası
stopwatch.Stop();
_logger.LogInformation($"Action tamamlandı. Süre: {stopwatch.ElapsedMilliseconds}ms");
}
}
// Controller/action üzerinde kullanım:
[ServiceFilter(typeof(PerformanceLoggingFilter))]
public IActionResult Get(int id) { ... }
// Veya global olarak (tüm action'lar için):
builder.Services.AddControllers(options =>
{
options.Filters.Add<PerformanceLoggingFilter>();
});
Exception Filter ile Hata Yakalama:
csharp
public class CustomExceptionFilter : IExceptionFilter
{
private readonly ILogger<CustomExceptionFilter> _logger;
public void OnException(ExceptionContext context)
{
_logger.LogError(context.Exception, "Action hatası!");
// Özel bir hata yanıtı dön:
context.Result = new ObjectResult(new { error = "Bir hata oluştu." })
{
StatusCode = 500
};
context.ExceptionHandled = true; // Exception'ı bastır
}
}
3. Middleware vs Action Filter: Ne Zaman Hangisi?
| Özellik | Middleware | Action Filter |
|---|---|---|
| Kapsam | Tüm istekler (statik dosyalar dahil) | Sadece MVC/WebAPI action'ları |
| Erişim | HttpContext üzerinden ham veri |
Model binding sonrası zengin veri (Controller, ModelState, ActionArguments) |
| Örnek Kullanım | Authentication, CORS, Statik Dosya, Özel Header | Loglama, Validasyon, Cache, İş mantığı öncesi kontrol |
| Sıralama | Pipeline sırasına göre | Filter türüne göre (Authorization > Resource > Action > Exception > Result) |
Altın Kural: Eğer işleminiz Controller/Model bilgisine ihtiyaç duymuyorsa (ör. gelen token'ı doğrulama, CORS), Middleware kullanın. Eğer Action parametrelerine, ModelState'e veya ActionResult'a müdahale edecekseniz, Action Filter kullanın.
4. Factory-based Filters (Dependency Injection ile) ve ServiceFilter/TypeFilter
Filter'ları DI üzerinden kaydedip kullanmanın iki yolu:
-
[ServiceFilter]: Filter'ı DI container'a kaydetmenizi gerektirir (ölümcül bağımlılıkları çözümler). -
[TypeFilter]: Filter'ı DI'a kaydetmeye gerek yoktur, container'dan çözümler ama her seferinde yeni bir örnek oluşturur.
csharp
// DI'a kaydet
builder.Services.AddScoped<PerformanceLoggingFilter>();
// Controller'da kullan
[ServiceFilter(typeof(PerformanceLoggingFilter))]
public IActionResult Get() { ... }
Not: Filter'lar varsayılan olarak Scoped yaşam döngüsüne sahiptir. İçinde DbContext kullanacaksanız, Scoped olması doğrudur. Ancak Singleton bir filter istiyorsanız (önerilmez), yaşam döngüsüne dikkat edin.
5. Performans ve Dikkat Edilmesi Gereken Noktalar
-
Async Kullanımı: Mümkünse
IAsyncActionFilterveyaIAsyncResultFilterkullanarak thread havuzunu bloklamayın. -
Middleware'de
HttpContext.Response.Bodyile oynamak tehlikelidir: Yanıt yazılmaya başlandıktan sonra (response body) değiştirilemez. Bu nedenle yanıta müdahale edecekseniz (ör. loglama için), bununextçağrıldıktan önce yapın veya birStreamwrapper ile sarın. -
Filter Sıralamasını Kontrol Edin:
[ServiceFilter]ve[TypeFilter]sıralamasınıOrderparametresi ile belirleyebilirsiniz. -
Exception Filter vs Middleware: Exception filter sadece MVC katmanındaki hataları yakalar. Middleware ise tüm pipeline'daki hataları yakalar. Bu nedenle global hata yönetimi için
UseExceptionHandlermiddleware'i tercih edilir; sadece belirli Action'lara özel hata yönetimi için Exception Filter kullanılır.
Sonuç:
ASP.NET Core pipeline'ında Middleware genel, her isteği kapsayan işlemler için; Action Filter ise MVC/WebAPI katmanında daha kontrollü, veriye erişim gerektiren işlemler için idealdir. İkisini birlikte kullanarak uygulamanızı modüler, test edilebilir ve bakımı kolay hale getirebilirsiniz. Unutmayın: Pipeline sırası, performans ve güvenlik için hayati önem taşır.