حفر البيانات مع travajs



في مشاركتي السابقة ، وصفت النقاط الرئيسية عند تطوير مكتبة أخرى مفتوحة المصدر . لقد نسيت أن أذكر شيئًا آخر: إذا لم تخبر أي شخص بالمكتبة ، أيا كان شكلها ، فعلى الأرجح لن يعرفها أحد.

لذا ، تعرف على trava.js - التحقق من صحة العصير لصالح المشروع. بالمناسبة ، كنا نستخدم العشب منذ أكثر من ستة أشهر ، وأعتقد أن الوقت قد حان لإخبارك بفوائد استخدامه. إذا جفت بالفعل ، فاحبس أنفاسك. والمضي قدما.

مفهوم


للوهلة الأولى ، يبدو أن التحقق من الصحة موضوع تافه لا يتطلب عناية خاصة. القيمة صحيحة أم لا ، والتي يمكن أن تكون أكثر بساطة:

function validate (value) { // any checking... if (!check(value)) return false; return true; } 

لكن من الجيد عادة معرفة الخطأ الذي حدث بالضبط:

 function validate (value) { if (!check1(value)) return 'ERROR_1'; if (!check2(value)) return 'ERROR_2'; } 

في الواقع هذا كل شيء ، تم حل المشكلة.

إن لم يكن لأحد "ولكن".

من تجربة تطوير تطبيقات حقيقية ، لوحظ أن المسألة لا تنتهي بالتحقق. عادةً ما تحتاج هذه البيانات أيضًا إلى تحويلها إلى تنسيق معين ، لسبب ما لا يدعمه المتسلل خارج الصندوق ، على سبيل المثال ، التواريخ أو المجموعات أو أنواع البيانات المخصصة الأخرى. نظرًا لأن هذا هو JSON بشكل أساسي ، فقد تبين في الممارسة العملية أنه عليك القيام بتمرير مزدوج خلال بنية بيانات الإدخال أثناء التحقق من الصحة والتحول. ظهرت الفكرة ، لماذا لا تجمع بين هاتين المرحلتين في مرحلة واحدة. بالإضافة إلى ذلك ، سيكون وجود مخطط بيانات إعلاني صريح.

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

  1. ربما كان الأكثر شيوعًا هو إرجاع المجموعة [خطأ ، بيانات]:

     function validate (value) { if (!check1(value)) return ['ERROR_1']; if (!check2(value)) return ['ERROR_2']; return [null, value]; } 

    يوجد أيضًا خيار مماثل حيث لا يتم إرجاع صفيف ، ولكن كائن {error ، data} ، ولكن لا توجد اختلافات جوهرية. ميزة هذا النهج هو الوضوح ، ناقص أنه الآن في كل مكان تحتاج إلى الحفاظ على هذا العقد. للتحقق من الصحة ، هذا لا يسبب أي إزعاج ، ولكن بالنسبة للتحولات ، من الواضح أنه لا لزوم لها.
  2. استخدام استثناءات. على الرغم من أن خطأ التحقق من الصحة في رأيي هو موقف قياسي في التطبيق ، لا يوجد شيء استثنائي. بصراحة ، أعتقد أنه من الأفضل استخدام الاستثناءات فقط عندما يكون هناك خطأ ما. أيضًا ، يمكن استدعاء الاستثناءات بطريق الخطأ في المدققين أنفسهم ، ومن ثم قد لا تعلم على الإطلاق أنه كان خطأ في الكود ، وليس في القيمة. تتمثل ميزة النهج في تبسيط الواجهة - الآن يتم دائمًا إرجاع القيمة بالطريقة المعتادة ، ويتم طرح الخطأ كاستثناء.
  3. هناك خيار لوضع خطأ في متغير عمومي. لكنني لن أسحب الدولة دون داع.
  4. استخدام نوع منفصل عن الأخطاء. يبدو الخيار مع وجود استثناءات ، إذا أخذت نوع الخطأ منهم ، ولكن لا ترميه.

     function validate (value) { if (!check1(value)) return new Trava.ValidationError({ code: 401 }); if (!check2(value)) return new Trava.ValidationError({ code: 405 }); return parseOrTransform(value); // apply some parse or transform } 

لقد استقرت على الخيار الأخير ، على الرغم من أن هذا أيضًا حل وسط ، لكنه ليس سيئًا بشكل عام. يتم اقتراح Trava.ValidationError كنوع للخطأ ، الذي يرث من الخطأ القياسي ويضيف القدرة على استخدام نوع بيانات تعسفي للإبلاغ عن خطأ. ليس من الضروري استخدام Trava.ValidationError ، يمكنك استخدام الخطأ القياسي ، ولكن لا تنسَ أن رسالة الخطأ ليست سوى سلاسل.

للتلخيص ، يمكننا القول أن المدقق عبارة عن وظيفة نظيفة ومتزامنة ، بالإضافة إلى القيمة ، يمكنها إرجاع خطأ. بسيط للغاية. وهذه النظرية تعمل بشكل جيد بدون مكتبات. في الممارسة العملية ، يتم الجمع بين المدققين في السلاسل والتسلسلات الهرمية ، وهنا العشب سيكون بالتأكيد في متناول اليدين.

تركيب


ربما يكون التكوين هو الحالة الأكثر شيوعًا للعمل مع المدققين. قد يكون تنفيذ التكوين مختلفًا. على سبيل المثال ، في مكتبات joi و v8n الشهيرة ، يتم ذلك من خلال كائن وسلسلة من الأساليب:

 Joi.string().alphanum().min(0).max(255) 

على الرغم من أنها تبدو جميلة للوهلة الأولى ، إلا أن هذا النهج له عدة عيوب ، وواحد قاتل. وهنا هو الشيء. في تجربتي ، يعد المدقق دائمًا أمرًا مفيدًا لتطبيق معين ، لذا ينبغي أن يكون التركيز الرئيسي في المكتبة على راحة توسيع المدققين والتكامل مع النهج الحالي ، وليس على عدد البدائل الأساسية ، والتي ، في رأيي ، تزيد من وزن المكتبة فحسب ، ولكن لن يتم استخدام معظم. خذ على سبيل المثال نفس المدقق للسلسلة. ثم اتضح أنك تحتاج إلى قطع المسافات من النهايات ، ثم فجأة تحتاج إلى السماح باستخدام أحرف خاصة في حالة واحدة ، وفي مكان ما تحتاج إلى أن تقود إلى أحرف صغيرة ، إلخ. في الواقع ، يمكن أن يكون هناك الكثير من هذه العناصر الأولية ، وأنا لا أرى حتى نقطة البدء في إضافتها إلى المكتبة. في رأيي ، فإن استخدام الأشياء ضروري أيضًا ويؤدي إلى زيادة التعقيد أثناء التمدد ، على الرغم من أنه يبدو للوهلة الأولى أنه يجعل الحياة أسهل. على سبيل المثال ، ليس من السهل أن تكتب c joi أداة التحقق من الصحة .

نهج وظيفي والعشب هنا قد يساعد. نفس مثال التحقق من صحة الرقم المحدد في النطاق من 0 إلى 255:

 //    const isNumber = n => typeof n == 'number' && !isNaN(n); //  const numberValidator = Trava.Check(isNumber); const byteValidator = Trava.Compose([ numberValidator, Trava.Check(n => 0 <= n && n < 256), ]); byteValidator(-1); // ! 

تجعل العبارة Check مدققًا من التحقق من الحقيقة (value => true / false). ويؤلف سلاسل المدققين. عند تنفيذها ، يتم مقاطعة السلسلة بعد الخطأ الأول. الشيء المهم هو أن الوظائف العادية تستخدم في كل مكان ، والتي هي بسيطة جدا للتوسع والاستخدام. إن سهولة التوسع هذه ، في رأيي ، هي الميزة الرئيسية لمكتبة تحقق صالحة.

تقليديًا ، يتم احتلال مكان منفصل في التحقق من الصحة عن طريق التحقق من وجود قيمة خالية وغير محددة . هناك عوامل مساعدة في العشب لهذا:

 //   null  undefined const requiredNumberValidator = Trava.Required(numberValidator); requiredNumberValidator(undefined); // ! const optNumberValidator = Trava.Optional(numberValidator, 2); // 2 is default optNumberValidator(undefined); // 2 optNumberValidator(null); // null const nullNumberValidator = Trava.Nullable(numberValidator, 3); // 3 is default nullNumberValidator(undefined); // 3 nullNumberValidator(null); // 3 

هناك العديد من مشغلي المساعد في العشب ، وكلهم يؤلفون بشكل جميل ومدهش ببساطة التوسع. مثل الوظائف العادية :)

سلم


يتم تنظيم أنواع البيانات البسيطة في تسلسل هرمي. الحالات الأكثر شيوعًا هي الأشياء والمصفوفات. هناك عوامل تشغيل في العشب تسهل العمل معهم:

 //   const byteArrayValidator = Trava.Each(byteValidator); byteArrayValidator([1, -1, 2, -3]); // ValidationError: {"1":"Incorrect value","3":"Incorrect value"} //   const pointValidator = Trava.Keys({ x: numberValidator, y: numberValidator, }); pointValidator({x: 1, y: 'a'}); // ValidationError: {"y":"Incorrect value"} 

عند التحقق من صحة الكائنات ، تقرر التأكيد على شدة التعريف: جميع المفاتيح مطلوبة افتراضيًا (ملفوفة في مطلوب ). يتم تجاهل المفاتيح غير المحددة في أداة التحقق.

تفضل بعض jsonschema ، حلول الرباعية لوصف المدققين في شكل بيانات ، على سبيل المثال {x: 'number' ، y: 'number'} ، لكن هذا يؤدي إلى نفس الصعوبات عند التوسع. من المزايا المهمة لهذا النهج إمكانية إجراء تسلسل وتبادل الدوائر ، وهو أمر مستحيل مع الوظائف. ومع ذلك ، يمكن تنفيذ ذلك بسهولة أعلى الواجهة الوظيفية. لا حاجة لإخفاء وظائف وراء الخطوط! وظائف لديها بالفعل أسماء وهذا كل ما هو مطلوب.

لسهولة الاستخدام داخل أجهزة التحقق من الصحة ، يمكن حذف مشغلي " إنشاء المفاتيح والمفاتيح" ؛ كما أنه من الملائم التفاف جهاز التحقق من الجذر في برنامج Trava :

 const pointValidator = Trava({ //  -> Keys x: [numberValidator, Trava.Check(v => v > 180)], //  -> Compose y: [numberValidator, Trava.Check(v => v < 180)], }); 

إذا اتصلت بـ Trava باستخدام الوسيطة الثانية ، فستكون قيمة الإرجاع نتيجة تطبيق المدقق:

 const point = Trava({ x: [numberValidator, Trava.Check(v => v > 180)], y: [numberValidator, Trava.Check(v => v < 180)], }, //      { x: 200, y: 100, }); // { x: 200, y: 100 } 

حتى الآن ، تم تنفيذ الدعم فقط للصفائف والكائنات ، كما السم في الأساس JSON وهذا يكفي. سحب طلبات ويلكوم!

السياق


عند استخدام أداة التحقق من الصحة كمعلمة أخيرة ، يمكنك تمرير السياق الذي يمكن الوصول إليه من جميع أجهزة التحقق المسماة كالمعلمة الأخيرة. شخصيا ، هذه الفرصة لم تأتي في متناول يدي حتى الآن ، لكنها ممكنة.

بالنسبة لبعض أجهزة التحقق من الصحة التي قد تُرجع خطأً ، من الممكن تحديد رسالة خطأ على مستويات مختلفة. مثال:

 const pos = Trava.Check(v => v > 0); pos(-1); // ValidationError: "Incorrect value" (by default) 

تجاوز لحالة واحدة:

 const pos = Trava.Check(v => v > 0, "    "); pos(-1); // ValidationError: "    " 

تجاوز لجميع الحالات:

 Trava.Check.ErrorMessage = " "; pos(-1); // ValidationError: " " 

أيضًا ، للحصول على تكوين أكثر تفصيلًا ، يمكنك نقل وظيفة في مكان الخطأ ، والتي يجب أن تُرجع خطأ وسيتم استدعاؤها بمعلمات المصادقة.

استخدام القضية


غالبًا ما نسمم JSON على الواجهة الخلفية مع الكوا. يجلس الواجهة الأمامية ببطء أيضًا. من المريح أن يكون هناك مصادقون مشتركون من كلا الطرفين. والآن سأظهر حالة استخدام شبه حقيقية. افترض أنك تريد تطبيق واجهة برمجة التطبيقات لإنشاء وتحديث بيانات المريض.

 // validators.js const trava = require('trava'); const { isFilledString, isDate, isNumber } = require('../common/validators'); const patientSchema = { name: isFilledString, dateOfBirth: isDate, height: isNumber, } //        //      const patientNew = trava(patientSchema); //      const patientPatch = trava(mapValues(patientSchema, trava.Optional)); module.exports = { patientNew, patientPatch, }; // controllers.js const validate = require('./validators'); const { ValidationError } = require('../common/errors'); function create (ctx) { const patientData = validate.patientNew(ctx.request.body); //       Error,             Error if (patientData instanceof Error) return ValidationError(ctx, patientData); // ...create new patient } function update (ctx) { const patientData = validate.patientPatch(ctx.request.body); if (patientData instanceof Error) return ValidationError(ctx, patientData); // ...update patient data } 

المشتركة / أخطاء
const trava = requ ('trava') ؛

وظيفة التحقق من الصحة (ctx ، params) {
إذا (params instof Error) {
params = trava.ValidationError.extractData (params)؛
}
ctx.body = {
الكود: 'VALIDATION_ERROR' ،
بارامس،

ctx.status = HttpStatus.BAD_REQUEST؛
}

على الرغم من أن المثال بسيط للغاية ، إلا أنه لا يمكن تسميته مبسطة. في تطبيق حقيقي ، سوف تكون معقدة فقط من المدققين. يمكنك أيضًا التحقق من الصحة في البرامج الوسيطة - يتم تطبيق المدقق بالكامل على السياق أو نص الطلب.

في عملية العمل واستخدام التحقق من الصحة ، توصلنا إلى استنتاج مفاده أن أجهزة التحقق البسيطة المتزامنة ورسائل الخطأ البسيطة كافية تمامًا. في الواقع ، توصلنا إلى استنتاج مفاده أننا نستخدم رسالتين فقط: "مطلوب" و "غير صالح" ، والتي يتم ترجمتها على الواجهة الأمامية مع مطالبات الحقول. عمليات التحقق الأخرى التي تتطلب إجراءات إضافية (على سبيل المثال ، عند التسجيل للتحقق من وجود مثل هذا البريد بالفعل) هي خارج نطاق التحقق من الصحة. في أي حال ، العشب ليس حول هذه الحالة.

في الختام


في هذه المقالة القصيرة ، وصفت الوظيفة الكاملة للمكتبة تقريبًا ، خارج نطاق المقالة ، يوجد العديد من المساعدين الذين يبسطون الحياة. أطلب تفاصيل حول github github.com/uNmAnNeR/travajs .

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

Source: https://habr.com/ru/post/ar462189/


All Articles