30. تمرين عملي: الإصدار 12
في هذا الفصل، سنكتب تطبيق ويب يتبع بنية MVC (نموذج-عرض-وحدة تحكم). سيكون التطبيق قادرًا على إرجاع استجابات بثلاثة تنسيقات: JSON و XML و HTML. هناك زيادة كبيرة في التعقيد بين ما نحن على وشك القيام به وما قمنا به سابقًا. سنعيد استخدام معظم المفاهيم التي تمت تغطيتها حتى الآن وسنوضح بالتفصيل جميع الخطوات المؤدية إلى التطبيق النهائي.
30.1. بنية MVC
سنقوم بتنفيذ نمط بنية MVC (Model–View–Controller) على النحو التالي:

ستتم معالجة طلب العميل على النحو التالي:
- 1 - الطلب
ستكون عناوين URL المطلوبة على النحو التالي: http://machine:port/action/param1/param2/… سيستخدم [المتحكم الرئيسي] ملف تكوين لـ"توجيه" الطلب إلى المتحكم الصحيح. للقيام بذلك، سيستخدم حقل [action] في عنوان URL. يتكون باقي عنوان URL [param1/param2/…] من معلمات اختيارية سيتم تمريرها إلى الإجراء. يشير الحرف "C" في MVC هنا إلى السلسلة [وحدة التحكم الرئيسية، وحدة التحكم / الإجراء]. إذا لم تتمكن أي وحدة تحكم من معالجة الإجراء المطلوب، فسيرد خادم الويب بأن عنوان URL المطلوب لم يتم العثور عليه.
- 2 - المعالجة
- يمكن للإجراء المحدد [2a] استخدام المعلمات التي مررها إليه [وحدة التحكم الرئيسية]. قد تأتي هذه المعلمات من مصدرين:
- مسار [/param1/param2/…] لعنوان URL،
- من المعلمات المنشورة في نص طلب العميل؛
- عند معالجة طلب المستخدم، قد يتطلب الإجراء طبقة [الأعمال] [2b]. بمجرد معالجة طلب العميل، قد يؤدي ذلك إلى استجابات مختلفة. ومن الأمثلة الكلاسيكية على ذلك:
- استجابة خطأ إذا تعذر معالجة الطلب بشكل صحيح؛
- استجابة تأكيد في الحالات الأخرى؛
- سيُرجع [المتحكم / الإجراء] استجابته [2c] إلى المتحكم الرئيسي مع رمز الحالة. وستمثل رموز الحالة هذه بشكل فريد الحالة الحالية للتطبيق. وسيكون إما رمز نجاح أو رمز خطأ؛
- 3 - الاستجابة
- اعتمادًا على ما إذا كان العميل قد طلب استجابة JSON أو XML أو HTML، سيقوم [المتحكم الرئيسي] بإنشاء مثيل [3a] لنوع الاستجابة المناسب وإصدار تعليمات له بإرسال الاستجابة إلى العميل. سيقوم [المتحكم الرئيسي] بتمرير كل من الاستجابة ورمز الحالة المقدم من [المتحكم/الإجراء] الذي تم تنفيذه؛
- إذا كان الرد المطلوب من نوع JSON أو XML، فسيقوم الرد المحدد بتنسيق الرد الوارد من [وحدة التحكم/الإجراء] الذي تم تزويده به وإرساله [3c]. ويمكن أن يكون العميل القادر على معالجة هذا الرد عبارة عن برنامج نصي لـ Python على وحدة التحكم أو برنامج نصي لـ JavaScript مدمج في صفحة HTML؛
- إذا كانت الاستجابة المطلوبة من نوع HTML، فستقوم الاستجابة المحددة باختيار [3b] إحدى طرق عرض HTML [Vuei] باستخدام رمز الحالة المقدم لها. هذا هو V في MVC. تتوافق طريقة عرض واحدة مع رمز حالة واحد. ستعرض طريقة العرض V هذه الاستجابة من [وحدة التحكم / الإجراء] التي تم تنفيذها. وهي تغلف البيانات من هذه الاستجابة في HTML و CSS و JavaScript. تسمى هذه البيانات نموذج العرض. هذا هو M في MVC. غالبًا ما يكون العميل متصفحًا؛
الآن، دعونا نوضح العلاقة بين بنية الويب MVC والبنية الطبقية. اعتمادًا على كيفية تعريف النموذج، قد يكون هذان المفهومان مرتبطين أو غير مرتبطين. دعونا نفكر في تطبيق ويب MVC أحادي الطبقة:

في المثال أعلاه، يشتمل كل من [وحدة التحكم / الإجراء] على أجزاء من طبقتي [الأعمال] و[DAO]. في طبقة [الويب]، لدينا بنية MVC، لكن التطبيق ككل لا يحتوي على بنية طبقية. هنا، توجد طبقة واحدة فقط — طبقة الويب — تتولى معالجة كل شيء.
الآن، لنفكر في بنية ويب متعددة الطبقات:

يمكن تنفيذ طبقة [الويب] دون اتباع نموذج MVC. عندئذٍ يكون لدينا بنية متعددة الطبقات، لكن طبقة الويب لا تنفذ نموذج MVC.
على سبيل المثال، في عالم .NET، يمكن تنفيذ طبقة [الويب] أعلاه باستخدام ASP.NET MVC، مما ينتج عنه بنية ذات طبقات مع طبقة [ويب] على غرار MVC. بعد القيام بذلك، يمكننا استبدال طبقة ASP.NET MVC هذه بطبقة ASP.NET كلاسيكية (WebForms) مع الحفاظ على بقية العناصر (منطق الأعمال، DAO، برنامج التشغيل) دون تغيير. وبذلك نحصل على بنية متعددة الطبقات مع طبقة [الويب] التي لم تعد قائمة على MVC.
في MVC، قلنا إن نموذج M هو نموذج عرض V، أي مجموعة البيانات التي يعرضها عرض V. وهناك تعريف آخر لنموذج M في MVC:

يعتبر العديد من المؤلفين أن ما يقع على يمين طبقة [الويب] يشكل نموذج M في MVC. لتجنب الغموض، يمكننا الإشارة إلى:
- نموذج المجال عند الإشارة إلى كل ما يقع على يمين طبقة [الويب]؛
- نموذج العرض عند الإشارة إلى البيانات التي تعرضها طريقة العرض V؛
فيما يلي، عندما نشير إلى النموذج، فإننا نشير دائمًا إلى نموذج العرض.
30.2. بنية تطبيق العميل/الخادم
ستتضمن تطبيق الويب البنية التالية:

