بهترین روش ها در استفاده از خدمات وب API ماتریس فاصله

سرویس‌های وب پلتفرم نقشه‌های Google مجموعه‌ای از رابط‌های HTTP به سرویس‌های Google هستند که داده‌های جغرافیایی را برای برنامه‌های نقشه‌های شما ارائه می‌کنند.

این راهنما برخی از روش‌های رایج مفید برای تنظیم درخواست‌های وب سرویس و پردازش پاسخ‌های سرویس را توضیح می‌دهد. برای مستندات کامل Distance Matrix API به راهنمای توسعه‌دهنده مراجعه کنید.

وب سرویس چیست؟

سرویس‌های وب پلتفرم Google Maps رابطی برای درخواست داده‌های Maps API از سرویس‌های خارجی و استفاده از داده‌های درون برنامه‌های Maps شما هستند. این سرویس‌ها برای استفاده همراه با نقشه طراحی شده‌اند، طبق محدودیت‌های مجوز در شرایط خدمات پلتفرم نقشه‌های Google.

سرویس‌های وب Maps API از درخواست‌های HTTP(S) به آدرس‌های اینترنتی خاص، ارسال پارامترهای URL و/یا داده‌های POST با فرمت JSON به عنوان آرگومان‌هایی برای سرویس‌ها استفاده می‌کنند. به طور کلی، این سرویس ها داده ها را در بدنه پاسخ به صورت JSON یا XML برای تجزیه و/یا پردازش توسط برنامه شما برمی گرداند.

یک درخواست معمولی Distance Matrix API معمولاً به شکل زیر است:

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

که در آن output فرمت پاسخ را نشان می دهد (معمولاً json یا xml ).

توجه : همه برنامه‌های Distance Matrix API نیاز به احراز هویت دارند. اطلاعات بیشتری در مورد اعتبار احراز هویت دریافت کنید.

دسترسی SSL/TLS

HTTPS برای همه درخواست‌های پلتفرم Google Maps که از کلیدهای API استفاده می‌کنند یا حاوی داده‌های کاربر هستند، لازم است. درخواست‌های ارسال شده از طریق HTTP که حاوی داده‌های حساس هستند ممکن است رد شوند.

ساخت یک URL معتبر

ممکن است فکر کنید که URL "معتبر" بدیهی است، اما کاملاً اینطور نیست. برای مثال، URL وارد شده در یک نوار آدرس در مرورگر، ممکن است حاوی کاراکترهای خاصی باشد (به عنوان مثال "上海+中國" ). مرورگر باید آن کاراکترها را به صورت داخلی قبل از انتقال به رمزگذاری دیگری ترجمه کند. به همین ترتیب، هر کدی که ورودی UTF-8 را تولید یا قبول می کند، ممکن است URL های دارای کاراکترهای UTF-8 را به عنوان "معتبر" تلقی کند، اما همچنین باید آن کاراکترها را قبل از ارسال به سرور وب ترجمه کند. این فرآیند کدگذاری URL یا رمزگذاری درصد نامیده می شود.

شخصیت های خاص

ما باید کاراکترهای ویژه را ترجمه کنیم زیرا همه URL ها باید با نحو مشخص شده توسط مشخصات Uniform Resource Identifier (URI) مطابقت داشته باشند. در واقع، این بدان معنی است که URL ها باید فقط شامل یک زیرمجموعه خاص از کاراکترهای ASCII باشند: نمادهای الفبایی عددی آشنا، و برخی از کاراکترهای رزرو شده برای استفاده به عنوان کاراکترهای کنترل در URL. این جدول به طور خلاصه این شخصیت ها را نشان می دهد:

خلاصه کاراکترهای URL معتبر
تنظیم کنید شخصیت ها استفاده از URL
الفبایی abcdefghijklm nopqrstuvwxyz ABCDEFGHIJKLM NOPQRSTUVWXYZ 0 1 2 3 4 5 6 7 8 9 رشته های متنی، استفاده از طرح ( http )، پورت ( 8080 )، و غیره.
بدون رزرو - _ . ~ رشته های متنی
رزرو شده است ! * ' ( ) ; : @ & = + $ , / ? % # [ ] کاراکترها و/یا رشته های متنی را کنترل کنید

هنگام ساختن یک URL معتبر، باید مطمئن شوید که فقط شامل کاراکترهای نشان داده شده در جدول باشد. تطبیق یک URL برای استفاده از این مجموعه از کاراکترها به طور کلی منجر به دو مشکل می شود، یکی حذف و دیگری جایگزینی:

  • کاراکترهایی که می خواهید آنها را مدیریت کنید خارج از مجموعه فوق وجود دارند. برای مثال، کاراکترهای زبان‌های خارجی مانند上海+中國باید با استفاده از کاراکترهای بالا کدگذاری شوند. طبق قرارداد رایج، فضاها (که در URL ها مجاز نیستند) اغلب با استفاده از کاراکتر '+' نیز نمایش داده می شوند.
  • کاراکترها در مجموعه فوق به عنوان کاراکترهای رزرو شده وجود دارند، اما باید به معنای واقعی کلمه استفاده شوند. به عنوان مثال ? در URL ها برای نشان دادن ابتدای رشته پرس و جو استفاده می شود. اگر می خواهید از رشته "? and the Mysterions" استفاده کنید، باید '?' را رمزگذاری کنید. شخصیت

همه کاراکترهایی که باید با URL رمزگذاری شوند با استفاده از یک کاراکتر '%' و یک مقدار هگز دو نویسه مطابق با نویسه UTF-8 کدگذاری می شوند. به عنوان مثال،上海+中國در UTF-8 به صورت %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 که از ورودی کاربر دریافت می کنید گاهی اوقات مشکل است. به عنوان مثال، کاربر ممکن است آدرسی را به عنوان "خیابان پنجم و اصلی" وارد کند. به طور کلی، شما باید URL خود را از قسمت های آن بسازید و هر ورودی کاربر را به عنوان کاراکتر تحت اللفظی در نظر بگیرید.

علاوه بر این، URLها برای همه سرویس‌های وب پلتفرم Google Maps و APIهای وب استاتیک به ۱۶۳۸۴ کاراکتر محدود می‌شوند. برای اکثر سرویس ها، به ندرت به این محدودیت شخصیت نزدیک می شود. با این حال، توجه داشته باشید که برخی از سرویس ها دارای چندین پارامتر هستند که ممکن است منجر به URL های طولانی شود.

استفاده مودبانه از Google API

کلاینت‌های API با طراحی ضعیف می‌توانند بار بیشتری را بر روی اینترنت و سرورهای Google قرار دهند. این بخش شامل برخی از بهترین شیوه ها برای مشتریان API است. پیروی از این بهترین شیوه ها می تواند به شما کمک کند تا از مسدود شدن برنامه خود به دلیل سوء استفاده ناخواسته از API ها جلوگیری کنید.

عقب نشینی نمایی

در موارد نادر ممکن است مشکلی در ارائه درخواست شما پیش بیاید. ممکن است یک کد پاسخ HTTP 4XX یا 5XX دریافت کنید، یا اتصال TCP ممکن است به سادگی در جایی بین کلاینت شما و سرور Google خراب شود. اغلب ارزش امتحان مجدد درخواست را دارد زیرا ممکن است درخواست پیگیری زمانی که درخواست اصلی با شکست مواجه شود موفق شود. با این حال، مهم است که به سادگی درخواست های مکرر را به سرورهای Google حلقه نکنید. این رفتار حلقه‌ای می‌تواند شبکه بین مشتری شما و Google را بیش از حد بارگذاری کند و برای بسیاری از طرف‌ها مشکل ایجاد کند.

یک رویکرد بهتر، تلاش مجدد با افزایش تاخیر بین تلاش ها است. معمولاً تأخیر با یک عامل ضربی با هر تلاش افزایش می‌یابد، رویکردی که به عنوان عقب‌نشینی نمایی شناخته می‌شود.

به عنوان مثال، برنامه‌ای را در نظر بگیرید که می‌خواهد این درخواست را به API منطقه زمانی ارائه کند:

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}")

همچنین باید مراقب باشید که در زنجیره فراخوانی برنامه کدی که به درخواست‌های مکرر پی در پی منجر می‌شود، کدی برای امتحان مجدد وجود نداشته باشد.

درخواست های همگام شده

تعداد زیادی از درخواست‌های همگام‌سازی‌شده به APIهای Google می‌توانند مانند حمله انکار سرویس توزیع‌شده (DDoS) به زیرساخت‌های Google به نظر برسند و مطابق با آن رفتار شود. برای جلوگیری از این امر، باید مطمئن شوید که درخواست‌های API بین کلاینت‌ها همگام‌سازی نمی‌شوند.

به عنوان مثال، برنامه ای را در نظر بگیرید که زمان را در منطقه زمانی فعلی نمایش می دهد. این برنامه احتمالاً زنگ هشداری را در سیستم عامل مشتری تنظیم می کند که آن را در ابتدای دقیقه بیدار می کند تا زمان نمایش داده شده به روز شود. برنامه نباید هیچ تماس API را به عنوان بخشی از پردازش مرتبط با آن زنگ برقرار کند.

برقراری تماس‌های API در پاسخ به زنگ هشدار ثابت بد است، زیرا باعث می‌شود که تماس‌های API با شروع دقیقه، حتی بین دستگاه‌های مختلف، به جای توزیع یکنواخت در طول زمان، همگام‌سازی شوند. یک برنامه با طراحی ضعیف که این کار را انجام می‌دهد، در شروع هر دقیقه یک ترافیک در سطح 60 برابر عادی ایجاد می‌کند.

در عوض، یکی از طرح‌های خوب ممکن این است که زنگ دوم را روی زمان انتخابی تصادفی تنظیم کنید. هنگامی که این زنگ دوم فعال می شود، برنامه هر API مورد نیاز خود را فراخوانی می کند و نتایج را ذخیره می کند. هنگامی که برنامه می خواهد نمایشگر خود را در ابتدای دقیقه به روز کند، به جای فراخوانی مجدد API از نتایج ذخیره شده قبلی استفاده می کند. با این رویکرد، فراخوانی های API در طول زمان به طور مساوی پخش می شوند. علاوه بر این، تماس‌های API هنگام به‌روزرسانی نمایشگر، رندر را به تأخیر نمی‌اندازند.

به غیر از شروع دقیقه، سایر زمان‌های همگام‌سازی معمولی که باید مراقب باشید در شروع یک ساعت و شروع هر روز در نیمه‌شب است.

پردازش پاسخ ها

در این بخش چگونگی استخراج این مقادیر به صورت پویا از پاسخ های سرویس وب بحث می شود.

سرویس‌های وب Google Maps پاسخ‌هایی را ارائه می‌دهند که به راحتی قابل درک هستند، اما دقیقاً کاربر پسند نیستند. هنگام انجام یک پرس و جو، به جای نمایش مجموعه ای از داده ها، احتمالاً می خواهید چند مقدار خاص را استخراج کنید. به طور کلی، شما می خواهید پاسخ ها را از وب سرویس تجزیه کنید و فقط مقادیری را استخراج کنید که مورد علاقه شما هستند.

طرح تجزیه ای که استفاده می کنید بستگی به این دارد که آیا خروجی را در XML یا JSON برمی گردانید. پاسخ‌های JSON، که قبلاً به شکل اشیاء جاوا اسکریپت هستند، ممکن است در خود جاوا اسکریپت روی کلاینت پردازش شوند. پاسخ های XML باید با استفاده از یک پردازنده XML و یک زبان پرس و جو XML برای آدرس دادن به عناصر در قالب XML پردازش شوند. ما از XPath در مثال‌های زیر استفاده می‌کنیم، زیرا معمولاً در کتابخانه‌های پردازش XML پشتیبانی می‌شود.

پردازش XML با XPath

XML یک فرمت اطلاعات ساختار یافته نسبتاً بالغ است که برای تبادل داده ها استفاده می شود. اگرچه XML به سبک JSON نیست، اما پشتیبانی از زبان بیشتر و ابزارهای قوی تری را ارائه می دهد. برای مثال، کد پردازش XML در جاوا در بسته‌های 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 با تمام عناصر مطابقت دارند. شما می‌توانید عبارت را برای مطابقت با یک عنصر خاص با ارائه یک گزاره محدود کنید، که در پرانتز ( [] ) محصور شده است. برای مثال عبارت XPath " /GeocodeResponse/result[2] همیشه نتیجه دوم را برمی گرداند.

نوع بیان
گره ریشه
بیان 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 Expression: " /WebServiceResponse//location "
انتخاب:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
مسیر با محمول
XPath Expression: " /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 Expression: " /WebServiceResponse/result[type/text()='sample']/name "
انتخاب:
    Sample XML
    

توجه به این نکته مهم است که هنگام انتخاب عناصر، گره ها را انتخاب می کنید، نه فقط متن درون آن اشیاء را. به طور کلی، شما می خواهید روی تمام گره های همسان تکرار کنید و متن را استخراج کنید. همچنین می توانید گره های متن را مستقیماً مطابقت دهید. گره های متن را در زیر ببینید.

توجه داشته باشید که XPath از گره های ویژگی نیز پشتیبانی می کند. با این حال، تمام خدمات وب Google Maps عناصر را بدون ویژگی ارائه می دهند، بنابراین تطبیق ویژگی ها ضروری نیست.

انتخاب متن در عبارات

متن درون یک سند 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 در جاوا

جاوا پشتیبانی گسترده ای برای تجزیه XML و استفاده از عبارات XPath در بسته javax.xml.xpath.* دارد. به همین دلیل، کد نمونه در این بخش از جاوا برای نشان دادن نحوه مدیریت XML و تجزیه داده‌ها از پاسخ‌های سرویس XML استفاده می‌کند.

برای استفاده از XPath در کد جاوا، ابتدا باید یک نمونه از یک XPathFactory نمونه‌سازی کنید و newXPath() را در آن کارخانه فراخوانی کنید تا یک شی XPath ایجاد کنید. سپس این شیء می تواند عبارات XML و XPath را با استفاده از متد evaluate() پردازش کند.

هنگام ارزیابی عبارات XPath، مطمئن شوید که روی هر "مجموعه گره" ممکنی که ممکن است برگردانده شود، تکرار کنید. از آنجایی که این نتایج به عنوان گره‌های DOM در کد جاوا برگردانده می‌شوند، باید چنین مقادیر متعددی را در یک شی 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");
    }
  }
}