0Pricing
C# Academy · درس

كتابة Middleware مخصّص

أنشئوا middleware مضمنًا باستخدام Use/Run/Map، وmiddleware قائمًا على الفئات باستخدام InvokeAsync للمكوّنات القابلة لإعادة الاستخدام.

كتابة Middleware مخصّص درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.

لماذا نستخدم برمجية وسيطة مخصّصة؟

تتعامل البرمجية الوسيطة المخصّصة مع الاهتمامات المشتركة التي تنطبق على طلبات كثيرة، مثل التسجيل وقياس الوقت ومعالجة الأخطاء والمصادقة والضغط والتخزين المؤقت. وكتابتها مرة واحدة يجعل المنطق يُشغَّل تلقائيًا لكل طلب مطابق.

برمجية وسيطة مضمنة باستخدام Use

أسرع طريقة لإضافة برمجية وسيطة هي تمرير مفوّض إلى app.Use(). وهذا مناسب لإنشاء نموذج أولي أو لتنفيذ منطق بسيط جدًا.

app.Use(async (context, next) =>
{
    var start = DateTimeOffset.UtcNow;

    await next(context);

    var elapsed = DateTimeOffset.UtcNow - start;
    context.Response.Headers.Append(
        "X-Elapsed", elapsed.TotalMilliseconds.ToString("F0") + "ms");
});

برمجية وسيطة قائمة على فئة وفق الاصطلاح

تحتوي فئة البرمجية الوسيطة التقليدية على مُنشئ يستقبل RequestDelegate وطريقة عامة باسم InvokeAsync. ويمكن حقن معاملات المُنشئ الإضافية من DI.

public class SecurityHeadersMiddleware
{
    private readonly RequestDelegate _next;

    public SecurityHeadersMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        var headers = context.Response.Headers;
        headers.Append("X-Content-Type-Options", "nosniff");
        headers.Append("X-Frame-Options", "DENY");
        headers.Append("X-XSS-Protection", "1; mode=block");
        headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");

        await _next(context);
    }
}

// Extension method for clean registration:
public static class SecurityHeadersExtensions
{
    public static IApplicationBuilder UseSecurityHeaders(
        this IApplicationBuilder app) =>
        app.UseMiddleware<SecurityHeadersMiddleware>();
}

IMiddleware للتبعيات ذات النطاق Scoped

إذا كانت البرمجية الوسيطة تحتاج إلى خدمات ذات نطاق، مثل DbContext، فنفّذ IMiddleware. سجّلها بالنطاق Scoped، وسيحلّها الإطار من جديد لكل طلب.

public class AuditMiddleware : IMiddleware
{
    private readonly AuditDbContext _db;
    private readonly IHttpContextAccessor _http;

    public AuditMiddleware(AuditDbContext db, IHttpContextAccessor http)
    {
        _db = db;
        _http = http;
    }

    public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
    {
        await next(ctx);

        if (ctx.Request.Method != "GET")
        {
            _db.AuditLogs.Add(new AuditLog
            {
                User   = ctx.User.Identity?.Name,
                Path   = ctx.Request.Path,
                Status = ctx.Response.StatusCode,
                At     = DateTime.UtcNow
            });
            await _db.SaveChangesAsync();
        }
    }
}

builder.Services.AddScoped<AuditMiddleware>();
app.UseMiddleware<AuditMiddleware>();

البرمجية الوسيطة مع Options

مرّر الإعدادات إلى البرمجية الوسيطة عبر كائن خيارات في المُنشئ. سجّل الخيارات في حاوية DI، أو مرّرها مباشرةً عند تسجيل البرمجية الوسيطة.

public class ThrottleOptions
{
    public int MaxRequestsPerSecond { get; set; } = 100;
    public string[] ExcludedPaths { get; set; } = Array.Empty<string>();
}

public class ThrottleMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ThrottleOptions _options;

    public ThrottleMiddleware(RequestDelegate next, ThrottleOptions options)
    {
        _next   = next;
        _options = options;
    }
    // ...
}

// Registration with options:
app.UseMiddleware<ThrottleMiddleware>(new ThrottleOptions
{
    MaxRequestsPerSecond = 50
});

برمجية وسيطة لمعالجة الاستثناءات

تلتقط برمجية وسيطة مخصّصة لمعالجة الاستثناءات جميع الاستثناءات غير المعالجة، وتعيد استجابات أخطاء منظمة.

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

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> l)
    {
        _next = next;
        _logger = l;
    }

    public async Task InvokeAsync(HttpContext ctx)
    {
        try { await _next(ctx); }
        catch (NotFoundException ex)
        {
            ctx.Response.StatusCode = 404;
            await ctx.Response.WriteAsJsonAsync(new { error = ex.Message });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception");
            ctx.Response.StatusCode = 500;
            await ctx.Response.WriteAsJsonAsync(new { error = "Internal server error" });
        }
    }
}

برمجية وسيطة لتسجيل الطلبات