- في [1]، سيحتوي خادم الويب على نوعين من العملاء:
- في [2]، عميل وحدة التحكم الذي سيتبادل JSON و XML مع الخادم؛
- في [3]، متصفح سيتلقى HTML من الخادم ويعرضه؛
- يحتفظ خادم الويب [1] بطبقتي [الأعمال] و[DAO] من الإصدارات السابقة؛
- سيتم تحديث عميل الويب [2] لمراعاة عناوين URL الجديدة لخدمات تطبيق الويب؛
- يجب كتابة تطبيق HTML الذي يعرضه المتصفح من البداية؛
سنقوم بتطوير التطبيق على عدة مراحل:
- سنقوم بتطوير إصدار JSON للخادم. سنقوم باختبار عناوين URL لخدمات الخادم واحدة تلو الأخرى باستخدام عميل Postman. تسمح لنا هذه الطريقة ببناء إطار عمل خادم الويب دون القلق بشأن طرق عرض التطبيق (=HTML)؛
- بعد اختبار خادم JSON باستخدام Postman، سنقوم باختباره باستخدام عميل وحدة التحكم؛
- ثم سننتقل إلى النسخة XML من الخادم. وقد رأينا أن الانتقال من JSON إلى XML أمر بسيط؛
- وأخيرًا، سننتقل إلى إصدار HTML للخادم. سنقوم ببناء بنية MVC وتحديد طرق العرض التي سيتم عرضها. سيتم اختبار تطبيق HTML باستخدام كل من عميل Postman ومتصفح قياسي؛
30.3. هيكل دليل كود الخادم

