“`html

C# geliştirme dünyasında, uygulama akışını yönetmek ve kodun farklı katmanları arasında bilgi paylaşımını sağlamak için güçlü mekanizmalar bulunur. Middleware, özellikle .NET Core tabanlı uygulamalarda HTTP istek/cevap boru hattını şekillendiren, çapraz kesen endişeleri (logging, kimlik doğrulama) merkezi bir yerden ele almayı sağlayan esnek bir yaklaşımdır. Attribute’lar ise, kodumuza meta veri ekleyerek derleme zamanı veya çalışma zamanında ek davranışlar kazandırmamıza olanak tanıyan deklaratif bir programlama aracıdır. Bu makale, bu iki güçlü özelliği derinlemesine inceleyecektir.

C# Dilinde Middleware Geliştirme

Middleware, .NET Core uygulamalarında HTTP istek işleme boru hattının (request pipeline) kalbinde yer alır. Her bir istek, boru hattındaki middleware bileşenlerinden sırayla geçer ve her bileşen, isteği işleyebilir, değiştirebilir veya bir sonraki bileşene devredebilir. Bu yapı, logging, kimlik doğrulama, yetkilendirme, hata yönetimi ve statik dosya sunumu gibi çapraz kesen endişelerin (cross-cutting concerns) uygulamadan bağımsız ve modüler bir şekilde ele alınmasını sağlar.

Middleware Nasıl Oluşturulur?

Özel bir middleware oluşturmanın temel yolu, RequestDelegate tipini yapıcı metodunda alan ve InvokeAsync(HttpContext context) metodunu uygulayan bir sınıf tanımlamaktır. InvokeAsync metodu, middleware’in asıl iş mantığını içerir ve genellikle bir sonraki middleware’i çağırmak için _next(context) çağrısını içerir.

Örnek: İstek Süresini Ölçen Middleware

Aşağıdaki örnek, her HTTP isteğinin ne kadar sürdüğünü loglayan basit bir middleware’i göstermektedir. Bu, performans izleme için faydalı olabilir.


using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Threading.Tasks;

public class RequestTimerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestTimerMiddleware> _logger;

    public RequestTimerMiddleware(RequestDelegate next, ILogger<RequestTimerMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var watch = Stopwatch.StartNew();
        await _next(context); // İstek işlemeye devam et
        watch.Stop();
        _logger.LogInformation($"Request to {context.Request.Path} took {watch.ElapsedMilliseconds} ms.");
    }
}

Middleware Boru Hattına Ekleme

Oluşturulan middleware’i uygulamanın boru hattına eklemek için genellikle bir uzantı metodu (extension method) kullanılır. Bu, kodun daha temiz ve okunabilir olmasını sağlar.


using Microsoft.AspNetCore.Builder;

public static class RequestTimerMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestTimer(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestTimerMiddleware>();
    }
}

Daha sonra Program.cs veya Startup.cs dosyasındaki Configure metodunda bu uzantı metodunu çağırarak middleware’i boru hattına ekleyebilirsiniz:


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Diğer middleware'ler...
    app.UseRequestTimer(); // Özel middleware'imizi ekliyoruz
    // Diğer middleware'ler...
}

Middleware’lerin sırası önemlidir. Bir middleware boru hattında ne kadar erken yer alırsa, o kadar önce çalışır ve isteği daha erken müdahale edebilir veya sonlandırabilir.

C# Dilinde Attribute Geliştirme

Attribute’lar, C# dilinde kod elemanlarına (sınıflar, metotlar, property’ler, parametreler vb.) deklaratif olarak meta veri eklemek için kullanılan güçlü bir yapıdır. Bu meta veriler derleme zamanında statik analiz için kullanılabileceği gibi, çalışma zamanında (runtime) Reflection API’leri aracılığıyla okunarak dinamik davranışlar sergilenmesini de sağlayabilir.

Mevcut Attribute’ları Kullanma

C# ve .NET framework birçok dahili attribute sağlar. Örneğin:

  • [Serializable]: Bir sınıfın serileştirilebilir olduğunu belirtir.
  • [Obsolete]: Bir metodun veya sınıfın kullanım dışı kaldığını ve gelecekte kaldırılacağını belirtir.
  • [Required], [StringLength] (Data Annotations): Model doğrulama kurallarını tanımlar.
  • [Authorize] (ASP.NET Core): Bir metodun veya kontrolcünün yetkilendirme gerektirdiğini belirtir.

Özel Attribute Oluşturma

Kendi iş mantığınızı yansıtan özel attribute’lar oluşturmak oldukça kolaydır. Bir attribute oluşturmak için System.Attribute sınıfından türeyen bir sınıf tanımlamanız yeterlidir.

Örnek: Yetki Kontrolü Attribute’u

Belirli bir izne sahip olmayı gerektiren metotları veya sınıfları işaretlemek için bir attribute oluşturabiliriz:


using System;

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class RequiresPermissionAttribute : Attribute
{
    public string PermissionName { get; }

    public RequiresPermissionAttribute(string permissionName)
    {
        PermissionName = permissionName;
    }
}

Yukarıdaki örnekte:

  • [AttributeUsage] attribute’u, bu özel attribute’un nerede kullanılabileceğini (metotlar veya sınıflar), türetilmiş sınıflar tarafından miras alınıp alınamayacağını (Inherited = true) ve bir elemana birden fazla kez uygulanıp uygulanamayacağını (AllowMultiple = false) belirtir.
  • PermissionName özelliği, hangi iznin gerekli olduğunu belirler ve yapıcı metot aracılığıyla atanır.

Bu attribute’u bir kontrolcüye veya metoda şu şekilde uygulayabiliriz:


[RequiresPermission("AdminPanelAccess")]
public class AdminController : Controller
{
    // ...
    [RequiresPermission("DeleteUser")]
    public IActionResult DeleteUser(int id)
    {
        // ...
    }
}

Attribute’ları Çalışma Zamanında Okuma (Reflection)

Attribute’ların gerçek gücü, çalışma zamanında Reflection API’leri kullanılarak okunabilmesinden gelir. Bu sayede, kodun kendisini inceleyerek dinamik davranışlar sergileyebilirsiniz.


using System;
using System.Reflection;

public class AttributeReader
{
    public static void ReadPermissions(Type type)
    {
        // Sınıf üzerindeki attribute'ları oku
        var classAttribute = type.GetCustomAttribute<RequiresPermissionAttribute>();
        if (classAttribute != null)
        {
            Console.WriteLine($"Class '{type.Name}' requires permission: {classAttribute.PermissionName}");
        }

        // Metotlar üzerindeki attribute'ları oku
        foreach (var method in type.GetMethods())
        {
            var methodAttribute = method.GetCustomAttribute<RequiresPermissionAttribute>();
            if (methodAttribute != null)
            {
                Console.WriteLine($"Method '{method.Name}' requires permission: {methodAttribute.PermissionName}");
            }
        }
    }

    // Kullanım örneği:
    // AttributeReader.ReadPermissions(typeof(AdminController));
}

Middleware ve Attribute’ların Birlikte Kullanımı

Middleware ve Attribute’lar ayrı ayrı güçlü olsa da, birlikte kullanıldıklarında çok daha dinamik ve esnek çözümler sunabilirler. Tipik bir senaryo, bir middleware’in HTTP isteğini işlerken, hedef endpoint’e veya kontrolcü/aksiyon metoduna uygulanmış özel attribute’ları Reflection aracılığıyla okuyarak isteğin davranışını değiştirmesidir.

Örnek: Middleware İçinde Yetki Attribute’unu Kontrol Etme

Daha önce tanımladığımız RequiresPermissionAttribute‘u bir yetkilendirme middleware’i içinde kullanarak, belirli bir izne sahip olmayan kullanıcıların belirli endpoint’lere erişimini engelleyebiliriz.


using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

public class PermissionAuthorizationMiddleware
{
    private readonly RequestDelegate _next;

    public PermissionAuthorizationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var endpoint = context.GetEndpoint();
        if (endpoint != null)
        {
            // Endpoint'e veya ilgili metoda uygulanmış RequiresPermissionAttribute'ı ara
            var requiresPermissionAttribute = endpoint.Metadata.GetMetadata<RequiresPermissionAttribute>();

            if (requiresPermissionAttribute != null)
            {
                var requiredPermission = requiresPermissionAttribute.PermissionName;
                // Bu kısımda, kullanıcının 'requiredPermission' adlı izne sahip olup olmadığını kontrol edin
                // Örneğin, JWT claim'lerinden veya bir veritabanından sorgulayarak.
                bool userHasPermission = CheckUserPermissions(context.User, requiredPermission); // Varsayımsal metot

                if (!userHasPermission)
                {
                    context.Response.StatusCode = StatusCodes.Status403Forbidden; // Yetkisiz erişim
                    await context.Response.WriteAsync("You do not have the required permission.");
                    return; // İsteği sonlandır
                }
            }
        }

        await _next(context); // İzin varsa bir sonraki middleware'e geç
    }

    private bool CheckUserPermissions(System.Security.Claims.ClaimsPrincipal user, string permission)
    {
        // Gerçek bir uygulamada, kullanıcının rollerini/izinlerini kontrol eden mantık burada yer alacaktır.
        // Örneğin: user.IsInRole("Admin") || user.HasClaim("Permission", permission)
        if (user == null || !user.Identity.IsAuthenticated) return false;
        
        // Örnek basit bir kontrol: eğer istenen izin "AdminPanelAccess" ise, kullanıcının "Admin" rolüne sahip olması gerekir.
        if (permission == "AdminPanelAccess")
        {
            return user.IsInRole("Admin");
        }
        // Daha karmaşık izin kontrolleri burada yapılabilir.
        return true; // Varsayılan olarak izin ver. Gerçek uygulamada daha katı olunmalı.
    }
}

// Ve bu middleware'i eklemek için uzantı metodu:
public static class PermissionAuthorizationMiddlewareExtensions
{
    public static IApplicationBuilder UsePermissionAuthorization(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<PermissionAuthorizationMiddleware>();
    }
}

Bu yaklaşım, yetkilendirme mantığını merkezi bir middleware’e taşırken, her bir endpoint’in veya metodun gerektirdiği özel izinleri deklaratif olarak attribute’lar aracılığıyla belirtme esnekliğini sunar. Bu, kodun temizliğini artırır ve yetkilendirme kurallarının yönetimini kolaylaştırır.

Özetle, C# dilinde Middleware ve Attribute’lar, modern uygulama geliştirmenin temel taşlarındandır. Middleware, istek işleme boru hattı üzerinde eşsiz bir kontrol sağlayarak çapraz kesen sorunları modüler ve yönetilebilir bir şekilde çözmenizi sağlar. Attribute’lar ise, kodunuza anlam katmanları ekleyerek deklaratif programlamanın gücünü ortaya çıkarır ve çalışma zamanında dinamik davranışlar sergilemenize olanak tanır. Bu ikilinin doğru kullanımı, daha sürdürülebilir, ölçeklenebilir ve güçlü C# uygulamaları geliştirmenize büyük katkı sağlayacaktır.

“`