إنشاء واجهة برمجة تطبيقات REST باستخدام Node.js وقاعدة بيانات Oracle. الجزء 5

الجزء 5. إنشاء API REST: ترقيم الصفحات ، والفرز اليدوي ، والترشيح

في المقالة السابقة ، أكملت بناء الوظيفة الأساسية لواجهة برمجة التطبيقات (API) لـ CRUD.

والآن ، عندما يتم إصدار طلب HTTP GET على طريق الموظفين ، يتم إرجاع جميع صفوف الجدول. قد لا يكون هذا الأمر مهمًا مع وجود 107 صفوف فقط في جدول HR.EMPLOYEES ، ولكن تخيل ما سيحدث إذا كان الجدول يحتوي على آلاف أو ملايين الصفوف. عادةً ما يعرض العملاء ، مثل تطبيقات الجوال وتطبيقات الويب ، جزءًا صغيرًا فقط من الصفوف المتوفرة في قاعدة البيانات ، ثم تحديد المزيد من الصفوف عند الضرورة - ربما عندما يقوم المستخدم بالتمرير لأسفل أو النقر فوق الزر "التالي" في عنصر تحكم فاصل إلى الصفحات في واجهة المستخدم.

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

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

ترقيم الصفحات

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

ابدأ بتحديث منطق وحدة التحكم لاستخراج القيم من سلسلة الاستعلام وتمريرها إلى واجهة برمجة تطبيقات قاعدة البيانات. افتح الملف controllers / employee.js وأضف سطور التعليمات البرمجية التالية إلى دالة get بعد السطر الذي يوزع المعلمة req.params.id.

// *** line that parses req.params.id is here *** context.skip = parseInt(req.query.skip, 10); context.limit = parseInt(req.query.limit, 10); 

أنت الآن بحاجة إلى تحديث منطق قاعدة البيانات لأخذ هذه القيم في الاعتبار وتحديث استعلام SQL وفقًا لذلك. في SQL ، يتم استخدام جملة الإزاحة لتخطي الصفوف ، ويتم استخدام جملة الجلب للحد من عدد الصفوف التي يتم إرجاعها بواسطة الاستعلام. كالعادة ، لن تتم إضافة القيم مباشرةً إلى الاستعلام - بدلاً من ذلك ، ستتم إضافتها كمتغيرات ربط لأسباب تتعلق بالأداء والأمان. افتح db_apis / employee.js وأضف الكود التالي بعد block if في find find ، مما يضيف جملة where إلى الطلب.

 // *** if block that appends where clause ends here *** if (context.skip) { binds.row_offset = context.skip; query += '\noffset :row_offset rows'; } const limit = (context.limit > 0) ? context.limit : 30; binds.row_limit = limit; query += '\nfetch next :row_limit rows only'; 

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

 # use default limit (30) curl "http://localhost:3000/api/employees" # set limit to 5 curl "http://localhost:3000/api/employees?limit=5" # use default limit and set skip to 5 curl "http://localhost:3000/api/employees?skip=5" # set both skip and limit to 5 curl "http://localhost:3000/api/employees?skip=5&limit=5" 

الفرز

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

ارجع إلى controllers / employee.js وأضف سطر التعليمات البرمجية التالي إلى دالة get بعد السطر الذي يوزع المعلمة req.query.limit.

 // *** line that parses req.query.limit is here *** context.sort = req.query.sort; 

ثم افتح db_apis / employee.js وأضف السطر التالي أسفل الأسطر التي تعلن وتهيئة baseQuery.

 // *** lines that initalize baseQuery end here *** const sortableColumns = ['id', 'last_name', 'email', 'hire_date', 'salary']; 

sortableColumns هي قائمة بيضاء بالأعمدة التي يمكن للعملاء استخدامها للفرز. ثم ، داخل دالة find ، أضف ما يلي if block ، الذي يضيف الترتيب بحسب جملة. يجب أن يتم ذلك بعد إضافة جملة حيث ، ولكن قبل جمل الإزاحة وجلب.

 // *** if block that appends where clause ends here *** if (context.sort === undefined) { query += '\norder by last_name asc'; } else { let [column, order] = context.sort.split(':'); if (!sortableColumns.includes(column)) { throw new Error('Invalid "sort" column'); } if (order === undefined) { order = 'asc'; } if (order !== 'asc' && order !== 'desc') { throw new Error('Invalid "sort" order'); } query += `\norder by "${column}" ${order}`; } 

يتحقق الجزء الأول من الكتلة if من اجتياز العميل لقيمة الفرز. إذا لم يكن الأمر كذلك ، فسيتم إضافة الترتيب الافتراضي حسب الجملة إلى استعلام SQL ، الذي يفرز حسب اسم العائلة بالترتيب التصاعدي. إذا تم تحديد قيمة الفرز ، فسيتم تقسيمها أولاً إلى قيم الأعمدة والطلب ، ويتم التحقق من كل قيمة قبل إضافة الطلب إلى الاستعلام.

