التحقق من صحة البيانات (Jakarta Validation)
التحقق من صحة البيانات (Jakarta Validation)
قبل أن تصل أي طلب إلى منطق العمل الخاصة بك، تحتاج إلى التأكد من أن البيانات التي يحملها سليمة هيكليًا. Jakarta Bean Validation (كانت تُعرف سابقًا بـ javax.validation ثم أصبحت jakarta.validation) هي المواصفة القياسية في Java التي تتيح لك التعبير عن القيود مباشرةً على فئات النماذج باستخدام التعليقات التوضيحية (annotations). يدمج Spring Boot 3 هذه المواصفة بشكل كامل عبر Hibernate Validator، وهو التنفيذ المرجعي لها.
التبعية التي تحتاجها
أضف المُبدِّئ (starter) الخاص بـ Spring Boot — فهو يسحب Hibernate Validator وواجهة برمجة Jakarta Validation API تلقائيًا:
بدون هذا المُبدِّئ تُترجَم التعليقات التوضيحية بشكل صحيح لكن القيود تُتجاهل بصمت في وقت التشغيل — وهو مصدر شائع للارتباك لدى المطورين القادمين من مشاريع Spring القديمة.
spring-boot-starter-web يشمل مُبدِّئ التحقق منذ Spring Boot 2.3. صرّح به صراحةً في كل وحدة تُجري فيها عمليات التحقق من المدخلات.
كيف يعمل Bean Validation
تُعرّف المواصفة كيانًا يُسمى Validator يفحص حقول الكائن وخصائصه ومعاملات المُنشئ مقابل مجموعة من القيود المُعلَن عنها. كل قيد هو تعليق توضيحي مدعوم بتنفيذ ConstraintValidator يحتوي على منطق الفحص الفعلي. عندما تُعالج Spring تعليق @Valid أو @Validated على معامل دالة، تستدعي المُحقِّق قبل تنفيذ جسم الدالة. إذا فشل أي قيد، يُرمى استثناء MethodArgumentNotValidException.
jakarta.validation:jakarta.validation-api. أما Hibernate Validator فهو التنفيذ الذي يوفر مُحققي القيود الفعليين، مع إضافات كثيرة تتجاوز المواصفة. أنت تكتب الكود مقابل الواجهة؛ والتنفيذ قابل للاستبدال.
تعليقات القيود القياسية
تأتي مواصفة Jakarta Validation مع نحو 25 قيدًا مدمجًا. أكثرها استخدامًا في العمل اليومي هي:
@NotNull— يجب ألا يكون الحقلnull. يقبل السلاسل الفارغة والسلاسل التي تحتوي على مسافات فقط.@NotEmpty— يجب ألا يكونnullويجب أن يحتوي على حرف واحد على الأقل (أو عنصر واحد للمجموعات).@NotBlank— يجب ألا يكونnullولا فارغًا ويجب أن يحتوي على حرف واحد على الأقل غير مسافة. هذا هو الخيار الصحيح لحقول النص التي يُدخلها المستخدمون.@Size(min, max)— يقيّد الطول (String، المصفوفة، Collection، Map) أو العدد بينminوmax. كلا الحدّين شاملان.@Min(value)/@Max(value)— الحد الأدنى والأقصى للأرقام (يعمل معintوlongوBigDecimalوغيرها).@Email— يجب أن تتطابق السلسلة مع تنسيق عنوان بريد إلكتروني صالح وفقًا لمعيار RFC.@Pattern(regexp)— يجب أن تتطابق السلسلة مع التعبير النمطي المُحدد.@Positive/@PositiveOrZero/@Negative— قيود الإشارة للأنواع الرقمية.@Past/@Future— قيود زمنية لأنواعLocalDateوInstantوما يشابهها.@Digits(integer, fraction)— يُحدد عدد الأرقام الصحيحة والعشرية المسموح بها.
إضافة التعليقات التوضيحية على كائن نقل البيانات (DTO)
النمط المعتاد في Spring Boot هو إضافة التعليقات على فئة Java بسيطة (تُسمى عادةً request DTO أو command object) تمثّل البيانات الواردة. هذا مثال واقعي لطلب تسجيل مستخدم:
لاحظ عدة أنماط تستحق الترسيخ:
- استخدم
@NotBlankبدلًا من@NotNullلحقول String حيث يجب أيضًا رفض السلاسل الفارغة أو التي تحتوي على مسافات فقط. - أضف دائمًا سمة
messageتخاطب المستخدم النهائي أو مستهلك الـ API، لا المطوّر. - ضع تعليقات متعددة على الحقل نفسه — تعمل جميعها، وتُبلَّغ جميع الإخفاقات في تمريرة واحدة.
- لأنواع المجمّعات البدائية (
IntegerوLong) استخدم@NotNullبشكل منفصل عن@Min/@Maxلأن قيود الأرقام هذه لا تشترط عدم كون القيمة null.
@NotNull مقابل @NotEmpty مقابل @NotBlank
هذه الثلاثة هي مصدر معظم أخطاء المبتدئين. مقارنة جانبية:
@NotNull: يقبل""، يقبل" "، يرفض فقطnull.@NotEmpty: يرفضnullو""، يقبل" ".@NotBlank: يرفضnullو""و" "— الأكثر صرامةً بين الثلاثة للسلاسل النصية.
@NotBlank على السلاسل التي يُدخلها المستخدم (الاسم، اسم المستخدم، العنوان). احتفظ بـ @NotNull للحقول غير النصية (الأرقام، القيم المنطقية، الكائنات المتداخلة) حيث تحتاج ببساطة إلى التأكد من الوجود. أما @NotEmpty فهو مفيد للمجموعات والمصفوفات.
قيد @Email في التطبيق العملي
تعليق @Email في المواصفة يتحقق من التنسيق الهيكلي لعنوان البريد الإلكتروني. افتراضيًا، فحص Hibernate Validator متساهل نسبيًا — يقبل بعض التنسيقات الصالحة تقنيًا لكن غير المعتادة. للتحقق الأكثر صرامةً المطابق للمعيار RFC 5322، مرّر سمة regexp:
في معظم الـ APIs يكفي @Email القياسي. احتفظ بالتعديل بتعبير نمطي للحالات التي تتحكم فيها بشكل صارم في تنسيق العنوان (مثل أدوات المؤسسات الداخلية).
@Size على المجموعات والمصفوفات
@Size لا يقتصر على السلاسل النصية. ينطبق على أي نوع يمتلك حجمًا قابلًا للقياس:
عندما تكون القائمة null، يجتاز @Size الفحص (تعتبر معظم القيود أن null صالحة ما لم تُدمج مع @NotNull). ادمجهما عندما تهمّك كلٌّ من الحضور والحجم.
تخصيص الرسائل الافتراضية
لكل قيد رسالة افتراضية مخزّنة في ValidationMessages.properties على مسار الفئات. يمكنك تجاوزها عالميًا بوضع ملفك الخاص في src/main/resources/ValidationMessages.properties:
تُحلّ العناصر النائبة {min} و {max} تلقائيًا من سمات التعليق التوضيحي.
الخلاصة
يمنحك Jakarta Bean Validation طريقة تصريحية تعتمد على التعليقات التوضيحية للتعبير عن قيود البيانات مباشرةً على فئات النماذج. تُغطي القيود الأساسية — @NotNull و @NotBlank و @NotEmpty و @Size و @Email و @Min و @Max وأخواتها — الغالبية العظمى من احتياجات التحقق في العالم الحقيقي. ادمجها بحرية، وأضف قيم message ذات معنى، وضعها على كائنات نقل البيانات الخاصة بالطلبات بدلًا من كيانات النطاق للإبقاء على طبقة التحقق منفصلة عن طبقة الاستمرارية. يوضح الدرس التالي كيف تُفعِّل Spring Boot كل هذا تلقائيًا عند إضافة @Valid إلى معامل دالة المتحكم.