قصة عن كيفية تصميم API

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

الصورة

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

نظرة عامة على الوضع


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

المشكلة رقم 1: طلب تنسيق النص


نظرًا لأن العميل مهتم فقط بمعلومات حول ما إذا كانت غرفة الفندق مجانية أو مشغول ، على سبيل /getAvailabilities ، نحن مهتمون فقط بالإشارة إلى /getAvailabilities API /getAvailabilities . وعلى الرغم من أن الدعوة إلى مثل واجهة برمجة التطبيقات هذه يجب أن تؤدي إلى بيانات حول توفر الغرف ، إلا أن هذه الدعوة ، في الواقع ، تبدو وكأنها طلب POST ، نظرًا لأن مؤلف واجهة برمجة التطبيقات قرر تزويده بالقدرة على قبول المرشحات كجسم JSON للطلب. فيما يلي قائمة بمعلمات الاستعلام الممكنة وأمثلة للقيم التي تقبلها:

 {   "checkIn": "20151001",   "lastNight": "20151002",   "checkOut": "20151003",   "roomId": "12345",   "propId": "1234",   "ownerId": "123",   "numAdult": "2",   "numChild": "0",   "offerId": "1",   "voucherCode": "",   "referer": "",   "agent": "",   "ignoreAvail": false,   "propIds": [       1235,       1236   ],   "roomIds": [       12347,       12348,       12349   ] } 

دعنا نسير عبر كائن JSON هذا ونتحدث عن الأخطاء هنا.

  1. التواريخ ( checkIn و lastNight و checkOut ) checkOut . لا يوجد أي سبب مطلقًا لعدم استخدام تنسيق ISO 8601 القياسي ( YYYY-MM-DD ) عند تحويل التواريخ إلى سلاسل ، لأن هذا المعيار يستخدم على نطاق واسع لعرض التواريخ. إنه مألوف لدى العديد من المطورين ، وهو ما يتوقعه العديد من محلل JSON لتلقيه عند المدخلات. بالإضافة إلى ذلك ، هناك شعور بأن حقل lastNight متكرر ، نظرًا لوجود حقل checkOut ، والذي يتم تمثيله دائمًا بتاريخ قبل يوم واحد من التاريخ المحدد في lastNight . فيما يتعلق بأوجه القصور المذكورة أعلاه ، أقترح ، عند تصميم واجهات برمجة التطبيقات هذه ، السعي دائمًا لاستخدام الأساليب القياسية لتقديم التواريخ ومحاولة عدم تحميل مستخدمي واجهة برمجة التطبيقات الحاجة إلى العمل مع البيانات المكررة.
  2. جميع حقول المعرف ، وكذلك numAdult و numAdult ، numChild ، لكن يتم numAdult numChild . في هذه الحالة ، لا يوجد سبب واضح لتمثيلهم كسلسلة.
  3. هنا يمكنك ملاحظة أزواج الحقول التالية: roomId و roomIds ، وكذلك propId و propIds . وجود propId و propId أمر ضروري ، حيث يمكن استخدام كلاهما لنقل المعرفات. بالإضافة إلى ذلك ، هناك مشكلة مع الأنواع. يرجى ملاحظة أن مجال roomId هو حقل سلسلة ، ويجب استخدام القيم العددية للمعرفات في مجموعة roomIds . قد يؤدي ذلك إلى حدوث تشويش ومشاكل في التحليل ، وبالإضافة إلى ذلك ، تشير إلى أنه يتم إجراء بعض العمليات على الخادم بالسلاسل ، وبعضها مع أرقام ، على الرغم من حقيقة أن هذه السلاسل والأرقام تُستخدم لتمثيل نفس البيانات.

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

المشكلة رقم 2: تنسيق نص الاستجابة


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

هنا هو الطلب:

 {   "checkIn": "20190501",   "checkOut": "20190503",   "ownerId": "25748",   "numAdult": "2",   "numChild": "0" } 

هنا الجواب:

 {   "10328": {       "roomId": "10328",       "propId": "4478",       "roomsavail": "0"   },   "13219": {       "roomId": "13219",       "propId": "5729",       "roomsavail": "0"   },   "14900": {       "roomId": "14900",       "propId": "6779",       "roomsavail": 1   },   "checkIn": "20190501",   "lastNight": "20190502",   "checkOut": "20190503",   "ownerId": 25748,   "numAdult": 2 } 

تحدث عن مشاكل الاستجابة.

  1. في نص الاستجابة ، أصبحت numAdult و numAdult فجأة أرقامًا. وفي الطلب كان من الضروري الإشارة إليها في شكل سلاسل.
  2. يتم عرض قائمة الكائنات العقارية في شكل خصائص الكائن ، والتي تكون مفاتيحها عبارة عن معرفات الغرفة ( roomId ). سيكون من المنطقي توقع أن يتم إخراج هذه البيانات كصفيف. بالنسبة لنا ، هذا يعني أنه من أجل الحصول على قائمة من الغرف المتاحة ، نحتاج إلى roomsavail على الكائن بأكمله ، بينما roomsavail من وجود خصائص معينة للكائنات المحاطة به ، مثل roomsavail ، وعدم الانتباه إلى شيء مثل checkIn و lastNight . بعد ذلك ، سيكون من الضروري التحقق من قيمة العقار roomsavail ، وإذا كان أكبر من 0 ، يمكننا أن نستنتج أن الخاصية المقابلة متاحة للحجز. الآن دعونا نلقي نظرة على الممتلكات. فيما يلي خيارات تقديمه في هيئة الاستجابة: "roomsavail": "0" و "roomsavail": 1 . انظر النمط؟ إذا كانت الغرف مشغولة ، يتم تمثيل قيمة العقار بسلسلة. إذا مجانا - يتحول إلى رقم. يمكن أن يؤدي ذلك إلى العديد من المشكلات في اللغات التي ترتبط ارتباطًا وثيقًا بأنواع البيانات ، حيث أن الخاصية نفسها فيها لا يجب أن تأخذ قيمًا من أنواع مختلفة. فيما يتعلق بما تقدم ، أود أن أقترح على المطورين استخدام صفائف كائنات JSON لتمثيل مجموعات بيانات معينة ، وعدم استخدام إنشاءات غير ملائمة في شكل أزواج ذات قيمة مفتاح مماثلة لتلك التي ندرسها هنا. بالإضافة إلى ذلك ، من الضروري التأكد من أن مجالات الكائنات المتجانسة لا تحتوي على بيانات من أنواع مختلفة. قد تبدو استجابة الخادم المنسقة بشكل صحيح مثل الاستجابة أدناه. يرجى ملاحظة أنه عند تقديم البيانات في هذا النموذج ، لا تحتوي معلومات الغرفة على بيانات مكررة.

 {   "properties": [       {           "id": 4478,           "rooms": [               {                   "id": 12328,                   "available": false               }           ]       },       {           "id": 5729,           "rooms": [               {                   "id": 13219,                   "available": false               }           ]       },       {           "id": 6779,           "rooms": [               {                   "id": 14900,                   "available": true               }           ]       }   ],   "checkIn": "2019-05-01",   "lastNight": "2019-05-02",   "checkOut": "2019-05-03",   "ownerId": 25748,   "numAdult": 2 } 

المشكلة 3: معالجة الأخطاء


هذه هي الطريقة التي يتم بها تنظيم معالجة الأخطاء في واجهة برمجة التطبيقات التي يتم النظر فيها هنا: يرسل النظام ردودًا تحتوي على كود 200 لجميع الطلبات ، حتى في حالة حدوث خطأ. هذا يعني أن الطريقة الوحيدة لتمييز استجابة عادية من استجابة برسالة خطأ هي تحليل نص الاستجابة والتحقق من وجود حقول error أو errorCode فيه. يتم توفير رموز الخطأ 6 التالية فقط في API.


Beds24 رموز خطأ API

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

في حالتنا ، هناك طريقتان لتحسين واجهة برمجة التطبيقات في هذا الاتجاه: يمكنك إما توفير رمز HTTP خاص في حدود 400-499 لكل من الأخطاء الستة المحتملة (من الأفضل القيام بذلك) ، أو العودة ، في حالة حدوث خطأ ، فإن الكود 500 ، الذي سيسمح على الأقل ، يجب أن يعرف العميل قبل تحليل نص الاستجابة الذي يحتوي على معلومات حول الخطأ.

المشكلة رقم 4: "تعليمات"


فيما يلي "التعليمات" الخاصة باستخدام API من وثائق المشروع:

يرجى قراءة التعليمات التالية عند استخدام API.

  1. يجب تصميم مكالمات واجهة برمجة التطبيقات (API) بحيث يكون على التنفيذ أثناء إرسالها إرسال واستقبال الحد الأدنى من البيانات.
  2. يتم إجراء مكالمات API واحدة في كل مرة. يجب الانتظار حتى المكالمة التالية إلى API قبل إجراء المكالمة التالية.
  3. إذا كنت بحاجة إلى إجراء عدة مكالمات إلى واجهة برمجة التطبيقات ، فيجب توفير وقفة من بضع ثوانٍ بينهما.
  4. لا يلزم إجراء مكالمات API كثيرًا ، مع الحفاظ على مستوى المكالمات عند الحد الأدنى الضروري لحل مهام العميل.
  5. سيؤدي الاستخدام المفرط لواجهة برمجة التطبيقات خلال فترة 5 دقائق إلى تعليق حسابك دون إخطارات إضافية.
  6. نحن نحتفظ بالحق في منع الوصول إلى النظام للعملاء الذين يستخدمون واجهة برمجة التطبيقات بشكل مفرط ، في رأينا. يتم ذلك وفقًا لتقديرنا وبدون إشعار آخر.

