لماذا يهمّ التحقق من المدخلات
لماذا يهمّ التحقق من المدخلات
كل تطبيق يقبل مدخلات من العالم الخارجي يواجه حقيقةً لا مفرّ منها: لا يمكنك الوثوق بأي شيء لم تُنشئه بنفسك. أجسام طلبات HTTP ومعاملات الاستعلام والملفات المرفوعة وحمولات الـ webhook والرسائل القادمة من الطوابير — كل ذلك يصل دون أي تحقق. قد يخطئ المستخدم في كتابة قيمة، أو قد يحمل العميل خطأً برمجيًا، أو قد يصطنع طرف خبيث بياناتٍ مصمَّمة عمدًا لإتلاف قاعدة البيانات، أو استدعاء سلوك غير متوقع، أو اختراق محيط الأمان.
التحقق من المدخلات هو البوابة التي تقف بين العالم الخارجي الفوضوي والبنية الداخلية المنظّمة لتطبيقك. في هذا البرنامج التعليمي ستتعلّم بناء تلك البوابة بصورة منهجية — باستخدام واجهة Jakarta Bean Validation القياسية وتكاملها مع Spring Boot والأنماط التي تجعل واجهة برمجية للإنتاج صارمةً وصادقةً فيما ترفضه ولماذا.
ثمن إهمال التحقق
يبدو من المغري افتراض أن العميل سيرسل دائمًا بيانات صحيحة التنسيق، خاصةً حين تتحكم في الطرفين. قاوم هذا الافتراض. التكاليف الفعلية لإهمال التحقق هي:
- تلف البيانات: عنوان بريد إلكتروني
nullأو سعر سالب مُخزَّن في قاعدة البيانات يخلق تناقضات مكلفة التصحيح وقد تكون غير قابلة للإصلاح. - استثناءات غير متوقعة: خدمة تستقبل
nullحيث تتوقع سلسلةً نصية ستُطلقNullPointerExceptionفي أعماق منطق العمل، مما ينتج استجابةً 500 عديمة الفائدة وتتبع مكدس محيّر. - ثغرات أمنية: حقول النصوص غير المُتحقق منها قد تحمل مقاطع حقن SQL، أو وسوم نصية لهجمات XSS، أو تسلسلات اجتياز المسارات (
../../etc/passwd) إذا استُخدمت القيمة في مسار ملف. - انتهاك الثوابت: نموذج نطاقك يملك قواعد — إجمالي طلب يجب أن يكون موجبًا، واسم مستخدم يجب أن يكون فريدًا، ونطاق تاريخ يجب أن يبدأ قبل نهايته. إذا تخطيت التحقق، تسرّبت تلك الثوابت إلى كل دالة تالية في الكود، مما يجعله هشًا في كل مكان بدلًا من أن يُحصَّن عند الحدود.
curl أو Postman. التحقق من جانب الخادم هو الوحيد الذي يُحسب للصحة والأمان. التحقق من جانب العميل يُحسّن تجربة الاستخدام؛ أما التحقق من جانب الخادم فإلزامي.
أين يقع التحقق في تطبيق Spring Boot
يمتلك تطبيق Spring Boot عادةً عدة طبقات — controller وservice وrepository. يمكن من حيث المبدأ إجراء التحقق في أي منها، لكن سؤال أين يجب أن يقع يعتمد على ما تتحقق منه.
- طبقة Controller / حدود الـ API: هذا هو المكان المناسب للتحقق من شكل البيانات الواردة وتنسيقها — هل الحقل المطلوب موجود؟ هل عنوان البريد الإلكتروني صالح؟ هل العدد الصحيح ضمن نطاق مقبول؟ هذا النوع من التحقق سريع وعديم الحالة ويمكن التعبير عنه بشكل تصريحي بالتعليقات التوضيحية. اكتشاف المدخلات السيئة هنا يمنع أي عمل لاحق.
- طبقة Service: هذا هو المكان المناسب للتحقق من قواعد العمل التي تستلزم سياق التطبيق — هل اسم المستخدم مُستخدم بالفعل؟ هل المنتج المُشار إليه موجود فعلًا؟ هل يتجاوز الخصم الحد الأقصى المُهيَّأ؟ غالبًا ما تحتاج هذه الفحوصات إلى استعلام قاعدة بيانات أو استدعاء خدمة أخرى ولا يمكن التعبير عنها بتعليقات توضيحية بسيطة.
- Repository / قاعدة البيانات: قيود قاعدة البيانات (
NOT NULLوUNIQUEوالمفاتيح الخارجية وقيود التحقق) هي خط الدفاع الأخير. تحمي سلامة البيانات حتى إذا وُجد خطأ في كود التطبيق. لكنها يجب أن تُكمّل تحقق التطبيق لا تحل محله — انتهاك القيود من قاعدة البيانات ينتج استثناء JDBC غير مفهوم لا رسالة خطأ ودية لعميل الـ API.
مثال ملموس: ماذا يمكن أن يسوء
تأمّل نقطة نهاية بسيطة POST /api/users تُنشئ مستخدمًا جديدًا. بدون أي تحقق قد يبدو الـ controller هكذا:
تخيّل الآن أن عميلًا أرسل هذا الجسم JSON:
بدون تحقق، ينتقل هذا الطلب إلى طبقة الخدمة. تحاول الخدمة حفظ مستخدم باسم مستخدم فارغ وبريد إلكتروني مشوّه وعمر سالب. بحسب مخطط قاعدة البيانات ستحصل إما على صف تالف أو انتهاك غامض لقيود SQL يتحول إلى استجابة 500 غير مفيدة — ولا يحصل العميل على أي معلومة عما أخطأ فيه.
نقطة نهاية مُتحقَّق منها بصورة صحيحة ستُعيد بدلًا من ذلك استجابةً فورية 400 Bad Request تُدرج بالضبط الحقول غير الصالحة ولماذا، دون لمس قاعدة البيانات أصلًا:
هذا هو السلوك الذي تُنتجه واجهات برمجية احترافية، وما ستبنيه على مدار هذا البرنامج التعليمي.
Jakarta Bean Validation: المعيار القياسي
تمتلك Java مواصفةً رسميةً للتحقق التصريحي: Jakarta Bean Validation 3.0 (المعروفة سابقًا بـ javax.validation، والآن jakarta.validation). تُعرّف المواصفة تعليقات توضيحية مثل @NotNull و@Size و@Email و@Min تضعها مباشرةً على حقول فاصول Java. المُتحقِّق — التنفيذ المرجعي هو Hibernate Validator — يقرأ تلك التعليقات في وقت التشغيل ويقيّمها مقابل القيم الفعلية.
يتضمّن Spring Boot 3 المُشغّل spring-boot-starter-validation الذي يجلب Hibernate Validator تلقائيًا. أنت تُصرّح عن شكل البيانات الصالحة؛ وتتولى Spring تشغيل الفحوصات وتحويل الإخفاقات إلى استجابات HTTP صحيحة:
spring-boot-starter-validation دائمًا إلى مشاريع Spring Boot الجديدة. فهو غير مُدرج في spring-boot-starter-web افتراضيًا. نسيانه يعني أن تعليقاتك التوضيحية @Valid لن تفعل شيئًا بصمت — وهو خطأ شائع ومحيّر.
ما يغطّيه هذا البرنامج التعليمي
الآن وقد فهمت الدافع والمشهد العام، إليك المسار المقبل:
- تطبيق تعليقات Bean Validation على DTO الطلبات (
@NotBlankو@Emailو@Minو@Patternوغيرها). - تفعيل التحقق في الـ controllers باستخدام
@Validو@Validated. - كتابة تعليقاتك التوضيحية المخصصة للقيود حين لا تكفي التعليقات المدمجة.
- تحويل إخفاقات التحقق إلى استجابات خطأ متسقة ومفيدة.
- معالجة الاستثناءات غير المتوقعة عالميًا بـ
@ControllerAdvice. - السيناريوهات المتقدمة: مجموعات التحقق والقيود متعددة الحقول والتحقق في طبقة الخدمة.
في النهاية ستمتلك أساسًا جاهزًا للتحقق ومعالجة الأخطاء يمكنك إدراجه في أي مشروع Spring Boot 3.
الخلاصة
لا تثق أبدًا في المدخلات الخارجية — تحقق منها عند أقرب حد مناسب. فحوص التنسيق والبنية تقع في طبقة الـ API (controller)؛ وفحوص قواعد العمل تقع في طبقة الخدمة؛ وقيود قاعدة البيانات شبكة أمان لا بديل. يوفّر Spring Boot 3 دعمًا من الدرجة الأولى لـ Jakarta Bean Validation عبر spring-boot-starter-validation. يُريك بقية هذا البرنامج التعليمي كيف تستخدمه بإتقان.