تسجّل برمجية وسيطة منظمة لتسجيل الطلبات والاستجابات الطريقة والمسار ورمز الحالة والوقت المنقضي لكل طلب.

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _log;

    public RequestLoggingMiddleware(RequestDelegate n, ILogger<RequestLoggingMiddleware> l)
    { _next = n; _log = l; }

    public async Task InvokeAsync(HttpContext ctx)
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        await _next(ctx);
        sw.Stop();

        _log.LogInformation(
            "{Method} {Path} -> {Status} in {Ms}ms",
            ctx.Request.Method,
            ctx.Request.Path,
            ctx.Response.StatusCode,
            sw.ElapsedMilliseconds);
    }
}

برمجية وسيطة شرطية باستخدام UseWhen

تفرّع UseWhen مسار التنفيذ بشكل شرطي استنادًا إلى دالة شرطية، من دون تقسيمه نهائيًا. ثم يندمج الفرع مجددًا مع مسار التنفيذ الرئيسي.

// Apply authentication middleware only to /api/* routes
app.UseWhen(
    ctx => ctx.Request.Path.StartsWithSegments("/api"),
    apiApp =>
    {
        apiApp.UseAuthentication();
        apiApp.UseAuthorization();
    });

// Public routes (/public/*) bypass authentication
app.MapGet("/public/health", () => "OK");
app.MapGet("/api/data", [Authorize] async (DataService s) => await s.GetAsync());

برمجية وسيطة لتخزين الاستجابات مؤقتًا

تخزّن البرمجية الوسيطة المضمنة لتخزين الاستجابات مؤقتًا الاستجابات الكاملة في الذاكرة. ويمكنك ضبط سياسات التخزين المؤقت لكل نقطة نهاية باستخدام الرؤوس أو السمات.

builder.Services.AddResponseCaching();

app.UseResponseCaching();

app.MapGet("/products", async (AppDbContext db) =>
{
    var products = await db.Products.AsNoTracking().ToListAsync();
    return Results.Ok(products);
})
.CacheOutput(p => p.Expire(TimeSpan.FromMinutes(2)).Tag("products"));

من واقع عملي: برمجية وسيطة لوضع الصيانة

تقرأ برمجية وسيطة لوضع الصيانة علامة من الإعدادات، مع دعم إعادة التحميل الفوري عبر IOptionsMonitor، وترفض الطلبات التي لا تتعلق بفحوصات الصحة أثناء الصيانة.

public class MaintenanceMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IOptionsMonitor<MaintenanceOptions> _options;

    public MaintenanceMiddleware(RequestDelegate next,
        IOptionsMonitor<MaintenanceOptions> opts)
    { _next = next; _options = opts; }

    public async Task InvokeAsync(HttpContext ctx)
    {
        if (_options.CurrentValue.IsEnabled
            && !ctx.Request.Path.StartsWithSegments("/health"))
        {
            ctx.Response.StatusCode = 503;
            ctx.Response.Headers.Append("Retry-After", "300");
            await ctx.Response.WriteAsJsonAsync(
                new { message = "Service under maintenance" });
            return;
        }
        await _next(ctx);
    }
}

تحقق سريع

لماذا ينبغي تنفيذ IMiddleware بدلًا من النهج القائم على الاصطلاح عند الحاجة إلى خدمة ذات نطاق، مثل DbContext؟

مراجعة: كتابة برمجيات وسيطة مخصّصة

أهم النقاط:

  • المضمنة (مفوّض Use): سريعة ومناسبة للسطر الواحد؛ والقائمة على فئة: قابلة لإعادة الاستخدام والاختبار
  • الفئة التقليدية: يستقبل مُنشئها RequestDelegate؛ وتستخدم InvokeAsync(HttpContext)
  • IMiddleware: حلّ التبعيات عبر DI لكل طلب — وهو مطلوب للتبعيات ذات النطاق Scoped
  • UseWhen: تفرّع شرطي يعاود الانضمام إلى مسار التنفيذ الرئيسي
  • اكتب برمجية معالجة الاستثناءات أولًا، أي في الطبقة الخارجية، لالتقاط جميع الأخطاء
  • أنشئ طرائق امتداد (UseXxx) لتسجيل واضح وسهل الاكتشاف

الأسئلة الشائعة

هل درس «كتابة Middleware مخصّص» مجاني؟

نعم — نص درس «كتابة Middleware مخصّص» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.

ماذا ستتعلم في «كتابة Middleware مخصّص»؟

أنشئوا middleware مضمنًا باستخدام Use/Run/Map، وmiddleware قائمًا على الفئات باستخدام InvokeAsync للمكوّنات القابلة لإعادة الاستخدام. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟

لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «كتابة Middleware مخصّص»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟

نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. نظرة عامة على مسار ASP.NET Core
  2. كتابة Middleware مخصّص
  3. الإيقاف المبكر والتفرّع
  4. ترتيب Middleware وMiddleware المضمّن
← العودة إلى C# Academy