- في [1: الخادم الويب ككل؛
- في [2]: في الوقت الحالي، سنتجاهل المجلدات [static، templates، tests_views]، التي تتعلق بإصدار HTML للخادم. خارج هذا المجلد، سنجد البرنامج النصي الرئيسي [main] وتكوينه؛
- في [3]، وحدات التحكم في خادم الويب. ستكون هذه مثيلات للفئات؛
![]() | ![]() |
- في [4]، ستتم معالجة استجابة HTTP للخادم بواسطة الفئات؛
- في [5]، نحتفظ بملف السجل من الخوادم السابقة؛
عندما نقوم بإنشاء النسخة HTML من الخادم، ستدخل مجلدات أخرى في اللعبة:
![]() | ![]() |
- في [6]، العناصر الثابتة لتطبيق HTML؛
- في [7]، قوالب تطبيق HTML مقسمة إلى طرق عرض [9] وأجزاء طرق عرض [8]؛
- في [9]، الفئات التي تنفذ نماذج العرض؛
30.4. عناوين URL لخدمات التطبيق
لبناء خادم الويب، سنقوم بما يلي:
- بناءً على طرق عرض تطبيق HTML، سنحدد الإجراءات التي يجب أن ينفذها تطبيق الويب. سنستخدم طرق العرض الفعلية هنا، ولكن يمكن أن تكون هذه مجرد طرق عرض على الورق؛
- بناءً على هذه الإجراءات، سنحدد عناوين URL لخدمات تطبيق HTML؛
- سننفذ عناوين URL لخدمات هذه باستخدام خادم يعرض JSON. وهذا يسمح لنا بتحديد إطار عمل خادم الويب دون القلق بشأن صفحات HTML التي سيتم عرضها. سنختبر عناوين URL لخدمات هذه باستخدام Postman؛
- سنقوم بعد ذلك باختبار خادم JSON الخاص بنا باستخدام عميل وحدة التحكم؛
- بمجرد التحقق من صحة خادم JSON، سننتقل إلى كتابة تطبيق HTML؛
ستكون طريقة العرض الأولى هي طريقة عرض المصادقة:

- سيُطلق على الإجراء المؤدي إلى هذا العرض الأول اسم [init-session] [1]؛
- سيؤدي النقر على زر [التحقق] إلى تشغيل الإجراء [authenticate-user] مع معلمتين منشورتين [2-3]؛
عرض حساب الضريبة:

- في [1]، الإجراء [authenticate-user] الذي أدى إلى هذه الشاشة؛
- في [2]، يؤدي النقر على زر [Validate] إلى تشغيل الإجراء [calculate-tax] مع ثلاثة معلمات منشورة [2-5]؛
- يؤدي النقر على الرابط [6] إلى تشغيل الإجراء [list-simulations] بدون معلمات؛
- يؤدي النقر على الرابط [7] إلى تشغيل الإجراء [end-session] بدون معلمات؛
تعرض العرض الثالث المحاكاة التي أجراها المستخدم المصادق عليه:

- في [3]، الإجراء [list-simulations] الذي أدى إلى ظهور هذه الصفحة؛
- في [2]، يؤدي النقر على رابط [حذف] إلى تشغيل الإجراء [delete-simulation] مع معلمة: رقم المحاكاة المراد حذفها من القائمة؛
- يؤدي النقر على الرابط [3] إلى تشغيل الإجراء [display-tax-calculation] بدون معلمات، مما يعيد عرض عرض حساب الضريبة؛
- يؤدي النقر على الرابط [4] إلى تشغيل الإجراء [end-session] بدون معلمات؛
باستخدام هذه المعلومات الأولية، يمكننا تحديد عناوين URL المختلفة لخدمات الخادم:
الإجراء | الدور | سياق التنفيذ |
/init-session | يُستخدم لتعيين نوع (json، xml، html) الاستجابات المطلوبة | طلب GET يمكن إرساله في أي وقت |
/authenticate-user | يوافق على تسجيل دخول المستخدم أو يرفضه | طلب POST. يجب أن يحتوي الطلب على معلمتين مرسلتين [user, password] لا يمكن إصداره إلا إذا كان نوع الجلسة (json، xml، html) معروفًا |
/calculate-tax | يقوم بمحاكاة حساب الضريبة | طلب POST. يجب أن يحتوي الطلب على ثلاثة معلمات مرسلة [married، children، salary] لا يمكن إصداره إلا إذا كان نوع الجلسة (json، xml، html) معروفًا وتم مصادقة المستخدم |
/list-simulations | طلب لعرض قائمة المحاكاة التي تم إجراؤها منذ بداية الجلسة | طلب GET. لا يمكن إصداره إلا إذا كان نوع الجلسة (json، xml، html) معروفًا وتم توثيق المستخدم |
/delete-simulation/number | حذف محاكاة من قائمة المحاكاة | طلب GET. لا يمكن إصداره إلا إذا كان نوع الجلسة (json، xml، html) معروفًا وتم توثيق المستخدم |
/display-tax-calculation | يعرض صفحة HTML لحساب الضريبة | طلب GET. لا يمكن إصداره إلا إذا كان نوع الجلسة (json، xml، html) معروفًا وتم مصادقة المستخدم |
/end-session | ينهي جلسة المحاكاة. | من الناحية الفنية، يتم حذف جلسة الويب القديمة وإنشاء جلسة جديدة لا يمكن إصداره إلا إذا كان نوع الجلسة (json، xml، html) معروفًا وتم توثيق المستخدم |
سيتم استخدام عناوين URL المختلفة للخدمة لكل من خادم HTML وخوادم JSON أو XML. سيتم استخدام عنوانين URL حصريًا للخادمين الأخيرين: وهما عنوانا URL من الإصدار السابق لخادم العميل/الويب الذي نعيد استخدامه هنا:
الإجراء | الدور | سياق التنفيذ |
/get-admindata | تُرجع البيانات الضريبية المستخدمة لحساب الضريبة | GET. يُستخدم فقط إذا كان نوع الجلسة json أو xml. يجب مصادقة المستخدم |
/calculate-taxes | يحسب الضريبة لقائمة من دافعي الضرائب المنشورة في طلب JSON | . يُستخدم فقط إذا كان نوع الجلسة json أو xml. يجب أن يكون المستخدم قد تمت مصادقته |
ستعمل جميع وحدات التحكم المرتبطة بهذه الإجراءات بنفس الطريقة:
- ستتحقق من معلماتها. توجد هذه المعلمات في الكائن:
- [request.path] للمعلمات الموجودة في عنوان URL بالشكل [/action/param1/param2/…]؛
- في الكائن [request.form] للمعلمات التي يتم إرسالها كـ [x-www-form-urlencoded] في نص الطلب؛
- في كائن [request.data] بالنسبة للقيم المرسلة بتنسيق JSON في نص الطلب؛
- يشبه وحدة التحكم وظيفة أو طريقة تتحقق من صحة معلماتها. لكن الأمر أكثر تعقيدًا قليلاً بالنسبة لوحدة التحكم:
- قد تكون المعلمات المتوقعة مفقودة؛
- المعلمات التي يستردها المتحكم هي سلاسل نصية. إذا كانت المعلمة المتوقعة رقمًا، فيجب على المتحكم التحقق من أن السلسلة النصية للمعلمة تمثل رقمًا بالفعل؛
- بمجرد التحقق من وجود المعلمات المتوقعة وصحة صياغتها، يجب عليك التحقق من صحتها في سياق التنفيذ الحالي. هذا السياق موجود في الجلسة. مثال المصادقة هو مثال على سياق التنفيذ. يجب معالجة إجراءات معينة فقط بعد مصادقة العميل. بشكل عام، يشير مفتاح في الجلسة إلى ما إذا كانت هذه المصادقة قد تمت أم لا؛
- بمجرد اكتمال الفحوصات السابقة، يمكن للوحدة الثانوية المضي قدمًا. عملية التحقق من المعلمات هذه مهمة جدًا. لا يمكننا قبول أن يرسل لنا العميل بيانات عشوائية في أي مرحلة من مراحل دورة حياة التطبيق. يجب أن نحافظ على السيطرة الكاملة على دورة حياة التطبيق؛
- بمجرد الانتهاء من عملها، تعيد وحدة التحكم الثانوية قاموسًا بمفاتيح [action, state, response] إلى وحدة التحكم الرئيسية التي استدعتها:
- [action] هو الإجراء الذي تم تنفيذه للتو؛
- [state] هو رقم مكون من ثلاثة أرقام يشير إلى نتيجة معالجة الإجراء:
- [x00] يشير إلى نجاح المعالجة؛
- [x01] يشير إلى فشل المعالجة؛
- [response] هو قاموس النتائج في شكل {‘response’:object}. سيكون للكائن هياكل مختلفة اعتمادًا على الإجراء الذي تتم معالجته؛
سنستعرض الآن مختلف وحدات التحكم — أو بعبارة أخرى، الإجراءات المختلفة التي تتولى هذه الوحدات معالجتها — والتي تقود سير عمل تطبيق الويب.
30.5. تكوين الخادم

تكوين قاعدة البيانات [config_database] وتكوين طبقة الخادم [config_layers] مطابقان لتلك الموجودة في الإصدارات السابقة. يتضمن ملف [config] الآن معلومات جديدة:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | |
- حتى السطر 41، نرى عناصر قياسية؛
- الأسطر 43–66: بحلول السطر 43، يتم تعريف مسار Python الخاص بالخادم. يمكننا بعد ذلك استيراد تبعيات المشروع:
- الأسطر 45–55: قائمة وحدات التحكم؛
- الأسطر 57–60: قائمة استجابات HTTP؛
- الأسطر 62-66: قائمة قوالب العرض؛
- الأسطر 68-189: تكوين التطبيق مع سلسلة من الثوابت؛
- الأسطر 71–98: نحن على دراية بهذه الأسطر بالفعل من الإصدارات السابقة؛
- الأسطر 101–122: قاموس وحدات التحكم:
- المفاتيح هي أسماء الإجراءات؛
- القيم هي مثيل للمتحكم المسؤول عن معالجة هذا الإجراء. يتم إنشاء مثيل لكل متحكم كمثيل واحد (singleton). سيتم تنفيذ نفس المثيل بواسطة خيوط خادم مختلفة. لذلك، يجب توخي الحذر مع البيانات المشتركة التي قد يرغب كل متحكم في تعديلها؛
- الأسطر 125–129: قاموس الاستجابات الثلاثة المحتملة لـ HTTP:
- المفاتيح هي نوع الاستجابة المطلوبة من قبل العميل (JSON، XML، HTML)؛
- القيم هي مثيل لاستجابة HTTP. يتم إنشاء مثيل لكل مولد استجابة كمثيل واحد (singleton). سيتم تنفيذ نفس المولد بواسطة خيوط خادم مختلفة. لذلك يجب توخي الحذر مع البيانات المشتركة التي قد يرغب كل مولد في تعديلها؛
- الأسطر 132–186: تكوين طرق عرض HTML. في الوقت الحالي، سنتجاهل هذه الأسطر؛
- الأسطر 191–202: لقد صادفنا هذه الأسطر بالفعل في الإصدارات السابقة؛
30.6. مسار طلب العميل داخل الخادم

سنتتبع مسار طلب العميل الذي يصل إلى الخادم وصولاً إلى استجابة HTTP المرسلة. ويتبع ذلك تدفق خادم MVC.
30.6.1. النص البرمجي [main]
النص البرمجي [main] مطابق في نواحٍ عديدة للنص البرمجي في الإصدارات السابقة. ومع ذلك، فإننا نقدمه بالكامل لضمان البدء على أساس سليم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
- الأسطر 1–92: تمت تغطية جميع هذه الأسطر وشرحها بالفعل؛
- السطر 92: سيقوم الخادم بإدارة جلسة العمل. لذلك نحتاج إلى مفتاح سري. بالنسبة لكل مستخدم، سنقوم بتخزين معلومتين في جلسة العمل:
- ما إذا كان المستخدم قد نجح في المصادقة؛
- في كل مرة يقوم فيها بحساب الضريبة، سيتم وضع نتائج هذا الحساب في قائمة تسمى قائمة محاكاة المستخدم. سيتم تخزين هذه القائمة في الجلسة؛
- الأسطر 100-151: قائمة عناوين URL لخدمات الخادم. تعمل الوظائف المرتبطة بها كمرشح: أي عناوين URL غير موجودة في هذه القائمة سيتم رفضها من قبل خادم Flask مع ظهور خطأ [404 NOT FOUND]. بمجرد اكتمال هذا التصفية، يتم توجيه الطلب بشكل منهجي إلى "Front Controller" الذي يتم تنفيذه بواسطة وظيفة [front_controller] في الأسطر 94–98، والتي سنناقشها بعد قليل؛
- الأسطر 100–103: معالجة مسار [/]. ستكون نقطة الدخول لتطبيق الويب هي عنوان URL الموجود في السطر 107. لذلك، في السطر 103، نقوم بإعادة توجيه العميل إلى عنوان URL هذا:
- يتم استيراد الدالة [url_for] في السطر 18. ولها معلمتان هنا:
- المعلمة الأولى هي اسم إحدى وظائف التوجيه، وفي هذه الحالة هي الوظيفة الموجودة في السطر 107. يمكننا أن نرى أن هذه الوظيفة تتوقع معلمة [type_response]، وهي نوع الاستجابة (json، xml، html) التي يطلبها العميل؛
- المعلمة الثانية تأخذ اسم المعلمة من السطر 107، [type_response]، وتعيّن لها قيمة. لو كانت هناك معلمات أخرى، لكنا كررنا العملية لكل منها؛
- تُرجع الدالة عنوان URL المرتبط بالدالة المحددة بواسطة المعلمتين المقدمتين إليها. هنا، ستُرجع الدالة عنوان URL من السطر 106، حيث يتم استبدال المعلمة بقيمتها [/init-session/html]؛
- تم استيراد الدالة [redirect] في السطر 18. وتتمثل مهمتها في إرسال رأس إعادة توجيه HTTP إلى العميل:
- المعلمة الأولى هي عنوان URL الذي يجب إعادة توجيه العميل إليه؛
- المعلمة الثانية هي رمز حالة استجابة HTTP المرسلة إلى العميل. الرمز [status.HTTP_302_FOUND] يتوافق مع إعادة توجيه HTTP؛
تقوم الدالة [ front_controller] في الأسطر 94–98 بإجراء المعالجة الأولية لطلب العميل:
- الأسطر 1–57: نحن على دراية بهذا الكود. على سبيل المثال، كان هذا هو كود الدالة المسماة [main] في البرنامج النصي [main] في الإصدار السابق. هناك أمر واحد جدير بالملاحظة وهو وحدة التحكم المستخدمة في الأسطر 25–26:
- السطر 25: نسترد مثيل وحدة التحكم المرتبط بالاسم [main-controller] من التكوين. هذه هي الأسطر التالية:
- (تابع)
- السطر 10 أعلاه، لاحظ أننا نسترد مثيلًا للفئة؛
- السطر 26: نطلب من وحدة التحكم [MainController] معالجة الطلب؛
- الأسطر 30–45: يتم إرسال الاستجابة التي يعيدها [MainController] إلى العميل. سنعود إلى هذه الأسطر لاحقًا؛
تتمثل مهمة دالة [front_controller] ثم فئة [MainController] في معالجة المهام المشتركة بين جميع الطلبات:
في الرسم البياني أعلاه، ما زلنا في المرحلة 1 من معالجة الطلب. سيواصل وحدة التحكم الرئيسية [MainController] الخطوة 1.

30.6.2. وحدة التحكم الرئيسية [MainController]
تواصل وحدة التحكم الرئيسية [MainController] العمل الذي بدأته وظيفة [front_controller]:
تنفذ جميع وحدات التحكم واجهة [InterfaceController] [2] التالية:

from abc import ABC, abstractmethod
from werkzeug.local import LocalProxy
class InterfaceController(ABC):
@abstractmethod
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
pass
- تحدد واجهة [InterfaceController] طريقة [execute] الوحيدة في السطر 8. تأخذ هذه الطريقة ثلاثة معلمات:
- [request]: طلب العميل؛
- [session]: جلسة عمل العميل؛
- [config]: تكوين التطبيق؛
تُرجع الطريقة [execute] مجموعة مكونة من عنصرين:
- الأول هو قاموس النتائج بالشكل {‘action’: action, ‘status’: status, ‘response’: results};
- والثاني هو رمز حالة HTTP الذي سيتم إرجاعه إلى العميل؛
تقوم وحدة التحكم الرئيسية [MainController] [1] بتنفيذ واجهة [InterfaceController] على النحو التالي:
يقوم [MainController] بإجراء الفحوصات الأولية للتحقق من صحة الطلب.
- الأسطر 11–13: يبدأ وحدة التحكم باسترداد الإجراء الذي طلبه العميل. تذكر أن عناوين URL للخدمة تكون بالشكل [/action/param1/param2/…] وأن عنوان URL هذا موجود في [request.path]؛
- الأسطر 17–23: تُستخدم الإجراء [init-session] لتهيئة نوع الاستجابة (json، xml، html) المطلوب من قبل العميل. يتم تخزين هذه المعلومات في الجلسة تحت المفتاح [responseType]. لذلك، إذا لم يكن الإجراء هو [init-session]، يجب أن تحتوي الجلسة على المفتاح [responseType]؛ وإلا، فإن الطلب غير صالح؛
- السطور 21-22: هيكل النتيجة التي تعيدها كل وحدة تحكم، وفي هذه الحالة نتيجة خطأ:
- [action]: هو اسم الإجراء الحالي. سيسمح لنا هذا باسترداد اسمه عند تسجيل نتيجة الطلب؛
- [status]: هو رمز حالة مكون من ثلاثة أرقام:
- [x00] للنجاح؛
- [x01] للفشل؛
- [response]: هو الرد على الطلب. وتختلف طبيعته باختلاف كل طلب؛
- الأسطر 24–30: يُستخدم الإجراء [authenticate-user] لمصادقة المستخدم. في حالة النجاح، تتم إضافة مفتاح [user=True] إلى جلسة عمل المستخدم. لا يمكن الوصول إلى عناوين URL لبعض الخدمات إلا للمستخدم المصادق عليه. وهذا ما يتم التحقق منه هنا؛
- السطر 26: لا يمكن للمستخدم الذي لم يتم مصادقته بعد تنفيذ سوى الإجراءين [init-session] و [authenticate-user]؛
- الأسطر 28-29: الاستجابة التي يتم إرسالها في حالة حدوث خطأ؛
- الأسطر 32-34: إذا حدث أي من الخطأين السابقين، يتم إرسال استجابة الخطأ إلى العميل مع حالة HTTP 400 BAD REQUEST؛
- الأسطر 35-39: إذا لم يحدث أي خطأ، يتم تمرير التحكم إلى وحدة التحكم المسؤولة عن معالجة الإجراء الحالي. توجد مثيلتها في تكوين التطبيق؛
تواصل فئة [MainController] عمل دالة [front_controller]: تعملان معًا على معالجة كل ما يمكن استبعاده من معالجة الطلب، في انتظار اللحظة الأخيرة لتمرير الطلب إلى وحدة تحكم محددة. إن تقسيم الكود بين دالة [front_controller] وفئة [MainController] أمر شخصي تمامًا. هنا أردت الحفاظ على بنية الإصدار السابق: كانت الدالة [front_controller] موجودة بالفعل تحت اسم [main]. في الممارسة العملية، يمكن للمرء:
- وضع كل شيء في دالة [front_controller] وإلغاء فئة [MainController]؛
- وضع كل شيء في فئة [MainController] وإلغاء وظيفة [front_controller]. أميل إلى اختيار هذا الحل لأنه يتميز بتبسيط كود البرنامج النصي الرئيسي [main]؛
30.7. المعالجة الخاصة بالإجراءات
لنعد إلى بنية MVC للتطبيق:

ما زلنا في الخطوة 1 أعلاه. إذا لم تكن هناك أخطاء، فستبدأ الخطوة 2. تم توجيه الطلب إلى وحدة التحكم الخاصة بالإجراء المطلوب في الطلب. لنفترض أن هذا الإجراء هو [/init-session] المحدد بواسطة المسار:
هذا الإجراء مرتبط بوحدة تحكم في التكوين [config]:
# actions autorisées et leurs contrôleurs
"controllers": {
# initialisation d'une session de calcul
"init-session": InitSessionController(),
…
},
ثم يتولى [InitSessionController] (السطر 4) زمام الأمور. وفيما يلي كوده:
- السطر 6: مثل وحدات التحكم الأخرى، تنفذ [InitSessionController] واجهة [InterfaceController]؛
- السطر 10: عنوان URL من النوع [/init-session/type_response]. نسترد الإجراء [init-session] ونوع الاستجابة المطلوب؛
- السطر 15: لا يمكن أن يكون نوع الاستجابة المطلوب سوى أحد الأنواع الموجودة في تكوين الاستجابة:
# les différents types de réponse (json, xml, html)
"responses": {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
},
- إذا لم يكن الأمر كذلك، يتم إعداد استجابة خطأ 701 (السطر 17)؛
- الأسطر 20-25: الحالة التي يكون فيها نوع الاستجابة المطلوب صالحًا؛
- السطر 22: يتم تخزين نوع الاستجابة المطلوب في الجلسة. وذلك لأننا سنحتاج إلى تذكره للطلبات اللاحقة؛
- الأسطر 23–24: إعداد استجابة نجاح 700؛
- السطر 25: يتم إرجاع استجابة النجاح إلى المتصل؛
- السطر 27: إذا حدث خطأ، يتم إرجاع استجابة الخطأ إلى المتصل؛
30.8. إنشاء استجابة HTTP للخادم
لنعد إلى بنية MVC للتطبيق:

لقد غطينا للتو الخطوتين 1 و 2. واجهنا ثلاثة رموز حالة:
- 700: نجحت عملية /init-session؛
- 701: فشل /init-session؛
- 101: طلب غير صالح، إما لأن الجلسة لم يتم تهيئتها أو لأن المستخدم لم يتم توثيقه؛
دعونا ندرس كيف سيتم إرسال استجابة الخادم إلى العميل خلال الخطوة 3 أعلاه. يحدث هذا في دالة [front_controller] في البرنامج النصي [main]:
- نحن الآن في السطر 26: أرجعت وحدة التحكم الرئيسية استجابة الخطأ الخاصة بها؛
- الأسطر 27–29: بغض النظر عن استجابة وحدة التحكم الرئيسية (نجاح أو فشل)، يتم تسجيل هذه الاستجابة في ملف السجل؛
- الأسطر 30-33: كما في الإصدارات السابقة، إذا كانت حالة HTTP هي [500 INTERNAL SERVER ERROR]، فإننا نرسل بريدًا إلكترونيًا إلى مسؤول التطبيق مع سجل الأخطاء؛
- الأسطر 34–39: نرسل استجابة HTTP، ويتم وضع النتيجة التي أعادها وحدة التحكم في نص هذه الاستجابة. نحتاج إلى معرفة التنسيق (JSON، XML، HTML) الذي يريده العميل لهذه الاستجابة. نبحث عن نوع الاستجابة المطلوب في الجلسة. إذا لم يكن موجودًا، فإننا نحدد هذا النوع بشكل تعسفي إلى JSON؛
- الأسطر 40–43: يتم إنشاء استجابة HTTP؛
في ملف التكوين، تم ربط كل نوع استجابة (json، xml، html) بمثيل فئة:
# les différents types de réponse (json, xml, html)
"responses": {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
},
توجد فئات الاستجابة في المجلد [responses] ضمن شجرة دليل الخادم:

تنفذ كل فئة استجابة واجهة [InterfaceResponse] التالية:
from abc import ABC, abstractmethod
from flask.wrappers import Response
from werkzeug.local import LocalProxy
class InterfaceResponse(ABC):
@abstractmethod
def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
résultat: dict) -> (Response, int):
pass
- الأسطر 8–11: تحدد واجهة [InterfaceResponse] طريقة واحدة [build_http_response] بالمعلمات التالية:
- [request, session, config]: هذه هي المعلمات التي يتلقاها وحدة التحكم في الإجراءات؛
- [result, status_code]: هذه هي النتائج التي تنتجها وحدة التحكم في الإجراءات؛
سنقدم الآن استجابة JSON. يتم إنشاؤها بواسطة فئة [JsonResponse] التالية:
نحن على دراية بهذا الكود، الذي صادفناه مرات عديدة. إنه كود الدالة [json_response] في الوحدة النمطية [myutils].
30.9. الاختبارات الأولية
في الكود الذي فحصناه، صادفنا ثلاثة رموز حالة:
- 700: نجحت عملية /init-session؛
- 701: فشل /init-session؛
- 101: طلب غير صالح، إما لأن الجلسة لم يتم تهيئتها أو لأن المستخدم لم يتم توثيقه؛
سنحاول تشغيل هذه الأوامر باستخدام جلسة JSON.
- نقوم بتشغيل خادم الويب ونظام إدارة قواعد البيانات وخادم البريد؛
- نقوم بتشغيل عميل Postman؛
الاختبار 1
أولاً، سنعرض طلبًا غير صالح لأن الجلسة لم يتم تهيئتها بعد:

