ASP.NET Core projelerimizde middleware tanımlamak önemli bir özellik bu özellik sayesinde http isteklerini filtreleyebilir, tüm isteklerden önce veya sonra kod çalıştırabiliriz.
Middleware Nedir?
Middleware, bir web uygulamasında isteklerin uygulamaya ulaşmadan önce veya sonra ele alınmasını ve gerekli durumlarda gerekli işlemlerin yapılmasını sağlayan sınıflardır. ASP.NET Core Web API veya MVC Rozor Pages projelerinde bir pipeline( işleme hattı ) ile işlenir ve middleware kullanımı çok kolaydır.
Hangi Durumlarda Kullanılır?
Middleware kullanım senaryolarından bazıları şu şekilde:
Loglama: isteklerden önce veya sonra istek ve isteği oluşturan kullanıcı hakkında log kayıtları oluşturabilirsiniz.
Exception Handling: Hataları yakalamak için yapılar kurabilirsiniz. İsteğe bağlı olarak loglama yada belirli hata mesajları gösterme veya yönlendirmeler yapabilirsiniz.
CORS Ayarları: Tarayıcıdan gelen cross orgin yani farklı kaynaklardan gelen istekleri kısıtlayabilirsiniz.
Yetki / Yetkilendirme Kontrolü: Authorizon veya Authentication yapıları kurarak kullanıcıyı ve yetkilerini kontrol edebilirsiniz.
Performans Ölçümü: İsteğin öncesinde ve sonrasında kod çalıştırabildiğimizi söylemiştik, bu sayede uygulamamızın bir isteği ne kadar sürede işlediğini ve sonucu hazırladığını ölçebiliriz.
Rate Limiting, IP veya Lokasyon Kısıtlama: Bir kullanıcının hangi sıklıkla istek atabileceğini limitleyebilir veya uygulamaya erişimini tamamen kısıtlayabilirsiniz.
Liste daha uzar ama genel olarak ne yapılabileceğine değinmiş olduk. Şimdi örnek bir WEB API projesinde middleware ekleyelim.
Basit Middleware Örneği
Kullanıcının en fazla dakikada 100 kez istek atabilmesini sağlayalım.
Middleware classlarına otomatik olarak bir parametre ekleniyor:
RequestDelegate next
middleware’i işleme alırken bu parametre üzerinden mevcut isteği tanıyabiliyoruz. Hemen örneğimize geçelim.
Middlewares/RateLimitingMiddleware.cs
// Middlewares/RateLimitingMiddleware.cs
using Microsoft.AspNetCore.Http;
using System.Collections.Concurrent;
using System.Net;
public class RateLimitingMiddleware
{
private readonly RequestDelegate _next;
private static readonly ConcurrentDictionary<string, List<DateTime>> _requestLog = new();
private const int LIMIT = 100; // max istek
private static readonly TimeSpan WINDOW = TimeSpan.FromMinutes(1); // süre
public RateLimitingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
string ip = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
var now = DateTime.UtcNow;
var requests = _requestLog.GetOrAdd(ip, _ => new List<DateTime>());
lock (requests)
{
// Eski kayıtları temizle
requests.RemoveAll(r => r < now - WINDOW);
if (requests.Count >= LIMIT)
{
context.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
context.Response.Headers["Retry-After"] = "60";
return;
}
requests.Add(now);
}
await _next(context);
}
}
Bu örnekde dikkat etmemiz gereken bir nokta var o da next isimli bir sonraki request’i temsil eden ve context isimli mevcut request’in objenin sınıfımıza inject edilmiş olması.
public RateLimitingMiddleware(RequestDelegate next)
{
_next = next;
}
Ve sonrasında InvokeAsync isimli fonksiyonumuzun içerisinde isteğimiz ile ilgili gerekli işlemleri yaptıkdan sonra context’i bir sonraki işleme göndermek.
public async Task InvokeAsync(HttpContext context)
{
await _next(context);
}
Bu yapı bizim sistemimizin omurgası, bu yapı tüm pipeline da mevcut ve tüm middleware lar birbirine bu şekilde isteği iletiyor.
Diğer kod kısımları örneğimize ait kısımlar.
Şimdi sırada bu oluşturduğumuz middleware’i Program.cs dosyamızda sıraya almak var.
Bunun için basitçe program.cs dosyamızı şu şekilde düzenliyoruz.
// Program.cs
using YourNamespace.Middlewares; // doğru namespace olduğuna dikkat et
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Rate Limiting Middleware en başta olabilir
app.UseMiddleware<RateLimitingMiddleware>();
app.MapControllers();
app.Run();
Burada birden fazla middleware eklersek eğer bilmemiz gereken bu middleware’lerin sırasının önemli olduğudur.
Birden fazla middleware olduğunu varsayalım bu durumda akış şu şekilde olacaktır.
İstek Akışı →
[C öncesi]
[B öncesi]
[A öncesi]
[KULLANICI → RESPONSE]
[A sonrası]
[B sonrası]
[C sonrası]
Bu yazıda middleware’nin ne olduğunu inceledik ve basit bir örnek yapmış olduk. Bir sonraki yazıda görüşmek üzere.