ASP.NET Core’da filtreler (filters), uygulamanın işlem akışını daha esnek ve yönetilebilir hale getiren güçlü yapılardır. Bir filtre, belirli bir işlem öncesinde, işlem anında veya sonrasında devreye girerek sürece müdahale etmemize imkan tanır. Örneğin;
- Bir kullanıcı isteği controller’a ulaşmadan önce güvenlik kontrolleri yapmak,
- Action metodunun çalışmasının ardından loglama işlemleri gerçekleştirmek,
- Beklenmeyen bir hata oluştuğunda özel bir cevap döndürmek
gibi durumlar filtreler aracılığıyla kolayca yönetilebilir.

ASP.NET Core’da birden fazla filtre türü bulunur ve bu filtreler belirli bir sıra ile çalışır. Bu sıralama, uygulamanın tutarlı şekilde ilerlemesini sağlarken aynı zamanda performans açısından gereksiz yüklerin önüne geçer. Dolayısıyla filtreler, sadece verimlilik değil, aynı zamanda güvenlik, yönetilebilirlik ve bakım kolaylığı açısından da uygulamaya büyük katkı sunar.
Bir diğer avantaj ise tekrarlayan kodların önüne geçmesidir. Örneğin, her action veya controller içine aynı loglama kodunu yazmak yerine bunu tek bir filtre ile merkezi olarak tanımlayabiliriz.
Burada önemli bir noktaya değinmek gerekir: ASP.NET Core’da bir istek (request) uygulamaya ulaştığında ilk olarak middleware zincirinden geçer. Middleware’ler uygulama genelinde tanımlanan kurallar bütünüdür (örneğin: authentication, request pipeline yönetimi, global hata yakalama vb.). Eğer istek bu katmanlardan başarıyla geçerse, ilgili controller veya action method seviyesine gelir. İşte tam bu noktada filtreler devreye girer.
Kısacası:
- Middleware → Uygulama genelinde çalışan kurallar zinciridir.
- Filter → Daha spesifik, controller veya action düzeyinde çalışan kurallar bütünüdür.
Şimdi, filtrelerin hangi sırayla çalıştığını ve her bir filtre türünün ne anlama geldiğini detaylı şekilde inceleyelim.
Bir requestin yolculuğu
Press enter or click to view image in full size

Bu görseli, bir request’in nasıl işlendiğini zihnde canlandırmak için ekledim. Görüldüğü üzere ilk olarak uygulama genelinde tanımlanmış kurallar (middleware) devreye giriyor. Ardından request, hangi endpoint’e yönlendirilmişse, o endpoint’in bulunduğu controller veya action seviyesinde tanımlanmış filtrelerin işleyişine tabi tutuluyor.
Burada bahsettiğim “kurallar” ifadesi yalnızca güvenlik ile sınırlı değildir. Daha genel bir anlamda; yapılacak ek işlemler, gerekli kontroller ya da request/response üzerinde gerçekleştirilecek çeşitli manipülasyonlar olarak düşünülebilir.
Bir requestin hangi aşamalardan geçtiğini hafızamızda canlandırdığımıza göre filtre’lara odaklanmaya başlayabiliriz.
ASP.NET Core Filters
ASP.NET Core’da filters (filtreler), bir isteğin (request) işlenme sürecine müdahale etmemizi sağlayan yapılardır. Bir filtre, isteğin controller’a ulaşmadan önce, action metodunun çalışması sırasında veya sonrasında devreye girerek sürece dahil olabilir.
Bu sayede filtreler, uygulamada sıkça ihtiyaç duyulan şu işlemler için güçlü bir mekanizma sunar:
- Yetkilendirme (Authorization) kontrolleri yapmak,
- Loglama veya izleme (monitoring) işlemleri gerçekleştirmek,
- Hata yönetimini merkezi olarak ele almak,
- Response üzerinde değişiklik yapmak,
- Tekrarlayan iş mantıklarını merkezi bir noktada toplamak.
ASP.NET Core, bu amaçlar için hazır filtre tipleri sunar (Authorization Filter, Resource Filter, Action Filter, Exception Filter, Result Filter). Ayrıca ihtiyaç halinde özel filtreler (custom filters) yazarak projeye özgü iş mantıkları da eklenebilir.
ASP.NET Core’da birden fazla filtre türü bulunur ve bu filtreler belirli bir sıra ile çalışır. Bu sıralama, uygulamanın tutarlı şekilde ilerlemesini sağlarken aynı zamanda performans açısından gereksiz yüklerin önüne geçer.
Yazımın başında da belirttiğim gibi, filtrelerin (ve aynı şekilde middleware’lerin) sırayla çalışması kritik bir noktadır. Bunu daha anlaşılır kılmak için günlük hayattan bir örnek üzerinden ilerleyelim.
Bir restoranda sipariş verdiğinizi düşünün:
- Öncelikle siparişinizi garsona iletirsiniz.
- Garson, bu siparişi mutfağa aktarır.
- Mutfakta gerekli malzemeler kullanılarak ürün hazırlanır. Eğer malzeme eksikse garson aracılığıyla bu durum müşteriye bildirilir.
- Bir problem yoksa yemek hazırlanır ve garson tarafından masanıza getirilir.
Şimdi bu sürecin sıra dışı işlediğini hayal edin: mutfak şefinin daha sipariş verilmeden malzeme araması, garsonun olmayan bir yemeği masaya getirmeye çalışması veya siparişin hiç mutfağa iletilmemesi… Bu durumda işleyişin sağlıklı ve düzenli olmasını bekleyemeyiz.
İşte yazılım dünyasında da aynı mantık geçerlidir. Middleware ve filtreler, bir işlem sırasına bağlı olarak çalışır. Bu sıra bozulmadığı sürece uygulama akışı tutarlı, yönetilebilir ve öngörülebilir olur.
Genel işleyiş ile ilgili yeterli bilgiye sahip olduğumuza göre filter tiplerine geçebiliriz.
Press enter or click to view image in full size