- [1-2]: الطلب [POSThttp://localhost:5000/authentifier-utilisateur] هو مسار صالح:
# authenticate-user
@app.route('/authentifier-utilisateur', methods=['POST'])
def authentifier_utilisateur() -> tuple:
# execute the controller associated with the action
return front_controller()
ولكن لا يتم قبولها إلا إذا تم تهيئة الجلسة مسبقًا باستخدام الإجراء [/init-session].
دعونا ننفذ الطلب ونرى النتيجة التي أرسلها الخادم:

- [1-2]: تلقينا استجابة JSON. عندما لا يكون نوع الاستجابة قد تم تحديده بعد من قبل العميل، يستخدم الخادم JSON للرد؛
- [3-5]: قاموس JSON للاستجابة؛
- [action]: الإجراء الذي تم تنفيذه؛
- [status]: رمز حالة الاستجابة. يشير الرمز [x01] إلى وجود خطأ؛
- [response]: يتم تخصيصه لكل إجراء. ويحتوي هنا على رسالة خطأ؛
الآن دعونا نبدأ جلسة عمل بنوع استجابة غير صحيح:

- [1-2] هو مسار صالح:
# init-session
@app.route('/init-session/<string:type_response>', methods=['GET'])
def init_session(type_response: str) -> tuple:
# execute the controller associated with the action
return front_controller()
وبالتالي، سيدخل في مسار معالجة الطلبات لخادم MVC. ومع ذلك، يجب رفضه أثناء هذه المعالجة لأن نوع الجلسة المطلوب غير صحيح.
الاستجابة هي كما يلي:

- في [4]، رمز الخطأ [x01]؛
- في [5]، شرح الخطأ؛
الآن، دعونا نُهيئ جلسة JSON:

الرد كما يلي:

الآن، دعونا نبدأ جلسة XML. سيتم استبدال استجابة JSON باستجابة XML تم إنشاؤها بواسطة فئة [XmlResponse] التالية:
هذا كود مألوف لنا — فهو مأخوذ من الدالة [xml_response] في الوحدة النمطية المشتركة [myutils].
نقوم بتهيئة جلسة عمل XML:

ثم يكون رد الخادم كما يلي:

نحصل على نفس الاستجابة كما في JSON، ولكن هذه المرة يتم تنسيق الاستجابة بتنسيق XML.
30.10. الإجراء [authenticate-user]
يتيح لك الإجراء [authenticate-user] مصادقة مستخدم يرغب في استخدام تطبيق حساب الضرائب. يتم تعريف مساره على النحو التالي في البرنامج النصي [main]:
يتوقع الخادم معلمتين POST:
- [user]: معرف المستخدم؛
- [password]: كلمة المرور الخاصة به؛
يتم تحديد قائمة المستخدمين المصرح لهم في التكوين [config]:
# utilisateurs autorisés à utiliser l'application
"users": [
{
"login": "admin",
"password": "admin"
}
],
هنا، لدينا قائمة تحتوي على عنصر واحد.
يتم التعامل مع الإجراء [authenticate-user] بواسطة وحدة التحكم [AuthentifierUtilisateurController] التالية:
- السطر 14: استرداد معلمات POST؛
- السطر 19: قائمة الأخطاء الموجودة في الطلب؛
- الأسطر 20–24: نتحقق من وجود معلمتين مرسلتين بالفعل؛
- الأسطر 27–31: التحقق من وجود معلمة [users]؛
- الأسطر 32–36: التحقق من وجود معلمة [password]؛
- الأسطر 38-39: إذا كانت المعلمات المرسلة غير صحيحة، قم بإعداد استجابة HTTP 400 BAD REQUEST؛
- الأسطر 40–58: التحقق من أن بيانات الاعتماد [user, password] تخص مستخدمًا مخولًا باستخدام التطبيق؛
- الأسطر 51-55: إذا لم يكن المستخدم (user, password) مخولًا لاستخدام التطبيق، قم بإعداد استجابة HTTP 401 UNAUTHORIZED؛
- الأسطر 56–58: إذا كان المستخدم مخولًا، نسجل في الجلسة باستخدام مفتاح [user] أنه قد تم توثيقه؛
لاحظ أنه إذا تمت مصادقة المستخدم باستخدام بيانات الاعتماد [credentials1] وفشل في المصادقة باستخدام بيانات الاعتماد [credentials2]، فإنه يظل مصادقًا عليه باستخدام بيانات الاعتماد [credentials1].
دعونا نجري بعض اختبارات Postman:
- نقوم بتشغيل خادم الويب ونظام إدارة قواعد البيانات وخادم البريد؛
- باستخدام عميل Postman:
- ابدأ جلسة JSON؛
- ثم المصادقة؛
فيما يلي سيناريوهات مختلفة.
الحالة 1: POST بدون معلمات مرسلة

- في [3-5]، لا يحتوي طلب POST على نص؛
وكانت نتيجة الطلب كما يلي:

- في [2]، تلقينا استجابة HTTP 400 BAD REQUEST؛
- في [5]، تلقينا رمز خطأ [201]؛
الحالة 2: POST مع بيانات اعتماد غير صحيحة

- في [6]، بيانات الاعتماد غير صحيحة؛
يرسل الخادم الاستجابة التالية:

- في [2]، استجابة HTTP 401 UNAUTHORIZED؛
- في [5]، رد الخطأ؛
الحالة 2: POST مع بيانات اعتماد صحيحة

- في [6]، بيانات الاعتماد صحيحة؛
استجابة الخادم هي كما يلي:
- في [2]، استجابة HTTP 200 OK؛
- في [5]، استجابة النجاح؛
30.11. الإجراء [calculate_tax]
يحسب الإجراء [calculate_tax] ضريبة دافع الضرائب. يتم تعريف مساره على النحو التالي في البرنامج النصي [main]:
يتوقع الخادم ثلاثة معلمات POST:
- [married]: نعم / لا؛
- [children]: عدد أطفال دافع الضرائب؛
- [الراتب]: الراتب السنوي للمكلف؛
تتولى وحدة التحكم [CalculateTaxController] معالجة الإجراء [calculate_tax]:
- السطر 13: نسترد اسم الإجراء الحالي؛
- السطر 17: نجمع الأخطاء في قائمة؛
- السطر 19: استرداد المعلمات المرسلة. يتم إرسال هذه المعلمات بتنسيق [x-www-form-urlencoded]، ولهذا السبب نستردها من [request.form]. لو كانت قد أُرسلت بتنسيق JSON، لكنا استردناها من [request.data]؛
- الأسطر 21–24: نتحقق من وجود ثلاثة معلمات مرسلة بالفعل؛
- الأسطر 27–36: التحقق من وجود وصحة المعلمة المنشورة [married]؛
- الأسطر 37–48: التحقق من وجود وصحة المعلمة المرسلة [children]؛
- الأسطر 49–60: التحقق من وجود وصحة المعلمة المنشورة [salary]؛
- الأسطر 62-66: إذا كان هناك خطأ، يتم إرسال استجابة خطأ 400 BAD REQUEST مع رمز الحالة [301]؛
- الأسطر 69–71: في حالة عدم وجود خطأ، الاستعداد لحساب الضريبة. للقيام بذلك،
- السطر 70: استرداد مرجع من طبقة [business]؛
- السطر 71: استرداد البيانات من سلطة الضرائب في تكوين الخادم؛
- الأسطر 72-74: يتم حساب ضريبة دافع الضرائب؛
- الأسطر 75-77: نحسب عدد حسابات الضريبة التي أجراها المستخدم؛
- السطر 76: استرجاع رقم آخر عملية حسابية تم إجراؤها من الجلسة. هنا، نشير إلى نتيجة عملية الحساب باسم [محاكاة]؛
- السطر 77: زيادة رقم آخر محاكاة؛
- السطر 78: يتم حفظ هذا الرقم في الجلسة؛
- الأسطر 79-84: لتتبع الحسابات التي أجراها المستخدم، سنقوم بتخزين قائمة المحاكاة التي أجراها في جلسته؛
- السطر 80: ستكون المحاكاة عبارة عن قاموس لكائن TaxPayer الذي ستكون قيمة خاصية [id] الخاصة به هي رقم المحاكاة؛
- الأسطر 82-84: تتم إضافة المحاكاة الحالية إلى قائمة المحاكاة في الجلسة؛
- السطران 86-87: نقوم بإعداد استجابة HTTP ناجحة؛
- السطر 90: نُرجع النتيجة؛
دعونا نجري بعض الاختبارات: يتم تشغيل خادم الويب ونظام إدارة قواعد البيانات وخادم البريد وعميل Postman.
الحالة 1: إجراء حساب ضريبي بينما الجلسة غير مهيأة

الرد كما يلي:

الحالة 2: إجراء حساب الضريبة دون المصادقة
أولاً، نبدأ جلسة JSON باستخدام [/init-session/json]. ثم نرسل نفس الطلب كما في السابق. والاستجابة هي كما يلي:

الحالة 3: إجراء حساب الضريبة مع وجود معلمات مفقودة
نقوم بتهيئة جلسة JSON، والمصادقة، ثم نرسل الطلب التالي:

- في [5]، المعلمة [married] مفقودة؛
الرد كما يلي:
الحالة 4: حساب الضريبة باستخدام معلمات غير صحيحة


رد الخادم كما يلي:

الحالة 4: إجراء حساب الضريبة باستخدام معلمات صحيحة

رد الخادم كما يلي:

30.12. إجراء [list-simulations]
يتيح الإجراء [list-simulations] للمستخدم عرض قائمة المحاكاة التي أجراها منذ بدء الجلسة. يتم تعريف مساره على النحو التالي في البرنامج النصي [main]:
لا يتوقع الخادم أي معلمات. يتم التعامل مع الإجراء [lister-simulations] بواسطة [ListerSimulationsController] التالي:
- السطر 13: يتم استرداد قائمة المحاكاة من الجلسة؛
- السطران 15-16: يتم إرجاع استجابة نجاح؛
دعونا نجري اختبار Postman التالي:
- نبدأ جلسة JSON؛
- نقوم بالمصادقة؛
- نقوم بإجراء حسابين ضريبيين؛
- نطلب قائمة المحاكاة؛
الطلب كما يلي:
- في [3]، لا توجد معلمات؛
رد الخادم كما يلي:

- في [4]، قائمة عمليات المحاكاة الخاصة بالمستخدم؛
30.13. إجراء [delete-simulation]
يسمح الإجراء [delete-simulation] للمستخدم بحذف إحدى عمليات المحاكاة من قائمة عمليات المحاكاة الخاصة به. يتم تعريف مساره على النحو التالي في البرنامج النصي [main]:
يتوقع الخادم معلمة واحدة: رقم المحاكاة المراد حذفها. يتم التعامل مع الإجراء [delete-simulation] بواسطة [DeleteSimulationController] التالي:
- السطر 10: استرجاع عنصري مسار الطلب. يتم استرجاعهما كسلاسل نصية؛
- السطر 13: يتم تحويل المعلمة [number] إلى عدد صحيح. ونعلم أن هذا ممكن بفضل توقيع المسار،
@app.route('/supprimer-simulation/<int:numero>', methods=['GET'])
ونعلم أيضًا أنه عدد صحيح >=0. في الواقع، لا يمكن أن يكون لدينا عنوان URL مثل [/delete-simulation/-4]. فهذا العنوان يرفضه خادم Flask؛
- السطر 15: نسترد قائمة عمليات المحاكاة من الجلسة؛
- السطر 16: باستخدام دالة [filter]، نبحث عن المحاكاة التي لها id==number. نحصل على كائن [filter] الذي نحوله إلى [list]؛
- الأسطر 17–20: إذا لم يُرجع المرشح أي شيء، فهذا يعني أن المحاكاة المراد حذفها غير موجودة. نُرجع استجابة خطأ تشير إلى ذلك؛
- الأسطر 21–23: نحذف المحاكاة التي أعادها المرشح؛
- السطر 25: نعيد قائمة المحاكاة الجديدة إلى الجلسة؛
- السطر 27: نُرجع قائمة المحاكاة الجديدة في الاستجابة؛
نجري اختبار النجاح واختبار الفشل. نقوم بتشغيل المحاكاة ثم نطلب قائمة المحاكاة:

- المحاكاة هنا تحمل الرقمين 2 و 3؛
نطلب إزالة المحاكاة التي تحمل الرقم 3.

الرد كما يلي:
الآن، دعونا نكرر نفس العملية (حذف المحاكاة التي تحمل الرقم التعريفي 3). يكون الرد عندئذ كما يلي:


30.14. إجراء [end-session]
يسمح الإجراء [end-session] للمستخدم بإنهاء جلسة المحاكاة الخاصة به. يتم تعريف مساره على النحو التالي في البرنامج النصي [main]:
لا يتوقع الخادم أي معلمات. يتم التعامل مع الإجراء بواسطة [FinSessionController] التالي:
- السطر 13: حذف جميع المفاتيح من الجلسة. يؤدي هذا إلى حذف:
- [typeResponse]: نوع استجابات HTTP (json، xml، html)؛
- [simulation_id]: معرف آخر محاكاة تم إجراؤها؛
- [simulations]: قائمة محاكاة المستخدم؛
- [user]: مؤشر على أن المستخدم قد تمت مصادقته؛
- إرجاع الاستجابة؛
قد يتساءل المرء عن كيفية إرجاع استجابة HTTP من السطر 15، الآن بعد أن لم يعد نوع الاستجابة موجودًا في الجلسة. لمعرفة ذلك، نحتاج إلى العودة إلى دالة |front_controller| في البرنامج النصي الرئيسي [main] وتعديلها على النحو التالي:
…
# on not# note the type of response required if this information is in the session
type_response1 = session.get('typeResponse', None)
# forward the request to the main controller
main_controller = config['controllers']["main-controller"]
résultat, status_code = main_controller.execute(request, session, config)
# we log the result sent to the customer
log = f"[front_controller] {résultat}\n"
logger.write(log)
# was there a fatal error?
if status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
# send an e-mail to the application administrator
send_adminmail(config, log)
# determine the desired type of response
type_response2=session.get('typeResponse')
if type_response2 is None and type_response1 is None:
# the session type has not yet been set - it will be jSON
type_response = 'json'
elif type_response2 is not None:
# the type of response is known and in the session
type_response = type_response2
else:
type_response=type_response1
# build the response to be sent
response_builder = config["responses"][type_response]
response, status_code = response_builder \
.build_http_response(request, session, config, status_code, résultat)
# we send the answer
return response, status_code
- السطر 3: يتم تخزين نوع الاستجابة الموجودة حاليًا في الجلسة؛
- السطر 6: يتم تنفيذ الإجراء. إذا كان:
- [end-session]، فإن مفتاح [typeResponse] لم يعد موجودًا في الجلسة؛
- [init-session]، فقد تتغير قيمة مفتاح [typeResponse] في الجلسة؛
- الأسطر 14-20: يجب إرسال استجابة HTTP. نحتاج إلى معرفة الشكل الذي ستكون عليه:
- الأسطر 16-18: إذا لم يتم تعريف نوع الاستجابة بواسطة [type_response1] في السطر 3 أو [type_response2] في السطر 15، فإن نوع الاستجابة لم يتم تعريفه قبل الإجراء أو بعده. ثم نستخدم JSON (السطر 18)؛
- الأسطر 19-21: إذا كان [type_response2] موجودًا — نوع الاستجابة في الجلسة بعد الإجراء — فهذا هو النوع الذي يجب استخدامه؛
- الأسطر 22-23: خلاف ذلك، فإن [type_response1]، أي نوع الاستجابة قبل الإجراء (الذي يجب أن يكون [end-session])، هو النوع الذي يجب استخدامه؛
30.15. الإجراء [get-admindata]
سنناقش الآن عنواني URL المخصصين لخدمات JSON و XML:
الإجراء | الدور | سياق التنفيذ |
/get-admindata | يعرض البيانات الضريبية المستخدمة لحساب الضريبة | GET. يُستخدم فقط إذا كان نوع الجلسة json أو xml. يجب مصادقة المستخدم |
/calculate-taxes | يحسب الضريبة لقائمة من دافعي الضرائب المنشورة بتنسيق JSON | طلب GET. يُستخدم فقط إذا كان نوع الجلسة json أو xml. يجب مصادقة المستخدم |
يتم تعريف عنوان URL [/get-admindata] في مسارات البرنامج النصي الرئيسي [main] على النحو التالي:
يتم التعامل مع المسار [/get-admindata] بواسطة [GetAdminDataController] التالي:
- الأسطر 13-21: نتحقق من أننا في جلسة JSON أو XML؛
- السطر 24: إرجاع قاموس بيانات إدارة الضرائب، الذي تم وضعه في التكوين عند بدء تشغيل الخادم:
# admindata sera une donnée de portée application en lecture seule
config["admindata"] = config["layers"]["dao"].get_admindata()
دعونا نستخدم عميل Postman ونطلب عنوان URL [/get-admindata]، بعد بدء جلسة JSON والمصادقة:

استجابة الخادم هي كما يلي:

30.16. الإجراء [calculate-taxes]
يحسب الإجراء [calculate-taxes] الضرائب لقائمة من دافعي الضرائب الموجودة في نص الطلب كسلسلة JSON. نحن على دراية بهذا الإجراء بالفعل: كان يُسمى [calculate_tax_in_bulk_mode] في الإصدار السابق.
مسارها كما يلي:
يتم التعامل مع هذا الإجراء بواسطة [CalculateTaxesController] التالي:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |
- الأسطر 16-24: نتحقق من أننا بالفعل في جلسة JSON أو XML
- الأسطر 26–120: هذا الكود مألوف لنا بشكل عام. إنه مأخوذ من دالة |index_controller| في الإصدار 10 من التطبيق، والتي تم تكييفها لتتوافق مع مواصفات واجهة [InterfaceController] التي تم تنفيذها؛
- الأسطر 104–115: كود تمت إضافته لمراعاة البيئة الجديدة لهذا المتحكم. لقد أجرينا للتو حسابات الضرائب. نحتاج إلى تخزين النتائج في قائمة المحاكاة التي يتم الاحتفاظ بها في الجلسة؛
- السطر 105: نسترد قائمة المحاكاة في الجلسة؛
- السطر 106: نسترد رقم آخر محاكاة تم إجراؤها؛
- الأسطر 107-112: نكرر قائمة القواميس التي تحتوي على نتائج حساب الضرائب؛ ونخصص معرف محاكاة لكل منها، ويتم إضافة كل قاموس إلى قائمة المحاكاة؛
- الأسطر 113–115: يتم إرجاع قائمة المحاكاة الجديدة ورقم آخر محاكاة تم إجراؤها إلى الجلسة؛
نقوم بإجراء اختبار Postman التالي بعد تهيئة جلسة JSON والمصادقة:


استجابة الخادم هي كما يلي:

إذا طلبنا الآن قائمة المحاكاة:
لاحظ أنه في قائمة النتائج لـ [/calcul-impots]، لا يمتلك دافعو الضرائب سمة [id]، بينما في قائمة المحاكاة، تحتوي كل محاكاة على رقم يحددها.