في حين أن النقطتين 1 و 4 تبدو مبررة تمامًا ، إلا أنني لا أتفق مع النقاط الأخرى في هذه التعليمات. النظر فيها.

  1. البند رقم 2. إذا كنت تقوم بتطوير واجهة برمجة تطبيقات REST ، فمن المفترض أن تكون واجهة برمجة تطبيقات مستقلة عن الدولة. يعد استقلال مكالمات واجهة برمجة التطبيقات (API) عن المكالمات السابقة لها أحد الأسباب التي جعلت تقنية REST قد وجدت تطبيقًا واسعًا في التطبيقات السحابية. إذا لم تدعم وحدة معينة من النظام الحالة ، فيمكن إعادة نشرها بسهولة في حالة حدوث خطأ. يمكن للأنظمة القائمة على هذه الوحدات أن تقيس بسهولة عندما يتغير الحمل عليها. عند تصميم واجهة برمجة تطبيقات RESTful ، يجب عليك التأكد من أنها واجهة برمجة تطبيقات مستقلة عن الدولة وأن من يستخدمونها لا داعي للقلق بشأن شيء مثل تنفيذ طلب واحد فقط في وقت واحد.
  2. البند رقم 3. هذا البند يبدو غريبا نوعا ما وغامض. لا أستطيع أن أفهم السبب وراء كتابة هذه الفقرة من التعليمات ، لكنني أشعر أنه يخبرنا أنه في عملية معالجة الطلب ، يقوم النظام بإجراءات معينة ، وإذا كان "يصرف" حسب طلب آخر ، لم ترسل في الوقت المحدد ، وهذا قد يتداخل مع عملها. بالإضافة إلى ذلك ، فإن حقيقة أن مؤلف الدليل يقول "بضع ثوان" لا تسمح لنا بمعرفة المدة الدقيقة للإيقاف التي يجب أن تستمر بين الطلبات المتتالية.
  3. البندان رقم 5 ورقم 6. يشير إلى "الاستخدام المفرط لواجهة برمجة التطبيقات" ، ولكن لا توجد معايير "الاستخدام المفرط". ربما يكون 10 طلبات في الثانية الواحدة؟ أو ربما 1؟ بالإضافة إلى ذلك ، يمكن أن تحتوي بعض مشاريع الويب على كميات هائلة من الزيارات. إذا قاموا ، دون أي أسباب كافية ودون إخطار ، بإغلاق الوصول إلى واجهة برمجة التطبيقات التي يحتاجون إليها ، فمن المرجح أن مسؤوليهم يرفضون استخدام واجهات برمجة التطبيقات هذه. إذا كنت تكتب مثل هذه التعليمات ، فاستخدم لغة واضحة فيها ووضع نفسك في مكان المستخدمين الذين يتعين عليهم العمل مع نظامك ، مسترشدين بتعليماتك.

المشكلة رقم 5: الوثائق


هذا ما تبدو عليه وثائق واجهة برمجة التطبيقات.


Beds24 وثائق API

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


تحسين الوثائق

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

بالمناسبة ، إذا كنت ترغب في إلقاء نظرة على الوثائق الخاصة بواجهة برمجة تطبيقات Beds24 ، ألق نظرة هنا .

المسألة 6: الأمن


توضح الوثائق الخاصة بجميع نقاط نهاية واجهة برمجة التطبيقات ما يلي:

لاستخدام هذه الوظائف ، يجب السماح بالوصول إلى واجهة برمجة التطبيقات. يتم ذلك في قائمة الإعدادات → الحساب → الوصول إلى الحساب.

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

تتطلب معظم أساليب JSON مفتاح API للوصول إلى حساب. يمكن تعيين مفتاح الوصول إلى واجهة برمجة التطبيقات (API) باستخدام قائمة الإعدادات ← حساب ← حساب الوصول.

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

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


مثال على مصادقة API لـ Beds24

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

المشكلة رقم 7: الأداء


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

ملخص


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

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

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

أعزائي القراء! هل واجهت واجهات برمجة التطبيقات سيئة التصميم؟

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


All Articles