معالجة الأخطاء

التسلسل الهرمي للاستثناءات، وسمات الأخطاء، وأخطاء حمولة HTTP 200، والتحقق من جانب العميل، وإرشادات إعادة المحاولة في Python SDK لـ Listings API.

يمكن استيراد جميع الاستثناءات من الوحدة listingsapi ذات المستوى الأعلى.

التسلسل الهرمي للاستثناءات

Code
ListingsAPIError├── APIError                    API-reported failure: non-2xx, or an error payload in HTTP 200│   ├── AuthenticationError     401, or payload codes SY90005 / SY90001│   ├── PermissionDeniedError   403, or payload code SY90003│   ├── NotFoundError           404│   ├── ValidationError         400 / 422, mutation failures, client-side validation│   ├── RateLimitError          429, or payload code RATE_LIMITED (carries .retry_after)│   └── InternalServerError     5xx (safe to retry)└── APIConnectionError          network failure: DNS, connection refused, timeout

التقط ListingsAPIError لمعالجة كل ما قد تثيره حزمة SDK، أو APIError لمعالجة كل ما أبلغت عنه واجهة API، أو فئة فرعية محددة لمعالجة حالات بعينها.

خصائص الاستثناءات

يتضمن APIError وفئاته الفرعية ما يلي:

الخاصيةالنوعالوصف
status_codeint | Noneرمز حالة HTTP (قد يكون 200 للأخطاء على مستوى الحمولة)
codestr | Noneأول رمز خطأ من المنصة، e.g. SY10005
errorslist[dict]عناصر الأخطاء بصيغة موحّدة، كل منها {"code", "message", "context"}
response_bodystr | Noneنص الاستجابة الخام من واجهة API
retry_afterfloat | NoneRateLimitError فقط؛ يُستخرج بتحليل ترويسة Retry-After

يرث APIConnectionError مباشرةً من ListingsAPIError ولا يتضمن أيًا من هذه الخصائص؛ وتصف رسالة الاستثناء فشل الشبكة.

التقاط الاستثناءات

Python
import listingsapi client = listingsapi.ListingsAPI() try:  location = client.locations.retrieve(16808)except listingsapi.AuthenticationError as e:  print("Invalid API key:", e.code)except listingsapi.PermissionDeniedError as e:  print("Not allowed:", e.status_code)except listingsapi.NotFoundError as e:  print("No such location:", e.status_code)except listingsapi.ValidationError as e:  for err in e.errors:      print(err["code"], err["message"], err["context"])except listingsapi.RateLimitError as e:  print("Rate limited, retry in", e.retry_after, "seconds")except listingsapi.InternalServerError as e:  print("Server error (safe to retry):", e.status_code)except listingsapi.APIConnectionError as e:  print("Network failure:", e)except listingsapi.APIError as e:  print("Unexpected API error:", e.status_code, e.code, e.response_body)

الأخطاء داخل استجابات HTTP 200

تُبلغ المنصة أحيانًا عن حالات فشل داخل جسم استجابة 200. تفحص حزمة SDK كل استجابة وتثير الاستثناءات، لذا لا تحتاج أبدًا إلى التحقق من أعلام success أو فحص مصفوفات errors بنفسك:

  • تُحوَّل أجسام errors[] ذات المستوى الأعلى إلى استثناءات حسب الرمز: يؤدي كل من SY90005 وSY90001 إلى إثارة AuthenticationError، ويؤدي SY90003 إلى إثارة PermissionDeniedError، ويؤدي RATE_LIMITED إلى إثارة RateLimitError، ويؤدي أي رمز آخر إلى إثارة APIError.
  • تؤدي استجابات عمليات الكتابة (الإنشاء والتحديث والأرشفة ونشر المنشورات) التي تتضمن success: false أو قائمة errors[] إلى إثارة ValidationError.

في كلتا الحالتين، يمثّل status_code حالة HTTP الفعلية (غالبًا 200)، ويمثّل code أول رمز من المنصة، ويحتوي errors على جميع العناصر بصيغتها الموحّدة:

Python
import listingsapi client = listingsapi.ListingsAPI() try:  result = client.locations.update({"id": 16808, "phone": "not-a-phone"})except listingsapi.ValidationError as e:  print(e.status_code)   # 200: the API accepted the request but rejected the mutation  print(e.code)          # first platform code, e.g. "SY10005"  for err in e.errors:      print(err["code"], err["message"], err["context"])

التحقق من الصحة من جانب العميل

يتحقق locations.add() والدوال المساعدة للمنشورات (posts.create_announcement() وposts.create_event() وposts.create_offer() وposts.bulk_publish()) من صحة حمولتها محليًا وتثير ValidationError قبل إجراء أي استدعاء عبر الشبكة، لذا تؤدي الحمولة غير الصالحة إلى فشل فوري مع رسالة واضحة بدلًا من إرسال طلب وانتظار استجابة.

بالنسبة إلى locations.add():

  • يجب أن يتراوح طول name بين 2 و150 حرفًا
  • يجب ألا يقل طول description عن 200 حرف
  • sub_category_id مطلوب
  • country_iso مطلوب
  • city مطلوب ما لم يتحقق hide_address=True (الأنشطة التجارية التي تخدم نطاقًا جغرافيًا)

بالنسبة إلى الدوال المساعدة للمنشورات:

  • name مطلوب ويجب ألا يكون location_ids فارغًا
  • يجب أن يكون sites مجموعة فرعية غير فارغة من GOOGLE، FACEBOOK
  • message مطلوب؛ وعندما يكون قاموسًا يخصص قيمة لكل موقع، يجب توفير نص لكل موقع مستهدف
  • يجب توفير cta_type وcta_url معًا، ويجب أن يكون cta_type إحدى القيم التالية: BOOK، ORDER، SHOP، LEARN_MORE، SIGN_UP، GET_OFFER
  • title مطلوب للفعاليات والعروض

في هذه الأخطاء التي تُثار محليًا، تكون قيمة كل من status_code وcode وresponse_body هي None، ويكون errors فارغًا؛ ويسرد str(e) جميع المشكلات التي عُثر عليها:

Python
import listingsapi client = listingsapi.ListingsAPI() try:  client.locations.add(      name="Acme Dental",      description="Too short.",      sub_category_id=1432,      country_iso="US",      city="New York",  )except listingsapi.ValidationError as e:  print(e)  # Invalid location: description must be at least 200 characters (got 10)  print(e.status_code)  # None: raised before any network call

إعادة المحاولة تلقائيًا

تعيد حزمة SDK المحاولة تلقائيًا عند حدوث 429 و500 و502 و503 و504 لطلبات GET وHEAD وOPTIONS (افتراضيًا، 2 محاولات إعادة، ومعامل تأخير أُسّي قدره 0.5 ثانية، مع الالتزام بـ Retry-After). لا تُعاد محاولة عمليات الكتابة: يُرسل طلب POST أو DELETE مرة واحدة ويُثار استثناء عند أول فشل. اضبط ذلك باستخدام وسيط المُنشئ max_retries؛ راجع الإعدادات.

لا تدعم واجهة API بالإصدار v4 ميزة Idempotency-Key ولا تكتشف عمليات الكتابة المكررة، لذا قد تكون عملية الكتابة التي تنتهي مهلتها أو تُرجع 5xx قد حُفظت بالفعل. ستؤدي إعادة محاولتها دون تحقق إلى إنشاء موقع ثانٍ أو منشور ثانٍ أو رد ثانٍ على تقييم. اقرأ السجل مجددًا باستخدام مفتاح تتحكم فيه قبل تكرار عملية الكتابة.

تأخير إعادة المحاولة عند حدوث RateLimitError

عندما يستمر حدوث 429 بعد محاولات الإعادة المدمجة، يحتوي RateLimitError.retry_after على قيمة Retry-After التي أرسلها الخادم بالثواني (أو None إذا كانت الترويسة غائبة). مثال بسيط لمهام المعالجة الدفعية:

Python
import randomimport time import listingsapi client = listingsapi.ListingsAPI(max_retries=0)  # manual retry control def with_backoff(call, max_attempts=5):  for attempt in range(max_attempts):      try:          return call()      except listingsapi.RateLimitError as e:          if attempt == max_attempts - 1:              raise          if e.retry_after is not None:              delay = e.retry_after          else:              delay = min(2 ** attempt, 30) + random.random()          time.sleep(delay) page = with_backoff(lambda: client.locations.list(first=50))for location in page:  print(location.name)

استيراد الاستثناءات

Python
# All exceptions from the top-level moduleimport listingsapi listingsapi.ListingsAPIErrorlistingsapi.APIErrorlistingsapi.AuthenticationErrorlistingsapi.PermissionDeniedErrorlistingsapi.NotFoundErrorlistingsapi.ValidationErrorlistingsapi.RateLimitErrorlistingsapi.InternalServerErrorlistingsapi.APIConnectionError # Or import individuallyfrom listingsapi import RateLimitError, ValidationError

انظر أيضًا