يمكنك الآن تشغيل العديد من أوامر URL للتحقق من صحتها. فيما يلي بعض الأمثلة التي يجب تجربتها:

 # use default sort (last_name asc) curl "http://localhost:3000/api/employees" # sort by id and use default direction (asc) curl "http://localhost:3000/api/employees?sort=id" # sort by hire_date desc curl "http://localhost:3000/api/employees?sort=hire_date:desc" # use sort with limit and skip together curl "http://localhost:3000/api/employees?limit=5&skip=5&sort=salary:desc" # should throw an error because first_name is not whitelisted curl "http://localhost:3000/api/employees?sort=first_name:desc" # should throw an error because 'other' is not a valid order curl "http://localhost:3000/api/employees?sort=last_name:other" 

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

تصفية

تعد القدرة على تصفية البيانات ميزة مهمة يجب أن توفرها جميع واجهات برمجة تطبيقات REST. كما هو الحال مع الفرز ، قد يكون التنفيذ بسيطًا أو معقدًا اعتمادًا على ما تريد دعمه. أسهل طريقة هي إضافة دعم لعوامل تصفية المطابقة الكاملة (على سبيل المثال ، last_name = Doe). يمكن للتطبيقات الأكثر تعقيدًا إضافة دعم للمشغلين الأساسيين (على سبيل المثال ، <،> ، instr ، وما إلى ذلك) والعوامل المنطقية المعقدة (على سبيل المثال ، و / أو) التي يمكنها تجميع عدة عوامل تصفية معًا.

سأحاول في هذا المنشور تبسيط الموقف وإضافة دعم المرشح لعمودين فقط: department_id و manager_id. لكل عمود ، سأقوم بتمكين المعلمة المقابلة في سلسلة الاستعلام. يجب تحديث منطق قاعدة البيانات الذي يضيف جملة حيث يتم إرسال طلبات GET إلى نقطة النهاية مع موظف واحد لمراعاة عوامل التصفية الجديدة هذه.

افتح وحدات التحكم / employee.js وأضف الأسطر التالية أسفل السطر الذي يوزع قيمة req.query.sort في دالة get.

 // *** line that parses req.query.sort is here *** context.department_id = parseInt(req.query.department_id, 10); context.manager_id = parseInt(req.query.manager_id, 10); 

ثم قم بتحرير db_apis / employee.js لإضافة الجملة 1 = 1 إلى الاستعلام الأساسي ، كما هو موضح أدناه.

 const baseQuery = `select employee_id "id", first_name "first_name", last_name "last_name", email "email", phone_number "phone_number", hire_date "hire_date", job_id "job_id", salary "salary", commission_pct "commission_pct", manager_id "manager_id", department_id "department_id" from employees where 1 = 1`; 

بالطبع ، 1 = 1 سيكون دائمًا صحيحًا ، لذلك سيتجاهل المحسن ببساطة. ومع ذلك ، ستعمل هذه الطريقة على تبسيط عملية إضافة عوامل تقييم إضافية في المستقبل.

في دالة find ، استبدل كتلة if ، التي تضيف جملة المكان عند تمرير سياق ، مع الأسطر التالية.

 // *** line that declares 'binds' is here *** if (context.id) { binds.employee_id = context.id; query += '\nand employee_id = :employee_id'; } if (context.department_id) { binds.department_id = context.department_id; query += '\nand department_id = :department_id'; } if (context.manager_id) { binds.manager_id = context.manager_id; query += '\nand manager_id = :manager_id'; } 

كما ترى ، فإن كل كتلة تضيف ببساطة القيمة التي تم تمريرها إلى كائن الربط ، ثم تضيف المسند المقابل إلى جملة حيث. حفظ التغييرات وإعادة تشغيل API. ثم استخدم أوامر URL هذه للتحقق من هذا:

 # filter where department_id = 90 (returns 3 employees) curl "http://localhost:3000/api/employees?department_id=90" # filter where manager_id = 100 (returns 14 employees) curl "http://localhost:3000/api/employees?manager_id=100" # filter where department_id = 90 and manager_id = 100 (returns 2 employees) curl "http://localhost:3000/api/employees?department_id=90&manager_id=100" 

هذا كل شيء - يدعم API الآن ترقيم الصفحات والفرز والتصفية! توفر الطريقة اليدوية الكثير من التحكم ، ولكنها تتطلب الكثير من التعليمات البرمجية. تحتوي وظيفة البحث الآن على 58 سطرًا وتدعم إمكانيات الفرز والتصفية المحدودة فقط. قد تفكر في استخدام وحدة نمطية ، مثل أداة إنشاء استعلام Knex.js ، لتبسيط هذه العمليات.

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


All Articles