مقدمة إلى الفلاتر (Filters)
مقدمة إلى الفلاتر (Filters)
فلتر السيرفلت (servlet filter) هو مكوّن قابل لإعادة الاستخدام يعترض كل طلب HTTP قبل وصوله إلى السيرفلت المستهدف (أو JSP) وكل استجابة HTTP قبل مغادرتها الخادم. تُشكّل الفلاتر سلسلة — تُعرف بـسلسلة الفلاتر (filter chain) — ويقرر كل فلتر باستقلالية تامة ما إذا كان سيمرّر الطلب، أو يعدّله، أو يوقفه كليًّا. تفصل هذه البنية بشكل نظيف الاهتمامات المشتركة (كالتسجيل، والتحقق من المصادقة، والضغط، ورؤوس CORS) عن السيرفلتات التجارية الخاصة بك.
عقد واجهة Filter
كل فلتر ينفّذ الواجهة jakarta.servlet.Filter التي تُعلن عن ثلاثة توابع:
init(FilterConfig config)— يُستدعى مرة واحدة عندما ينشئ الحاوي الفلتر. استخدمه لقراءة معاملات التهيئة أو اقتناء الموارد.doFilter(ServletRequest request, ServletResponse response, FilterChain chain)— يُستدعى في كل طلب مطابق. هنا تعيش منطق عملك.destroy()— يُستدعى مرة واحدة قبل إزالة الحاوي للفلتر. حرّر أي موارد اقتنيتها فيinit.
init() وdestroy() تنفيذات افتراضية فارغة في الواجهة، لذا تتجاوزهما فقط عندما يكون لديك ما تفعله. يبقى doFilter مجردًا ويجب دائمًا تنفيذه.
سلسلة الفلاتر
يبني الحاوي سلسلة مرتّبة من جميع الفلاتر التي يتطابق نمط URL الخاص بها مع الطلب الحالي. عندما تستدعي chain.doFilter(request, response) داخل فلترك، ينتقل التحكم إلى الفلتر التالي في السلسلة (أو إلى السيرفلت المستهدف إذا لم تتبقَّ فلاتر). يمكنك تشغيل منطق قبل هذا الاستدعاء (معالجة مسبقة) ومنطق بعد عودته (معالجة لاحقة). إذا لم تستدعِ chain.doFilter أبدًا، فإنك تقطع السلسلة — ولن يُستدعى السيرفلت المستهدف قط. هذه هي الآلية التي تستخدمها فلاتر المصادقة لحجب الطلبات غير الموثّقة.
لاحظ البنية: كل شيء قبل chain.doFilter ينفَّذ عند الدخول، وكل شيء بعده ينفَّذ عند الخروج. ينفَّذ السيرفلت المستهدف داخل تلك الفجوة.
تسجيل الفلتر باستخدام @WebFilter
التعليق التوضيحي @WebFilter (متاح منذ Servlet 3.0) هو الطريقة الحديثة والخالية من XML لتعريف الفلتر. أبرز خصائصه:
value/urlPatterns— أنماط URL التي يُطبَّق عليها هذا الفلتر. تدعم أحرف البدل:"/api/*"،"*.json".servletNames— استهداف أسماء سيرفلت محددة بدلًا من أنماط URL.filterName— تجاوز الاسم الافتراضي (اسم الفئة).initParams— تمرير أزواج مفتاح-قيمة@WebInitParamالقابلة للقراءة فيinit(FilterConfig).dispatcherTypes— التحكم في ما إذا كان الفلتر يُشغَّل عند الإرسالREQUESTأوFORWARDأوINCLUDEأوERRORأوASYNC. الافتراضي هوREQUESTفقط.
doFilter أنواع ServletRequest/ServletResponse الأساسية (التي تغطي بروتوكولات غير HTTP أيضًا)، لكن في تطبيق الويب تكون الكائنات دائمًا متغيرات HTTP. حوّل مبكرًا وخصّص لمتغير محلي بدلًا من التحويل مرارًا.
ترتيب الفلاتر
عندما تتطابق فلاتر متعددة مع نفس URL، على الحاوي اختيار ترتيب. القاعدة هي:
- الفلاتر المسجَّلة بالتعليقات التوضيحية (
@WebFilter): لا تضمن مواصفة Servlet أي ترتيب بينها. الترتيب يعتمد على التنفيذ. - الفلاتر المسجَّلة في
web.xml: تنفَّذ بالترتيب الذي تظهر فيه عناصر<filter-mapping>في الملف. - مختلطة: تُنفَّذ الفلاتر المُعلَنة في XML أولًا، ثم الفلاتر المُعلَنة بالتعليقات التوضيحية بترتيب غير محدد.
إذا كان الترتيب مهمًا — وهو كذلك بالنسبة لفلاتر الأمان دائمًا تقريبًا — فأعلن فلاترك في web.xml أو استخدم نهج ServletContextListener عبر ServletContext.addFilter().
في هذا الإعداد يلتف TimingFilter حول AuthFilter: يدخل الطلب TimingFilter، ثم (إذا تطابق URL) AuthFilter، ثم السيرفلت المستهدف. يُفكّ الاستجابة بترتيب عكسي — السيرفلت، ثم معالجة AuthFilter اللاحقة، ثم معالجة TimingFilter اللاحقة.
تغليف الطلب أو الاستجابة
لا تستطيع الفلاتر الفحص فحسب، بل يمكنها أيضًا استبدال كائنات الطلب أو الاستجابة بتغليفها. توفّر الواجهة البرمجية HttpServletRequestWrapper وHttpServletResponseWrapper لهذا الغرض. حالة الاستخدام الشائعة هي التقاط جسم الاستجابة (للتسجيل أو الضغط) بإحلال غلاف يخزّن الإخراج مؤقتًا قبل إرساله.
chain.doFilter(). إذا كان فلترك يتخطاها شرطيًا (مثلًا عند فشل المصادقة)، تأكد أن الفرع الآخر لا يزال يكتب استجابة كاملة (إعادة توجيه، أو حالة خطأ، أو جسم). إرجاع doFilter دون استدعاء السلسلة ودون كتابة استجابة يترك العميل معلّقًا مع 200 فارغ.
متى تستخدم Filters مقابل Interceptors
الفلاتر هي ميزة Servlet API وتعمل على طبقة HTTP — ترى الطلبات والاستجابات الخام. يوفّر Spring MVC كائن HandlerInterceptor الخاص به يعمل على طبقة المُرسِّل، بعد أن يحلّ Spring تابع المعالج. اختر الفلاتر للاهتمامات التي هي حقًا على مستوى النقل (رؤوس الأمان، الترميز، التسجيل) والمعترضات للاهتمامات المرتبطة بدورة حياة طلبات Spring. كلا الآليتين تكمّلان بعضهما وتُستخدمان معًا في الإنتاج.
الخلاصة
تُعدّ واجهة Filter وسلسلة الفلاتر إجابة Servlet API على الاهتمامات المشتركة. نفّذ doFilter، واستدعِ chain.doFilter لتمرير التحكم للأمام، وضع المنطق قبل هذا الاستدعاء أو بعده للمعالجة المسبقة/اللاحقة. استخدم @WebFilter للحالات البسيطة وweb.xml (أو التسجيل البرمجي) عندما يكون الترتيب حاسمًا. يبني الدرس التالي على هذا الأساس بتنفيذات فلاتر عملية وجاهزة للإنتاج.