ASP.NET Core Filter tipleri
İsterseniz ilk filter tipimiz olan “Authorization Filters” ile başlayalım.
Authorization Filters
Adından da anlaşılacağı gibi yetkilendirme ile ilgili filtreler bu başlık altında tanımlanır. Authorization Filterlar tanımlanırken “IAuthorizationFilter” interface’i kullanılır. Bu interface’in sağladığı metodlar uygun mantığa göre( hangi şartlarda validate edecekseniz) yazılır.
İsterseniz belirli bir ünvana sahip olan kullanıcıların erişebileceği bir filter tanımlayalım.
public class TitleAuthorizeFilter : Attribute, IAuthorizationFilter
{
private readonly string _requiredTitle;
public TitleAuthorizeFilter(string requiredTitle)
{
_requiredTitle = requiredTitle;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
// Kullanıcı giriş yapmamışsa
if (!user.Identity?.IsAuthenticated ?? true)
{
context.Result = new ForbidResult();
return;
}
// Kullanıcının sahip olduğu ünvanlarda istenen ünvanın kontrolü
var userTitle = user.Claims.FirstOrDefault(c => c.Type == "Title")?.Value;
if (userTitle == null || !string.Equals(userTitle, _requiredTitle, StringComparison.OrdinalIgnoreCase))
{
context.Result = new ForbidResult();
}
}
}
Bu filter’ımızda kullanıcının istenen ünvana sahip olma şartıyla istenen controller ya da action metoda erişim izni veriliyor. Kodu incelediğimizde Attribute sınıfından türetildiğini görüyoruz. Bunu sebebi adından da anlaşılacağı gibi attribute olarak kullanabilmek. Yetkilendirme filtresinin ilk sırada çalışmasının doğru olacağı için IAuthorizationFilter interface’ini kullanıyoruz. Böylece oluşturduğumuz filter, diğer filterlardan önce çalışacak.
[]
public IActionResult ProfessorPage()
{
return View();
}
[]
public IActionResult StudentPage()
{
return View();
}
Burada da kullanım örneğini görüyoruz. Requestin endpointi ProfessorPage olduğunda isteği yapan kullanıcının ünvanlarında ‘Professor’ şartı aranacak, aynı durum student için de geçerli. Bu örnekler oldukça çeşitlendirilebilir. Servis gerektiren filterlar da kullanılabilir, tamamen ihtiyaca bağlı.
İsterseniz ikinci sırada bulunan Resource Filter tipimize geçebiliriz.
Resource Filters
Resource Filterlar, action method çağrılmadan önce ve sonra çalışabilen bir filter türüdür. Bu filter tipi genellikle işlem yapılacak veri veya kaynak ile ilgili yerlerde kullanılmakta. İşlem yapılacak veriden kastım aslında şu: Örneğin bir endpointe sürekli istek atılıyor ve endpointteki istek çoğu zaman statik, yani zamana bağlı değişiklik göstermiyor. Böyle bir durumda o endpointe her istek atıldığında veritabanından ilgili verileri almak yerine cache’den veri alarak sistemi hızlandırabiliriz.
Örnek vermek gerekirse:
public class CacheResourceFilter : IResourceFilter
{
// in memory cache
private readonly IMemoryCache _cache;
public CacheResourceFilter(IMemoryCache cache)
{
_cache = cache;
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
var key = context.HttpContext.Request.Path.ToString();
if (_cache.TryGetValue(key, out string cachedResponse))
{
// veritabanından veri almak yerine cache'den alıyoruz.
context.Result = new ContentResult
{
Content = cachedResponse,
ContentType = "text/plain"
};
}
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
if (context.Result is ContentResult result)
{
var key = context.HttpContext.Request.Path.ToString();
_cache.Set(key, result.Content, TimeSpan.FromMinutes(5));
}
}
}
// Controller içinde kullanımı
[]
public IActionResult GetData()
{
return Content("Veri: " + DateTime.Now);
}
Bu örnekte, action çalıştırılmadan önce ilk olarak cache kontrolü yapılıyor; eğer cache’de ilgili veri varsa direkt cache’den veri döndürülüyor. Eğer cache’de veri yoksa action çalışıyor, devamında da elde edilen veri cache’e yazılıyor. Böylece bir sonraki request’in endpointi aynı action olduğunda direkt cache’den veri döndürülecek, veritabanı isteği atılmayacak ve response daha hızlı üretilecek.
Resource filter kullanmanın bir diğer avantajı ise model binding süreci ile ilgili. Model binding, HTTP request’ten gelen verilerin (genellikle JSON veya form verisi) action method parametrelerine otomatik olarak deserialize edilmesi sürecidir. Bu işlem CPU ve memory açısından ekstra maliyet getirir. Model binding süreci, resource filter aşamasından sonra gerçekleşir. Eğer gelen request’i resource filter ile handle edip short-circuit yaparsak, model binding aşamasına hiç geçilmez. Böylece gereksiz CPU ve memory kullanımının önüne geçmiş oluruz.
Bizim örneğimizde de cache den veri dönerek short-circuit yapmış oluyoruz.
Action Filters
Action Filter, bir action metod çağırılmadan önce veya çağırıldıktan sonra çalışan filtre türüdür. Metod çalıştırılmadan önce ve metod çalıştırıldıktan sonra çalışması, daha efektif manipülasyona izin verir.
Örneğin, metod çağırılmadan önce çalıştığında (OnActionExecution metodu) isteğin uygunluğunu kontrol edebilir (Request Header’da ‘encrypted-value’ değerinin olup olmamasına göre return etmek gibi), loglama yapabilir, request’i manipüle edebilir, model validasyonu yapabiliriz.
Metod çalıştıktan sonra çalıştığında ise action sonucu düzenleyebilir, response’a ekleme yapabiliriz.
İsterseniz action filters ile ilgili çeşitli örneklere göz atalım. Farzedelim ki bazı endpointlerin çalışma süresini hesaplamak istiyoruz. Bunu Stopwatch() ile metod bazlı uygulayabiliriz. Fakat aynı işlemi diğer metodlar için de yapmak istersek kod tekrarına girmiş oluruz. Bu problemi Action Filters ile çözebiliriz.
public class ExecutionTimeActionFilter : IActionFilter
{
private Stopwatch _stopwatch;
public void OnActionExecuting(ActionExecutingContext context)
{
// Action başlamadan önce başlatıyoruz.
_stopwatch = Stopwatch.StartNew();
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Action tamamlandıktan sonra süreyi durduruyoruz.
_stopwatch.Stop();
var elapsedMs = _stopwatch.ElapsedMilliseconds;
// Loglama
var actionName = context.ActionDescriptor.DisplayName;
Console.WriteLine($"Action '{actionName}' {elapsedMs} ms sürdü.");
}
}
// Controllerda şu şekilde kullanabiliriz.
[]
public IActionResult Index()
{
// İşlem süresi ölçülecek action
Thread.Sleep(500); // örnek uzun işlem
return View();
}
Burada metod bazlı çalışma süresini ölçebiliriz. Eğer uygulamamızdaki tüm actionların çalışma süresini ölçmek istiyorsak program.cs’e şu kodu ekleyebiliriz:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<ExecutionTimeActionFilter>();
});
Burada şu soruyu kendimize sorabiliriz; Çalışma süresini hesaplamak için action filter yazmak yerine middleware yazabilir miydik?
Yazabilirdik, fakat ölçümlerimiz farklı olurdu.
Çünkü middleware’e yazılan bir timer tüm pipeline’ı ölçerken (tüm actionların çalışma süresi + middlewarelerin çalışma süresi + filterların çalışma süresi), filter olarak yazılan bir timer yalnızca actionun çalışma süresini ölçer. Bu sebeple amacımızı doğru belirlemeliyiz.
Action Filter ile ilgili bir örnek daha vermek istiyorum. Bir API projesinde endpoint parametrelerinde dtolar kullanıyor, bu dtoları service kısmında amacımıza göre kullanıyoruz. Bu süreçte dto’nun null olma durumunu ve validasyonun geçerli olup olmadığını mutlak kontrol ediyoruz. Bu kontrol işlemlerini her bir metodda yapmak yerine bir ActionFilter ile tek bir yerden yönetebiliriz. Hadi bir ActionFilter yazalım:
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var controller = context.RouteData.Values["controller"];
var action = context.RouteData.Values["action"];
// Endpoint parametrelerini geziyoruz.
foreach (var arg in context.ActionArguments.Values)
{
// Dtoları yakalıyoruz.
if (arg != null && arg.GetType().Name.EndsWith("Dto"))
{
if (arg == null)
{
context.Result = new BadRequestObjectResult($"DTO is null\nController: {controller}\nAction: {action}");
return;
}
if (!context.ModelState.IsValid)
{
var errors = context.ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
context.Result = new UnprocessableEntityObjectResult(errors);
return;
}
}
}
}
}
// Controllerdaki kullanımı
[]
public IActionResult CreateUser(UserDto dto)
{
...
}
Bu örnekte de IActionFilter interface’i yerine ActionFilterAttribute sınıfını kullandım. Bu sınıf IActionFilter interface’ini zaten implemente eder. Aynı zamanda bu sınır bir ‘Attribute’ sınıfı olduğundan istediğimiz noktada attribute olarak kullanabiliriz.
Eğer IActionFilter ile yazsaydık [TypeFilter(typeof(ValidationFilter))] şeklinde kullanmamız gerekecekti.
ActionFilterAttribute sınıfı ile yazdığımız için daha basit bir kullanım olan [ValidationFilter] şekliyle kullanabiliyoruz.
Endpoint Filters
Endpoint Filters, ASP.NET Core Minimal API’lerde belirli bir endpoint’in çalışmadan önce vya çalıştıktan sonra çeşitli manipülasyonlar yapabilmemizi sağlayan yapılardır. Endpoint Filter’lar, Action Filter’ların minimal API versiyonu denilebilir.
// Minimal API
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/colorSelector/{color}", (string color) => $"Color specified: {color}!")
.AddEndpointFilter(async (context, next) =>
{
var color = context.GetArgument<string>(0);
if (color == "Red")
{
return Results.Problem("Red not allowed!");
}
return await next(context);
});
app.Run();
Örnekten de anlaşılacağı üzere ‘Red’ değeri geldiğinde hata döndürülüyor, diğer durumlarda endpoint normal çalışıyor. Action Filters ile aynı mantık olduğundan anlaşılması gayet kolay.
Exception Filter
Exception Filter, bir action çalışırken beklenmeyen bir hata (exception) oluştuğunda devreye giren filtredir. Controller, action ve uygulama geneli seviyelerinde tanımlanabilir. MVC Pipeline’ı içinde çalışır, yani sadece controller ve action seviyesindeki hataları yakalayabilir. Örneğin middleware, model binding veya controller dışında bulunan herhangi bir kodda oluşan hatayı yakalayamaz.
‘Model binding sırasında oluşan hataları neden yakalayamaz?’ gibi bir soru sorulabilir. Exception Filter, action çalışmaya başladıktan sonra devreye girer. Bu sebeple model binding aşamasında hata oluşsa dahi exception filter henüz çalışmadığı için hatayı yakalayamaz.
Belirli bir Controller yada Action bazında spesifik loglama yapmak istiyor olabiliriz.
public class CustomExceptionFilter : IExceptionFilter
{
private readonly ILogger<CustomExceptionFilter> _logger;
public CustomExceptionFilter(ILogger<CustomExceptionFilter> logger)
{
_logger = logger;
}
public void OnException(ExceptionContext context)
{
_logger.LogError(context.Exception, "Önemli actionda hata oluştu!");
context.Result = new ObjectResult(new
{
Message = "Önemli actionda hata meydana geldi. Lütfen tekrar deneyiniz."
})
{
StatusCode = StatusCodes.Status500InternalServerError
};
// Burada excepiton'u işlediğimizi söylüyoruz, böylece başka bir exception filter
// yada middleware'a geçmesini engelliyoruz.
context.ExceptionHandled = true;
}
}
// Action örneği
[]
[]
public IActionResult Create(Product model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
return Ok(model);
}
Yukarıdaki örnekte de endpoint bazlı spesifik loglama yapmak istersek bu yapıyı kullanabiliriz.
Result Filter
Result Filter, ASP.NET Core’da bir action çalıştıktan sonra dönen sonucu manipüle etmek için kullanılan bir filtredir. Action metodu sonuç ürettikten sonra, henüz client’a sonucu döndürmeden önce veya hemen sonra çeşitli eylemler yapabilmemize olanak tanır.
Örneğin, kişisel verilerin değiştirilebildiği bir endpointte güncelleme yapıldığında, güncellenme tarihi ve işlem durumunu ekstra olarak dönmek isteyebiliriz. Böyle bir durumda Result Filter kullanabiliriz.
public class PersonalDataLoggingFilter : IResultFilter
{
private readonly ILogger<PersonalDataLoggingFilter> _logger;
public PersonalDataLoggingFilter(ILogger<PersonalDataLoggingFilter> logger)
{
_logger = logger;
}
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult objResult && objResult.Value is User user)
{
// Kişisel veriler güncellendiğinde log tutmak istiyorum
_logger.LogInformation("User {UserId} updated personal data at {Time}",
user.Id, DateTime.UtcNow);
// Güncellenme tarihi ve durumunu ek olarak dönmek istiyorum
var responseWithMetadata = new
{
user.Id,
user.Name,
user.Email,
UpdatedAt = DateTime.UtcNow, // ek bilgi
Status = "Success" // ek bilgi
};
objResult.Value = responseWithMetadata; // response’u güncelliyoruz
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Response gönderildikten sonra ek işlem yapabiliriz.
}
}
Controllerdaki kullanımı da şu şekilde:
[]
[]
public IActionResult Update(int id, UserUpdateModel model)
{
var user = GetUserFromDb(id);
user.Name = model.Name;
user.Email = model.Email;
SaveChanges();
return Ok(user); // Yazdığımız filter bu satırda devreye girip
// response'u değiştiriyor.
}
Bu görsel ise bir request geldiğinde middleware pipeline’ından filter pipeline’ına geçen bir requestin yolculuğunu temsil ediyor. Yazıda bahsettiğim filterlar hakkında bilgi sahibi olduktan sonra bu görsel oldukça anlam kazandı diye düşünüyorum.
Press enter or click to view image in full size

Bu yazımda ASP.NET Core Filters hakkında öğrendiklerimi yazıya dökmek istedim. Umarım genel bir fikir olması amacıyla bir şeyler katabilmişimdir. İyi günler dilerim.
Comments 0
You cannot comment because you are not logged in.
Log in to comment.