سرویسهای وب پلتفرم نقشههای گوگل مجموعهای از رابطهای HTTP به سرویسهای گوگل هستند که دادههای جغرافیایی را برای برنامههای نقشه شما فراهم میکنند.
این راهنما برخی از شیوههای رایج مفید برای تنظیم درخواستهای وب سرویس شما و پردازش پاسخهای سرویس را شرح میدهد. برای مشاهده مستندات کامل Directions API (Legacy) به راهنمای توسعهدهنده مراجعه کنید.
وب سرویس چیست؟
سرویسهای وب پلتفرم نقشههای گوگل رابطی برای درخواست دادههای API نقشهها از سرویسهای خارجی و استفاده از دادهها در برنامههای نقشه شما هستند. این سرویسها برای استفاده در ارتباط با یک نقشه، مطابق با محدودیتهای مجوز در شرایط خدمات پلتفرم نقشههای گوگل، طراحی شدهاند.
سرویسهای وب Maps API از درخواستهای HTTP(S) به URLهای خاص استفاده میکنند و پارامترهای URL و/یا دادههای POST با فرمت JSON را به عنوان آرگومان به سرویسها ارسال میکنند. عموماً، این سرویسها دادهها را در بدنه پاسخ به صورت JSON یا XML برای تجزیه و/یا پردازش توسط برنامه شما برمیگردانند.
یک درخواست معمول از APIهای Directions (Legacy) معمولاً به شکل زیر است:
https://maps.googleapis.com/maps/api/directions/output?parameters
که در آن output ، فرمت پاسخ (معمولاً json یا xml ) را نشان میدهد.
توجه : همه برنامههای Directions API (Legacy) نیاز به احراز هویت دارند. اطلاعات بیشتری در مورد اعتبارنامههای احراز هویت دریافت کنید.
دسترسی SSL/TLS
HTTPS برای تمام درخواستهای پلتفرم نقشههای گوگل که از کلیدهای API استفاده میکنند یا حاوی دادههای کاربر هستند، الزامی است. درخواستهای ارسالی از طریق HTTP که حاوی دادههای حساس هستند، ممکن است رد شوند.
ساخت یک URL معتبر
ممکن است فکر کنید که یک URL «معتبر» بدیهی است، اما کاملاً اینطور نیست. برای مثال، یک URL که در نوار آدرس مرورگر وارد میشود، ممکن است حاوی کاراکترهای خاص باشد (مثلاً "上海+中國" )؛ مرورگر باید قبل از ارسال، آن کاراکترها را به صورت داخلی به یک کدگذاری متفاوت ترجمه کند. به همین ترتیب، هر کدی که ورودی UTF-8 را تولید یا میپذیرد، ممکن است URLهایی با کاراکترهای UTF-8 را به عنوان "معتبر" در نظر بگیرد، اما قبل از ارسال آنها به یک سرور وب، باید آن کاراکترها را نیز ترجمه کند. این فرآیند کدگذاری URL یا کدگذاری درصد نامیده میشود.
شخصیتهای ویژه
ما نیاز به ترجمه کاراکترهای ویژه داریم زیرا همه URLها باید با سینتکس مشخص شده توسط مشخصات شناسه منبع یکسان (URI) مطابقت داشته باشند. در واقع، این بدان معناست که URLها باید فقط شامل یک زیرمجموعه خاص از کاراکترهای ASCII باشند: نمادهای الفبایی-عددی آشنا و برخی کاراکترهای رزرو شده برای استفاده به عنوان کاراکترهای کنترلی در URLها. این جدول این کاراکترها را خلاصه میکند:
| تنظیم | شخصیتها | استفاده از آدرس اینترنتی (URL) |
|---|---|---|
| الفبایی-عددی | abcdefghijklm nopqrstuvwxyz ABCDEFGHIJKLM NOPQRSTUVWXYZ 0 1 2 3 4 5 6 7 8 9 | رشتههای متنی، استفاده از طرح ( http )، پورت ( 8080 ) و غیره. |
| بدون رزرو | - _ . ~ | رشتههای متنی |
| رزرو شده | ! * '(); : @ & = + $ , / ? % # [ ] | کاراکترهای کنترلی و/یا رشتههای متنی |
هنگام ساخت یک URL معتبر، باید مطمئن شوید که فقط شامل کاراکترهای نشان داده شده در جدول است. تطبیق یک URL با استفاده از این مجموعه کاراکترها عموماً منجر به دو مشکل میشود، یکی حذف و دیگری جایگزینی:
- کاراکترهایی که میخواهید مدیریت کنید، خارج از مجموعه فوق هستند. برای مثال، کاراکترهای زبانهای خارجی مانند
上海+中國باید با استفاده از کاراکترهای فوق کدگذاری شوند. طبق قرارداد رایج، فاصله (که در URLها مجاز نیست) اغلب با استفاده از کاراکتر جمع'+'نیز نمایش داده میشود. - کاراکترهایی در مجموعه فوق به عنوان کاراکترهای رزرو شده وجود دارند، اما باید به صورت تحتاللفظی استفاده شوند. برای مثال،
?در URLها برای نشان دادن ابتدای رشته پرسوجو استفاده میشود؛ اگر میخواهید از رشته "? و Mysterions" استفاده کنید، باید کاراکتر'?'را رمزگذاری کنید.
تمام کاراکترهایی که قرار است رمزگذاری URL شوند، با استفاده از یک کاراکتر '%' و یک مقدار هگز دو کاراکتری مربوط به کاراکتر UTF-8 خود رمزگذاری میشوند. برای مثال،上海+中國در UTF-8 به صورت %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B رمزگذاری URL میشوند. رشته ? and the Mysterians به صورت %3F+and+the+Mysterians یا %3F%20and%20the%20Mysterians رمزگذاری URL میشوند.
کاراکترهای رایجی که نیاز به کدگذاری دارند
برخی از کاراکترهای رایج که باید رمزگذاری شوند عبارتند از:
| شخصیت ناامن | مقدار کدگذاری شده |
|---|---|
| فضا | %20 |
| « | %22 |
| < | %3C |
| > | %3E |
| # | %23 |
| % | %25 |
| | | %7C |
تبدیل URL ای که از ورودی کاربر دریافت میکنید، گاهی اوقات دشوار است. برای مثال، ممکن است کاربر آدرسی به صورت "خیابان پنجم و اصلی" وارد کند. به طور کلی، شما باید URL خود را از بخشهای آن بسازید و هر ورودی کاربر را به عنوان کاراکترهای تحتاللفظی در نظر بگیرید.
علاوه بر این، URL ها برای همه سرویسهای وب پلتفرم نقشههای گوگل و API های وب استاتیک به ۱۶۳۸۴ کاراکتر محدود میشوند. برای اکثر سرویسها، این محدودیت کاراکتر به ندرت رعایت میشود. با این حال، توجه داشته باشید که برخی سرویسها پارامترهای متعددی دارند که ممکن است منجر به URL های طولانی شوند.
استفاده مودبانه از API های گوگل
کلاینتهای API با طراحی ضعیف میتوانند بار بیش از حد لازم را هم بر روی اینترنت و هم بر روی سرورهای گوگل اعمال کنند. این بخش شامل برخی از بهترین شیوهها برای کلاینتهای API است. پیروی از این بهترین شیوهها میتواند به شما کمک کند تا از مسدود شدن برنامه خود به دلیل سوءاستفاده غیرعمدی از APIها جلوگیری کنید.
عقبنشینی نمایی
در موارد نادر، ممکن است در ارائه درخواست شما مشکلی پیش بیاید؛ ممکن است کد پاسخ HTTP با کد 4XX یا 5XX دریافت کنید، یا اتصال TCP بین کلاینت شما و سرور گوگل به سادگی قطع شود. اغلب ارزش دارد که درخواست را دوباره امتحان کنید زیرا ممکن است درخواست بعدی در حالی که درخواست اصلی ناموفق بوده، موفق شود. با این حال، مهم است که درخواستهای مکرر به سرورهای گوگل را به سادگی در حلقه تکرار قرار ندهید. این رفتار حلقهای میتواند شبکه بین کلاینت شما و گوگل را بیش از حد بارگذاری کند و برای بسیاری از طرفین مشکل ایجاد کند.
یک رویکرد بهتر، تلاش مجدد با افزایش تأخیر بین تلاشها است. معمولاً تأخیر با هر تلاش به میزان یک ضریب افزایش مییابد، رویکردی که به عنوان Exponential Backoff شناخته میشود.
برای مثال، برنامهای را در نظر بگیرید که میخواهد این درخواست را به API منطقه زمانی ارسال کند:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEYمثال پایتون زیر نحوهی ارسال درخواست با backoff نمایی را نشان میدهد:
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های گوگل میتوانند مانند حملهی انکار سرویس توزیعشده (DDoS) به زیرساخت گوگل به نظر برسند و بر این اساس با آنها برخورد شود. برای جلوگیری از این امر، باید مطمئن شوید که درخواستهای API بین کلاینتها هماهنگ نمیشوند.
برای مثال، برنامهای را در نظر بگیرید که زمان را در منطقه زمانی فعلی نمایش میدهد. این برنامه احتمالاً در سیستم عامل کلاینت، آلارمی تنظیم میکند که آن را در شروع دقیقه بیدار میکند تا زمان نمایش داده شده بهروزرسانی شود. برنامه نباید هیچ فراخوانی API را به عنوان بخشی از پردازش مرتبط با آن آلارم انجام دهد.
انجام فراخوانیهای API در پاسخ به یک آلارم ثابت، کار بدی است، زیرا منجر به همگامسازی فراخوانیهای API با شروع دقیقه، حتی بین دستگاههای مختلف، به جای توزیع یکنواخت در طول زمان میشود. یک برنامه با طراحی ضعیف که این کار را انجام میدهد، باعث افزایش ترافیک به میزان شصت برابر سطح عادی در شروع هر دقیقه میشود.
در عوض، یک طرح خوب ممکن این است که یک زنگ هشدار دوم روی یک زمان تصادفی انتخاب شده تنظیم شود. وقتی این زنگ هشدار دوم فعال میشود، برنامه هر API مورد نیاز خود را فراخوانی میکند و نتایج را ذخیره میکند. وقتی برنامه میخواهد نمایش خود را در ابتدای دقیقه بهروزرسانی کند، به جای فراخوانی مجدد API، از نتایج ذخیره شده قبلی استفاده میکند. با این رویکرد، فراخوانیهای API به طور مساوی در طول زمان پخش میشوند. علاوه بر این، فراخوانیهای API هنگام بهروزرسانی نمایش، رندر را به تأخیر نمیاندازند.
گذشته از شروع دقیقه، سایر زمانهای همگامسازی رایج که باید مراقب باشید آنها را هدف قرار ندهید ، شروع ساعت و شروع هر روز در نیمهشب هستند.
پردازش پاسخها
این بخش به چگونگی استخراج پویای این مقادیر از پاسخهای وب سرویس میپردازد.
سرویسهای وب نقشههای گوگل پاسخهایی ارائه میدهند که به راحتی قابل فهم هستند، اما دقیقاً کاربرپسند نیستند. هنگام انجام یک پرسوجو، به جای نمایش مجموعهای از دادهها، احتمالاً میخواهید چند مقدار خاص را استخراج کنید. به طور کلی، شما میخواهید پاسخها را از سرویس وب تجزیه کنید و فقط مقادیری را که برای شما جالب هستند استخراج کنید.
طرح تجزیهای که استفاده میکنید بستگی به این دارد که آیا خروجی را به صورت XML یا JSON برمیگردانید. پاسخهای JSON، که از قبل به شکل اشیاء Javascript هستند، ممکن است در خود Javascript در سمت کلاینت پردازش شوند. پاسخهای XML باید با استفاده از یک پردازنده XML و یک زبان پرسوجوی XML برای آدرسدهی عناصر در قالب XML پردازش شوند. ما در مثالهای زیر از XPath استفاده میکنیم، زیرا معمولاً در کتابخانههای پردازش XML پشتیبانی میشود.
پردازش XML با XPath
XML یک فرمت اطلاعاتی ساختاریافته نسبتاً کامل است که برای تبادل دادهها استفاده میشود. اگرچه به اندازه JSON سبک نیست، اما XML پشتیبانی زبانی بیشتر و ابزارهای قویتری را ارائه میدهد. به عنوان مثال، کد پردازش 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 با همه عناصر مطابقت دارند. شما میتوانید با ارائه یک predicate که در داخل براکت ( [] ) قرار میگیرد، عبارت را به مطابقت با یک عنصر خاص محدود کنید. برای مثال، عبارت 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>
|
| مسیر با Wildcard | عبارت 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 متن آن "sample" است. | عبارت XPath: " /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، به مشخصات W3C XPath مراجعه کنید.
ارزیابی XPath در جاوا
جاوا پشتیبانی گستردهای برای تجزیه XML و استفاده از عبارات XPath در بسته javax.xml.xpath.* دارد. به همین دلیل، کد نمونه در این بخش از جاوا برای نشان دادن نحوه مدیریت XML و تجزیه دادهها از پاسخهای سرویس XML استفاده میکند.
برای استفاده از XPath در کد جاوا، ابتدا باید یک نمونه از XPathFactory ایجاد کنید و تابع newXPath() را روی آن factory فراخوانی کنید تا یک شیء 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"); } } }