ASP.NET Core uygulamasında bir istek geldiğinde bu istek hemen controller içine düşmez. Önce uygulamanın request hattından geçer, sonra uygunsa controller ve action seviyesine ulaşır.
Bu yüzden bazı kontroller en dış katmanda yapılır, bazıları ise action'a daha yakın noktada yapılır. İşte middleware ve filter farkı tam burada başlar.
Browser -> Middleware -> Controller -> Action -> Response
Middleware, uygulamaya gelen neredeyse her isteğin geçtiği genel kontrol katmanıdır. Request daha controller'a ulaşmadan önce burada karşılanır.
Kimlik doğrulama, yetkilendirme, logging, global hata yönetimi gibi sistem genelini ilgilendiren konular burada çözülür.
Kısacası middleware şunu sorar: Bu istek sisteme devam edebilir mi?
Filter ise MVC tarafında, controller ve action seviyesine daha yakın çalışan bir kontroldür. Her request için değil, sadece ilgili controller veya action devreye girdiğinde çalışır.
Bu yüzden iş kuralına yakın kontroller için çok uygundur. Mesela aktif plan kontrolü, belirli bir feature erişimi veya action'a özel bir kısıt burada mantıklı olabilir.
Filter daha çok şunu sorar: Bu kullanıcı bu action'ı çalıştırabilir mi?
Bir yazılım sistemi düşün. Yönetici panelinden bir kullanıcı banlanıyor. Artık o kişinin sistemde dolaşmasını istemiyorsun.
Kullanıcı o anda giriş yapmış olsa bile, en ufak sayfa yenilemede veya yeni istekte sistem onu fark etmeli. Yani sadece belirli bir controller değil, tüm uygulama seviyesinde bu durum hissedilmeli.
İşte bu senaryo filter'dan çok middleware kokar. Çünkü burada amaç belirli bir action'ı engellemek değil, kullanıcının sistemde ilerlemesini genel olarak durdurmaktır.
Çünkü ban kontrolü sistem geneline ait bir güvenlik kararıdır. Kullanıcıyı bir yerde durdurup başka yerde serbest bırakmak istemezsin. Banlandıysa, her yeni request'te bu bilgi dikkate alınmalıdır.
Middleware bu iş için uygundur çünkü controller'a gelmeden çalışır. Kullanıcı banlıysa oturumu kapatılabilir, erişimi kesilebilir ve uygun sayfaya yönlendirilebilir.
Yani middleware burada “bu kullanıcı bu action'a girebilir mi?” diye değil, “bu kullanıcı sistemde kalabilir mi?” diye düşünür.
Her kontrol middleware olmamalıdır. Mesela bir kullanıcının aktif planı var mı, belirli bir ürüne erişebilir mi, bu action sadece premium üyeler için mi gibi kurallar daha çok action seviyesine yakındır.
Bunlar her request'te değil, sadece ilgili controller veya action kullanıldığında kontrol edilmelidir. İşte burada filter daha temiz ve daha isabetli bir çözümdür.
Yani ban gibi sistemsel kararlar middleware'a, iş kuralına yakın kararlar ise çoğu zaman filter'a daha çok yakışır.
Aşağıdaki örnekte kullanıcı giriş yapmışsa ban durumu kontrol ediliyor. Eğer banlıysa sistem kullanıcıyı oturumdan çıkarıyor ve özel bir sayfaya yönlendiriyor.
Böylece kişi bir sonraki refresh anında bile sistem dışında kalmış oluyor.
public class BanCheckMiddleware
{
private readonly RequestDelegate _next;
public BanCheckMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var user = context.User;
if (user.Identity != null && user.Identity.IsAuthenticated)
{
var isBanned = false;
if (isBanned)
{
await context.SignOutAsync();
context.Response.Redirect("/Account/Banned");
return;
}
}
await _next(context);
}
}
Bir kontrolü nereye koyacağını seçerken şu soruyu sor: Bu kural sistem genelini mi ilgilendiriyor, yoksa belirli action'ları mı?
Eğer cevap tüm sistemi ilgilendiriyorsa middleware düşün. Eğer cevap belirli controller veya action'lara yakınsa filter düşün.