Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

Middleware كنمط مزين

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 97 من 107.

تُعد Middleware واحدة من أكثر التطبيقات العملية لنمط Decorator في لغة Go. في تطوير الويب، تقوم وظائف middleware بتغليف معالجات HTTP لإضافة اهتمامات شاملة (cross-cutting concerns) مثل تسجيل البيانات (logging)، أو المصادقة، أو التوقيت دون تعديل المعالج الأصلي.

الفكرة الجوهرية هي أن البرمجيات الوسيطة (middleware) تأخذ معالجاً (handler) وتعيد معالجاً جديداً بسلوك مضاف. وهذا يتبع نمط المزيّن (Decorator pattern) تماماً—نفس الواجهة في الدخول والخروج، مع وظائف محسنة:

type Handler func(request string) string

func LoggingMiddleware(next Handler) Handler {
    return func(request string) string {
        result := next(request)
        return "[LOG] " + result
    }
}

func AuthMiddleware(next Handler) Handler {
    return func(request string) string {
        return "[AUTH] " + next(request)
    }
}

تقوم كل برمجية وسيطة (middleware) بتغليف المعالج التالي في السلسلة، مع إضافة سلوكها قبل أو بعد استدعاء المعالج المغلف. يمكنك تكديس عدة برمجيات وسيطة من خلال تركيبها:

func MainHandler(request string) string {
    return "Response to: " + request
}

handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// [LOG] [AUTH] Response to: GET /users

يحدد ترتيب التغليف ترتيب التنفيذ - حيث يتم تشغيل البرمجية الوسيطة (middleware) الخارجية أولاً. يُستخدم هذا النمط على نطاق واسع في أطر عمل الويب بلغة Go لسلاسل معالجة الطلبات، مما يسهل إضافة أو إزالة الوظائف دون المساس بمنطق المعالج الأساسي.

challenge icon

التحدي

سهل

لنقم ببناء خط معالجة طلبات (request processing pipeline) باستخدام نمط Middleware as Decorator! ستقوم بإنشاء نظام حيث تقوم دوال الـ middleware بتغليف المعالجات (handlers) لإضافة وظائف مثل التوقيت، والتحقق من الصحة، والتنسيق — كل ذلك دون تعديل منطق المعالج الأساسي.

ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:

  • handler.go: حدد نوع المعالج (handler type) والمعالج الأساسي الذي يعالج الطلبات.

    قم بإنشاء نوع Handler كدالة تأخذ سلسلة نصية للطلب (request string) وتعيد سلسلة نصية للاستجابة (response string):

    type Handler func(request string) string

    قم بتنفيذ دالة BaseHandler التي تعيد Processed: [request] لأي طلب تستقبله.

  • middleware.go: قم بإنشاء دوال الـ middleware التي تغلف المعالجات بسلوك إضافي.

    قم ببناء ثلاث دوال middleware، كل منها تأخذ Handler وتعيد Handler جديداً:

    • TimingMiddleware — يغلف الاستجابة بمعلومات التوقيت، ويعيد [TIMING] [response]
    • ValidationMiddleware — يضيف بادئة التحقق، ويعيد [VALID] [response]
    • UppercaseMiddleware — يحول الاستجابة بالكامل إلى أحرف كبيرة (uppercase)

    يجب على كل middleware استدعاء المعالج المغلف وتحسين نتيجته.

  • main.go: قم ببناء خط معالجة عن طريق تركيب الـ middleware.

    اقرأ عدد طبقات الـ middleware المراد تطبيقها. ثم لكل طبقة، اقرأ نوع الـ middleware (إما timing، أو validation، أو uppercase) وقم بتغليف المعالج الحالي بهذا الـ middleware. قم بتطبيقها بالترتيب الذي قُرئت به — بحيث يصبح أول middleware تمت قراءته هو الغلاف الخارجي (outermost wrapper).

    بعد بناء خط المعالجة، اقرأ سلسلة الطلب النصية، وقم بمعالجتها من خلال المعالج المركب، واطبع النتيجة.

سيتم توفير المدخلات التالية:

  • السطر 1: عدد طبقات الـ middleware
  • الأسطر التالية: نوع middleware واحد في كل سطر (timing، أو validation، أو uppercase)
  • السطر الأخير: الطلب المراد معالجته

على سبيل المثال، إذا كان المدخل:

2
timing
validation
GET /users

يجب أن يكون مخرجك:

[TIMING] [VALID] Processed: GET /users

وإذا كان المدخل:

3
uppercase
timing
validation
POST /data

يجب أن يكون مخرجك:

[TIMING] [VALID] PROCESSED: POST /DATA

وإذا كان المدخل:

1
uppercase
hello world

يجب أن يكون مخرجك:

PROCESSED: HELLO WORLD

وإذا كان المدخل:

0
simple request

يجب أن يكون مخرجك:

Processed: simple request

لاحظ كيف يؤثر ترتيب تغليف الـ middleware على المخرج — أول middleware يتم تطبيقه (الأبعد خارجياً) يعمل أولاً ويرى النتيجة النهائية من جميع الـ middleware الداخلية. الـ middleware الخاص بـ uppercase في المثال الثاني يعمل آخراً (الأكثر داخلية)، لذا فهو يحول الاستجابة الأساسية إلى أحرف كبيرة قبل أن يضيف التوقيت والتحقق بادئاتهما!

ورقة مرجعية

تتبع البرمجيات الوسيطة (Middleware) في لغة Go نمط المزيّن (Decorator pattern) من خلال تغليف المعالجات (handlers) لإضافة وظائف دون تعديل المعالج الأصلي.

عرّف نوع المعالج كدالة:

type Handler func(request string) string

تأخذ دوال البرمجيات الوسيطة معالجاً وتُرجع معالجاً جديداً بسلوك محسّن:

func LoggingMiddleware(next Handler) Handler {
    return func(request string) string {
        result := next(request)
        return "[LOG] " + result
    }
}

func AuthMiddleware(next Handler) Handler {
    return func(request string) string {
        return "[AUTH] " + next(request)
    }
}

قم بتكديس برمجيات وسيطة متعددة من خلال دمجها:

func MainHandler(request string) string {
    return "Response to: " + request
}

handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// المخرجات: [LOG] [AUTH] Response to: GET /users

يتم تشغيل البرمجية الوسيطة الخارجية أولاً، مما يحدد ترتيب التنفيذ. يمكن لكل برمجية وسيطة إضافة سلوك قبل أو بعد استدعاء المعالج المغلّف.

جرّب بنفسك

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// قراءة عدد طبقات middleware
	nLine, _ := reader.ReadString('\n')
	n, _ := strconv.Atoi(strings.TrimSpace(nLine))

	// قراءة أنواع middleware
	middlewareTypes := make([]string, n)
	for i := 0; i < n; i++ {
		line, _ := reader.ReadString('\n')
		middlewareTypes[i] = strings.TrimSpace(line)
	}

	// قراءة request
	request, _ := reader.ReadString('\n')
	request = strings.TrimSpace(request)

	// TODO: ابدأ بـ BaseHandler

	// TODO: قم ببناء pipeline عن طريق تغليف handler بـ middleware
	// تطبيق middleware بالترتيب - أول middleware يتم قراءته يصبح الغلاف الخارجي
	// تلميح: قم بالمرور عبر middlewareTypes بترتيب عكسي (REVERSE) لتحقيق ذلك
	// آخر middleware يتم تطبيقه سيكون هو الخارجي (يعمل أولاً)

	// TODO: قم بمعالجة request من خلال handler المركب واطبع النتيجة
	fmt.Println("TODO: implement pipeline")
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming