أفضل الممارسات باستخدام خدمات ويب واجهة برمجة التطبيقات للمنطقة الزمنية

خدمات الويب "منصة خرائط Google" هي مجموعة من واجهات HTTP لخدمات Google التي توفّر بيانات جغرافية لتطبيقات الخرائط.

يصف هذا الدليل بعض الممارسات الشائعة المفيدة لإعداد طلبات خدمة الويب ومعالجة ردود الخدمات. يُرجى الرجوع إلى دليل المطوّر للاطّلاع على المستندات الكاملة حول واجهة برمجة التطبيقات Time Zone.

ما المقصود بخدمة الويب؟

خدمات "منصة خرائط Google" على الويب هي واجهة لطلب بيانات واجهة برمجة التطبيقات للخرائط من الخدمات الخارجية واستخدام البيانات داخل تطبيقات "خرائط Google". هذه الخدمات مصمَّمة لاستخدامها مع الخريطة، وفقًا لقيود الترخيص الواردة في بنود خدمة "منصة خرائط Google".

تستخدم خدمات الويب لـ Maps APIs طلبات HTTP(S) لعناوين URL محدَّدة، مع تمرير معلمات عناوين URL و/أو بيانات POST بتنسيق JSON كوسيطات للخدمات. وبشكل عام، تعرض هذه الخدمات البيانات في نص الاستجابة بتنسيق JSON أو XML لتحليلها و/أو معالجتها من خلال تطبيقك.

ويكون طلب واجهة برمجة التطبيقات للمناطق الزمنية النموذجي بشكل عام على النحو التالي:

https://maps.googleapis.com/maps/api/timezone/output?parameters

حيث تشير السمة output إلى تنسيق الردّ (عادةً json أو xml).

ملاحظة: تتطلب جميع تطبيقات واجهة برمجة التطبيقات للمنطقة الزمنية المصادقة. يمكنك الحصول على مزيد من المعلومات حول بيانات اعتماد المصادقة.

الوصول عبر طبقة المقابس الآمنة/بروتوكول أمان طبقة النقل (TLS)

يجب استخدام HTTPS لكل طلبات "منصة خرائط Google" التي تستخدم مفاتيح واجهة برمجة التطبيقات أو التي تحتوي على بيانات المستخدمين. قد يتم رفض الطلبات المقدّمة عبر HTTP والتي تحتوي على بيانات حسّاسة.

إنشاء عنوان URL صالح

قد تظنّ أنّ عنوان URL "الصالح" واضح بذاته، إلا أنّ الأمر ليس كذلك. عنوان URL الذي يتم إدخاله في شريط عناوين في أحد المتصفحات، على سبيل المثال، قد يحتوي على رموز خاصة (مثل "上海+中國")؛ يحتاج المتصفّح إلى ترجمة تلك الأحرف داخليًا إلى ترميز مختلف قبل النقل. وتجدر الإشارة إلى أنّ أي رمز ينشئ أو يقبل إدخال UTF-8 قد يتعامل مع عناوين URL التي تتضمن أحرف UTF-8 على أنها "صالحة"، ولكن قد يحتاج أيضًا إلى ترجمة تلك الأحرف قبل إرسالها إلى خادم ويب. يُطلَق على هذه العملية اسم ترميز عناوين URL أو الترميز المئوية.

الأحرف الخاصة

نحتاج إلى ترجمة الرموز الخاصة لأنّ جميع عناوين URL يجب أن تتوافق مع البنية المحدّدة في مواصفات معرّف الموارد المنتظم (URI). ويعني ذلك أنّ عناوين URL يجب أن تحتوي فقط على مجموعة فرعية خاصة من أحرف ASCII وهي: الرموز الأبجدية الرقمية المألوفة وبعض الأحرف المحجوزة لاستخدامها كأحرف تحكّم ضمن عناوين URL. يلخص هذا الجدول الأحرف التالية:

ملخّص أحرف عناوين URL الصالحة
تحديدالأحرفاستخدام عنوان URL
أحرف أبجدية رقمية ب السلاسل النصية واستخدام المخطط (http) والمنفذ (8080) وما إلى ذلك
غير محجوز - _ . ~ السلاسل النصية
تم الحجز ! * ' ( ) ; : @ & = + $ , / ? % # [ ] أحرف التحكّم و/أو سلاسل النص

عند إنشاء عنوان URL صالح، يجب التأكّد من أنّه لا يحتوي إلا على الأحرف التي تظهر في جدول "ملخّص أحرف عناوين URL الصالحة". يؤدي إنشاء عنوان URL لاستخدام هذه المجموعة من الأحرف إلى حدوث مشكلتَين، إحداهما عملية الحذف والأخرى البديلة:

  • الأحرف التي تريد معالجتها خارج المجموعة أعلاه. على سبيل المثال، يجب ترميز الأحرف باللغات الأجنبية مثل 上海+中國 باستخدام الأحرف المذكورة أعلاه. وفقًا للاصطلاحات الشائعة، غالبًا ما يتم تمثيل المسافات (غير المسموح بها ضمن عناوين URL) باستخدام حرف علامة الجمع '+' أيضًا.
  • توجد الأحرف ضمن المجموعة أعلاه كأحرف محجوزة، ولكن يجب استخدامها حرفيًا. على سبيل المثال، يتم استخدام ? ضمن عناوين URL للإشارة إلى بداية سلسلة طلب البحث. وإذا كنت تريد استخدام السلسلة "? and Mysterions"، عليك ترميز الحرف '?'.

يتم ترميز جميع الأحرف المطلوب ترميزها بعنوان URL باستخدام حرف '%' وقيمة سداسية عشرية مكوّنة من حرفَين تتوافق مع حرف UTF-8. على سبيل المثال، 上海+中國 بترميز UTF-8 سيتم ترميزها بعنوان URL بالصيغة %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. سيتم ترميز السلسلة ? and the Mysterians بعنوان URL بالشكل %3F+and+the+Mysterians أو %3F%20and%20the%20Mysterians.

الأحرف الشائعة التي تحتاج إلى ترميز

في ما يلي بعض الأحرف الشائعة التي يجب ترميزها:

حرف غير آمن قيمة مشفّرة
مسافة %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

قد يكون تحويل عنوان URL تتلقّاه من البيانات التي يُدخلها المستخدم أمرًا صعبًا في بعض الأحيان. على سبيل المثال، قد يُدخل المستخدم عنوانًا على النحو التالي: "5 شارع جامعة الدول العربية". بشكل عام، يجب إنشاء عنوان URL من أجزائه، مع التعامل مع أي إدخال للمستخدم كأحرف حرفية.

بالإضافة إلى ذلك، تقتصر عناوين URL على 16384 حرفًا لجميع خدمات الويب في "منصة خرائط Google" وواجهات برمجة تطبيقات الويب الثابتة. نادرًا ما يتم تطبيق هذا العدد المسموح به في معظم الخدمات، مع ذلك، يُرجى العلم أنّ بعض الخدمات تتضمّن عدة معلَمات قد تؤدي إلى إنشاء عناوين URL طويلة.

استخدام مهذب لواجهات Google APIs

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

تراجع أسي

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

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

على سبيل المثال، جرّب أحد التطبيقات التي تريد إرسال هذا الطلب إلى واجهة برمجة التطبيقات Time Zone:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

يوضح مثال بايثون التالي كيفية إجراء الطلب باستخدام تراجع أسي:

import json
import time
import urllib.error
import urllib.parse
import urllib.request

# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/timezone/get-api-key
API_KEY = "YOUR_KEY_HERE"
TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json"


def timezone(lat, lng, timestamp):

    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode(
        {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,}
    )
    url = f"{TIMEZONE_BASE_URL}?{params}"

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 5  # Set the maximum retry delay to 5 seconds.

    while True:
        try:
            # Get the API response.
            response = urllib.request.urlopen(url)
        except urllib.error.URLError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.load(response)

            if result["status"] == "OK":
                return result["timeZoneId"]
            elif result["status"] != "UNKNOWN_ERROR":
                # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
                # ZERO_RESULTS. There is no point retrying these requests.
                raise Exception(result["error_message"])

        if current_delay > max_delay:
            raise Exception("Too many retry attempts.")

        print("Waiting", current_delay, "seconds before retrying.")

        time.sleep(current_delay)
        current_delay *= 2  # Increase the delay each time we retry.


if __name__ == "__main__":
    tz = timezone(39.6034810, -119.6822510, 1331161200)
    print(f"Timezone: {tz}")

يجب أيضًا توخي الحذر لعدم وجود رمز لإعادة محاولة إدخال رمز أعلى في سلسلة استدعاءات التطبيق يؤدي إلى تكرار الطلبات في تتابع سريع.

الطلبات المتزامنة

قد تبدو الأعداد الكبيرة من الطلبات المتزامنة إلى واجهات برمجة تطبيقات Google كهجوم لحجب الخدمة الموزعة (DDoS) على البنية الأساسية لـ Google، وسيتم التعامل معه وفقًا لذلك. ولتجنّب ذلك، عليك التأكّد من عدم مزامنة طلبات واجهة برمجة التطبيقات بين العملاء.

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

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

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

بغض النظر عن بداية الدقيقة، يجب أن تحرص على عدم استهدافها في بداية الساعة، كما أنّ وقت بدء كل يوم في منتصف الليل.

جارٍ معالجة الردود

يناقش هذا القسم كيفية استخراج هذه القيم ديناميكيًا من ردود خدمة الويب.

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

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

جارٍ معالجة XML باستخدام XPath

XML هو تنسيق معلومات منظَّمة ناضج نسبيًا يُستخدم لتبادل البيانات. على الرغم من أنّ تنسيق XML ليس خفيفًا مثل JSON، فهو يوفّر المزيد من اللغات وأدوات أكثر فعالية. على سبيل المثال، تم تضمين رمز معالجة XML في Java في حِزم javax.xml.

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

تعبيرات XPath

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

يستخدم XPath التعبيرات لتحديد العناصر داخل مستند XML، وذلك باستخدام بناء جملة مشابه لتلك المستخدمة في مسارات الدليل. تحدد هذه التعبيرات العناصر داخل شجرة مستندات XML، وهي شجرة هرمية مشابهة لشجرة DOM. بشكل عام، تُعتبر تعبيرات XPath جامدة، مما يشير إلى أنها ستطابق جميع العُقد التي تطابق المعايير المقدمة.

سنستخدم ملف XML المجرّد التالي لتوضيح أمثلةنا:

<WebServiceResponse>
 <status>OK</status>
 <result>
  <type>sample</type>
  <name>Sample XML</name>
  <location>
   <lat>37.4217550</lat>
   <lng>-122.0846330</lng>
  </location>
 </result>
 <result>
  <message>The secret message</message>
 </result>
</WebServiceResponse>

اختيار العُقدة في التعبيرات

تحدد اختيارات XPath عُقد. تشمل العقدة الجذر المستند بأكمله. أنت تختار هذه العقدة باستخدام التعبير الخاص "/". لاحظ أن عقدة الجذر ليست هي عُقدة المستوى الأعلى لمستند XML، فهي في الواقع موجودة على مستوى واحد فوق عنصر المستوى الأعلى هذا وتتضمنها.

تمثل عُقد العناصر العناصر المتنوعة داخل شجرة مستندات XML. على سبيل المثال، يمثّل عنصر <WebServiceResponse> عنصر المستوى الأعلى الذي يتم عرضه في نموذج الخدمة أعلاه. ويمكنك اختيار العُقد الفردية إما من خلال مسارات مطلقة أو نسبية، ويشار إليها بوجود حرف "/" بادئة أو عدم وجوده.

  • المسار المطلق: يختار التعبير "/WebServiceResponse/result" جميع عُقد <result> التي تشكّل عناصر ثانوية للعقدة <WebServiceResponse>. (يُرجى العلم أنّ هذَين العنصرَين ينبعان من العقدة الجذر "/").
  • المسار النسبي من السياق الحالي: قد يتطابق التعبير "result" مع أي عناصر <result> ضمن السياق الحالي. وبشكل عام، لا داعي للقلق بشأن السياق، لأنّه تتم عادةً معالجة نتائج خدمات الويب من خلال تعبير واحد.

يمكن زيادة أي من هذين التعبيرات من خلال إضافة مسار حرف بدل، ويُشار إليه بشرطة مائلة مزدوجة ("//"). يشير حرف البدل هذا إلى أن صفر أو أكثر من العناصر قد تتطابق في المسار المتداخل. على سبيل المثال، سيطابق تعبير XPath "//formatted_address" كل العُقد لهذا الاسم في المستند الحالي. سيتطابق التعبير //viewport//lat مع جميع عناصر <lat> التي يمكن أن تتبع <viewport> كعنصر رئيسي.

بشكل افتراضي، تتطابق تعبيرات XPath مع جميع العناصر. يمكنك تقييد التعبير لمطابقة عنصر معين من خلال توفير predicate، التي يتم تضمينها بين قوسين مربعين ([]). يعرض تعبير XPath "/GeocodeResponse/result[2] دائمًا النتيجة الثانية، على سبيل المثال.

نوع التعبير
Root node
تعبير XPath:  "/"
الاختيار:
    <WebServiceResponse>
     <status>OK</status>
     <result>
      <type>sample</type>
      <name>Sample XML</name>
      <location>
       <lat>37.4217550</lat>
       <lng>-122.0846330</lng>
      </location>
     </result>
     <result>
      <message>The secret message</message>
     </result>
    </WebServiceResponse>
    
المسار المطلق
تعبير XPath:  "/WebServiceResponse/result"
الاختيار:
    <result>
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    </result>
    <result>
     <message>The secret message</message>
    </result>
    
مسار مع حرف بدل
تعبير XPath:  "/WebServiceResponse//location"
الاختيار:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
مسار مع إسناد
تعبير XPath:  "/WebServiceResponse/result[2]/message"
الاختيار:
    <message>The secret message</message>
    
جميع العناصر الثانوية المباشرة لأول result
تعبير XPath:  "/WebServiceResponse/result[1]/*"
الاختيار:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
تمثّل هذه السمة name للسمة result التي يكون نصها type هو "عيّنة".
تعبير XPath:  "/WebServiceResponse/result[type/text()='sample']/name"
الاختيار:
    Sample XML
    

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

تجدر الإشارة إلى أنّ XPath يتوافق مع عقد السمات أيضًا، ولكن جميع خدمات الويب في "خرائط Google" تعرض عناصر بدون سمات، لذلك ليس من الضروري مطابقة السمات.

تحديد النص في التعبيرات

يتم تحديد النص داخل مستند XML في تعبيرات XPath عبر عامل تشغيل عقدة نصية. يشير عامل التشغيل "text()" إلى استخراج النص من العقدة المشار إليها. على سبيل المثال، سيعرض تعبير XPath "//formatted_address/text()" كل النص داخل عناصر <formatted_address>.

نوع التعبير
جميع العقد النصية (بما في ذلك المسافة البيضاء)
تعبير XPath:  "//text()"
الاختيار:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
تحديد النص
تعبير XPath:  "/WebServiceRequest/result[2]/message/text()"
الاختيار:
    The secret message
    
التحديد الحساس للسياق
تعبير XPath:  "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
الاختيار:
    Sample XML
    

بدلاً من ذلك، يمكنك تقييم تعبير وعرض مجموعة من العُقد ثم التكرار عبر "مجموعة العُقد" تلك واستخراج النص من كل عقدة. ونستخدم هذا الأسلوب في المثال أدناه.

لمزيد من المعلومات عن XPath، راجع مواصفات XPath W3C.

تقييم XPath في جافا

توفر Java دعمًا واسعًا لتحليل XML واستخدام تعبيرات XPath ضمن حزمة javax.xml.xpath.*. لهذا السبب، يستخدم الرمز النموذجي في هذا القسم لغة Java لتوضيح كيفية التعامل مع XML وتحليل البيانات من استجابات خدمة XML.

لاستخدام XPath في رمز Java، ستحتاج أولاً إلى إنشاء مثيل من XPathFactory واستدعاء newXPath() في ذلك المصنع لإنشاء كائن XPath . ويمكن لهذا الكائن بعد ذلك معالجة تعبيرات XML وXPath التي تم تمريرها باستخدام طريقة evaluate().

عند تقييم تعبيرات XPath، احرص على التكرار على أي "مجموعات عُقد" محتملة قد يتم عرضها. بما أنّه يتم عرض هذه النتائج كعقد DOM في رمز Java، يجب التقاط هذه القيم المتعددة داخل كائن NodeList وتكرارها لاستخراج أي نص أو قيم من تلك العُقد.

ويوضّح الرمز التالي طريقة إنشاء كائن XPath وتعيينه بتنسيق XML وتعبير XPath، وتقييم التعبير لطباعة المحتوى ذي الصلة.

import org.xml.sax.InputSource;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import java.io.*;

public class SimpleParser {

  public static void main(String[] args) throws IOException {

	XPathFactory factory = XPathFactory.newInstance();

    XPath xpath = factory.newXPath();

    try {
      System.out.print("Web Service Parser 1.0\n");

      // In practice, you'd retrieve your XML via an HTTP request.
      // Here we simply access an existing file.
      File xmlFile = new File("XML_FILE");

      // The xpath evaluator requires the XML be in the format of an InputSource
	  InputSource inputXml = new InputSource(new FileInputStream(xmlFile));

      // Because the evaluator may return multiple entries, we specify that the expression
      // return a NODESET and place the result in a NodeList.
      NodeList nodes = (NodeList) xpath.evaluate("XPATH_EXPRESSION", inputXml, XPathConstants.NODESET);

      // We can then iterate over the NodeList and extract the content via getTextContent().
      // NOTE: this will only return text for element nodes at the returned context.
      for (int i = 0, n = nodes.getLength(); i < n; i++) {
        String nodeString = nodes.item(i).getTextContent();
        System.out.print(nodeString);
        System.out.print("\n");
      }
    } catch (XPathExpressionException ex) {
	  System.out.print("XPath Error");
    } catch (FileNotFoundException ex) {
      System.out.print("File Error");
    }
  }
}