الجزء 4. إنشاء واجهة برمجة تطبيقات REST: معالجة طلبات POST و PUT و DELETEفي
المقالة السابقة ، أضفت منطقًا إلى API لطلبات GET التي استرجعت البيانات من قاعدة بيانات. في هذا المنشور ، ستكمل الوظيفة الأساسية لواجهة برمجة التطبيقات (API) لـ CRUD عن طريق إضافة منطق للتعامل مع طلبات POST و PUT و DELETE.
مضيفا منطق التوجيهلتبسيط منطق التوجيه ، ستقوم بإعادة توجيه جميع أساليب HTTP عبر مسار موجود (باستخدام معلمة معرف اختيارية). افتح
ملف services / router.js واستبدل منطق التوجيه الحالي (الأسطر 5-6) بالكود التالي:
router.route('/employees/:id?') .get(employees.get) .post(employees.post) .put(employees.put) .delete(employees.delete);
يعين منطق التوجيه المحدّث أساليب HTTP الأربعة الأكثر شيوعًا المستخدمة في عمليات CRUD الأساسية إلى منطق وحدة التحكم الصحيح.
مرحلة ما بعد معالجة الطلبتُستخدم طلبات HTTP POST لإنشاء موارد جديدة (في هذه الحالة ، سجلات الموظفين). الفكرة الرئيسية هي استخراج البيانات من نص طلب HTTP واستخدامها لإنشاء صف جديد في قاعدة البيانات. لإضافة منطق وحدة التحكم لطلبات POST ، افتح ملف
controllers / employee.js وأضف الكود التالي:
function getEmployeeFromRec(req) { const employee = { first_name: req.body.first_name, last_name: req.body.last_name, email: req.body.email, phone_number: req.body.phone_number, hire_date: req.body.hire_date, job_id: req.body.job_id, salary: req.body.salary, commission_pct: req.body.commission_pct, manager_id: req.body.manager_id, department_id: req.body.department_id }; return employee; } async function post(req, res, next) { try { let employee = getEmployeeFromRec(req); employee = await employees.create(employee); res.status(201).json(employee); } catch (err) { next(err); } } module.exports.post = post;
تأخذ الدالة getEmployeeFromRec كائن طلب وتقوم بإرجاع كائن بالخصائص اللازمة لإنشاء سجل الموظف. تم الإعلان عن الوظيفة خارج وظيفة النشر بحيث يمكن استخدامها لاحقًا لطلبات PUT.
تستخدم وظيفة النشر getEmployeeFromRec لتهيئة المتغير ، والذي يتم بعد ذلك تمريره إلى طريقة إنشاء واجهة برمجة تطبيقات قاعدة بيانات الموظف. بعد عملية الإنشاء ، يتم إرسال رمز الحالة "201 Created" إلى العميل مع الموظف JSON (بما في ذلك القيمة الجديدة لمعرف الموظف).
الآن يمكنك الانتباه إلى المنطق في API قاعدة البيانات. افتح الملف
db_apis / employee.js وأضف الكود التالي أدناه.
const createSql = `insert into employees ( first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id ) values ( :first_name, :last_name, :email, :phone_number, :hire_date, :job_id, :salary, :commission_pct, :manager_id, :department_id ) returning employee_id into :employee_id`; async function create(emp) { const employee = Object.assign({}, emp); employee.employee_id = { dir: oracledb.BIND_OUT, type: oracledb.NUMBER } const result = await database.simpleExecute(createSql, employee); employee.employee_id = result.outBinds.employee_id[0]; return employee; } module.exports.create = create;
يبدأ المنطق أعلاه بإعلان ثابت المسمى createSql لتخزين عبارة insert. لاحظ أنه يستخدم
متغيرات الربط ، وليس تسلسل السلسلة ، للإشارة إلى القيم المراد إدراجها. يجدر تكرار مدى أهمية متغيرات الربط لأسباب تتعلق بالأمان والأداء. حاول تجنب تسلسل السلسلة كلما كان ذلك ممكنًا.
داخل دالة الإنشاء ، يتم تعريف وتثبيت ثابت الموظف للحصول على نسخة من المعلمة emp باستخدام Object.assign. هذا يمنع التعديل المباشر للكائن المنقول من وحدة التحكم.
ثم ، تتم إضافة الخاصية employee_id إلى كائن الموظف (تم تكوينه كـ "ربط خارجي") بحيث تحتوي على كافة متغيرات الربط اللازمة لتنفيذ عبارة SQL. بعد ذلك ، يتم استخدام الدالة simpleExecute لتنفيذ عبارة insert ، ويتم استخدام الخاصية outBinds للكتابة فوق خاصية employee.employee_id قبل إرجاع الكائن.
نظرًا لوجود رابط إلى وحدة oracledb ، فأنت بحاجة إلى إضافة السطر التالي إلى أعلى الملف.
const oracledb = require('oracledb');
وضع معالجة الطلبسيتم استخدام طلبات PUT لتحديث الموارد الحالية. من المهم ملاحظة أنه يتم استخدام PUT لاستبدال المورد بالكامل - فهو لا يقوم بإجراء تحديثات جزئية (سأوضح لك كيفية القيام بذلك باستخدام PATCH في المستقبل). ارجع إلى ملف
controllers / employee.js وأضف الكود التالي أدناه.
async function put(req, res, next) { try { let employee = getEmployeeFromRec(req); employee.employee_id = parseInt(req.params.id, 10); employee = await employees.update(employee); if (employee !== null) { res.status(200).json(employee); } else { res.status(404).end(); } } catch (err) { next(err); } } module.exports.put = put;
تستخدم الدالة put getEmployeeFromRec لتهيئة كائن يسمى الموظف ، ثم تضيف خاصية employee_id من معلمة المعرف إلى عنوان URL. ثم يتم تمرير كائن الموظف إلى وظيفة تحديث API لقاعدة البيانات ، ويتم إرسال الاستجابة المقابلة إلى العميل بناءً على النتيجة.
لإضافة منطق التحديث إلى API لقاعدة البيانات ، أضف التعليمات البرمجية التالية إلى ملف
db_apis / employee.js .
const updateSql = `update employees set 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 where employee_id = :employee_id`; async function update(emp) { const employee = Object.assign({}, emp); const result = await database.simpleExecute(updateSql, employee); if (result.rowsAffected && result.rowsAffected === 1) { return employee; } else { return null; } } module.exports.update = update;
منطق التحديث مشابه جدًا لمنطق الإنشاء. تم التصريح عن متغير لتخزين عبارة SQL ، ثم يستخدم simpleExecute لتنفيذ العبارة مع القيم الديناميكية المنقولة (بعد نسخها إلى كائن آخر). يتم استخدام result.rowsAffected لتحديد ما إذا كان التحديث ناجحًا وإرجاع القيمة الصحيحة.
حذف طلب معالجةالطريقة الأخيرة التي تحتاج إلى تنفيذها هي DELETE ، والتي ، مما لا يثير الدهشة ، ستزيل الموارد من قاعدة البيانات. أضف التعليمات البرمجية التالية في نهاية ملف
controllers / employee.js. async function del(req, res, next) { try { const id = parseInt(req.params.id, 10); const success = await employees.delete(id); if (success) { res.status(204).end(); } else { res.status(404).end(); } } catch (err) { next(err); } } module.exports.delete = del;
سوف يلقي محرك JavaScript استثناءًا إذا حاولت إعلان وظيفة تسمى "حذف" باستخدام مشغل الوظيفة. للتغلب على هذا ، يتم الإعلان عن وظيفة باسم "del" ، ثم تصديرها كـ "delete".
في هذه المرحلة ، يجب أن تكون قادرًا على قراءة المنطق وفهمه. بخلاف الأمثلة السابقة ، لا يحتوي طلب HTTP هذا على نص ؛ يتم استخدام معلمة المعرف فقط في مسار المسار. غالبًا ما يتم استخدام رمز الحالة "204 بلا محتوى" مع طلبات DELETE عندما لا يتم إرسال نص الاستجابة.
لإكمال منطق قاعدة البيانات ، ارجع إلى ملف
db_apis / employee.js وأضف الكود التالي إلى النهاية.
const deleteSql = `begin delete from job_history where employee_id = :employee_id; delete from employees where employee_id = :employee_id; :rowcount := sql%rowcount; end;` async function del(id) { const binds = { employee_id: id, rowcount: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER } } const result = await database.simpleExecute(deleteSql, binds); return result.outBinds.rowcount === 1; } module.exports.delete = del;
لأن الجدول JOB_HISTORY يحتوي على قيد مفتاح خارجي يشير إلى جدول الموظفين ، يتم استخدام كتلة PL / SQL بسيطة لحذف الصفوف الضرورية من كلا الجدولين في دورة واحدة.
تحليل طلبات JSONإذا نظرت إلى وظيفة getEmployeeFromRec في وحدات التحكم / employee.js ، ستلاحظ أن خاصية نص الطلب هي كائن JavaScript. يوفر هذا طريقة سهلة للحصول على قيم من نص الطلب ، لكن هذا لا يحدث تلقائيًا.
تتوقع واجهة برمجة التطبيقات التي تنشئها من العملاء إرسال البيانات بتنسيق JSON في نص طلبات POST و PUT. بالإضافة إلى ذلك ، يجب على العملاء تعيين رأس Content-Type للطلب إلى التطبيق / json حتى يعرف خادم الويب نوع نص الطلب الذي تم إرساله. يمكنك استخدام الوسيطة Express.json المضمنة بحيث يمكن لـ Express تحليل هذه الطلبات.
افتح ملف
services / web-server.js وأضف الأسطر التالية أسفل مكالمة app.use مباشرةً ، والتي تضيف مورغان إلى خط أنابيب الطلب.
عند تحليل بيانات JSON في كائنات JavaScript الأصلية ، عندئذٍ فقط أنواع البيانات المدعومة في JSON سيتم تعيينها بشكل صحيح إلى أنواع JavaScript. التواريخ غير مدعومة في JSON وعادةً ما يتم تمثيلها كسلسلة ISO 8601. باستخدام وظيفة reviver التي تم تمريرها إلى Express.json ، يمكنك إجراء التحويل يدويًا. أضف الكود التالي إلى نهاية ملف
services / web-server.js .
const iso8601RegExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/; function reviveJson(key, value) {
اختبار APIحان الوقت لاختبار وظيفة CRUD الجديدة! حتى الآن ، لقد استخدمت المستعرض لاختبار واجهة برمجة التطبيقات ، لكن هذا لن يعمل مع طلبات POST و PUT و DELETE. سأوضح لك كيفية اختبار واجهة برمجة التطبيقات باستخدام أوامر curl لأنه يمكن الوصول إليها بسهولة في جهاز افتراضي. ولكن يمكنك استخدام أداة رسومية مثل
Postman و
Insomina (مجانًا).
قم بتشغيل التطبيق ، ثم افتح نافذة طرفية أخرى وقم بتشغيل الأمر التالي لإنشاء موظف جديد.
curl -X "POST" "http://localhost:3000/api/employees" \ -i \ -H 'Content-Type: application/json' \ -d $'{ "first_name": "Dan", "last_name": "McGhan", "email": "dan@fakemail.com", "job_id": "IT_PROG", "hire_date": "2014-12-14T00:00:00.000Z", "phone_number": "123-321-1234" }'
إذا كان الطلب ناجحًا ، فيجب أن تحتوي الاستجابة على كائن موظف مع السمة employee_id. هنا مثال:

في حالتي ، كانت قيمة employee_id هي 227 - ستحتاج إلى تغيير الأوامر التالية استنادًا إلى القيمة التي تلقاها employee_id.
على سبيل المثال ، لتحديث إدخال جديد ، أدخل PUT لعنوان URL بقيمة المعرف هذه.
curl -X "PUT" "http://localhost:3000/api/employees/227" \ -i \ -H 'Content-Type: application/json' \ -d $'{ "first_name": "Dan", "last_name": "McGhan", "email": "dan@fakemail.com", "job_id": "AD_PRES", "hire_date": "2014-12-14T00:00:00.000Z", "phone_number": "123-321-1234" }'
سيقوم المشغل UPDATE_JOB_HISTORY في مخطط الموارد البشرية بالكشف عن تغيير الوظيفة وإضافة صف إلى جدول JOB_HISTORY. إذا نظرت إلى هذا الجدول ، فسترى سجلاً للموظف الجديد. قم بتشغيل الأمر التالي لحذف محفوظات الوظيفة وسجلات الموظفين.
curl -i -X "DELETE" "http://localhost:3000/api/employees/227"
والآن ، لديك كل شيء ، وظائف CRUD كاملة!
واجهة برمجة التطبيقات (API) تسير بشكل جيد ، ولكن هناك عمل يجب القيام به. في
المشاركة الأخيرة ، سأريك كيفية إضافة ترقيم الصفحات ، والفرز ، وتصفية القدرات في طلبات GET.