使用 Roads API Web Services 的最佳做法

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

Google 地圖平台網路服務是 Google 服務的一組 HTTP 介面,可為地圖應用程式提供地理資料。

本指南將說明設定網路服務的 一些常見做法,並提供要求和處理服務回應。如需 Roads API 的完整說明文件,請參閱開發人員指南

什麼是網路服務?

Google 地圖平台網路服務是一個介面,可從外部服務要求 Maps API 資料,以及使用地圖應用程式中的資料。根據《Google 地圖平台服務條款》的授權限制,這些服務的設計是與地圖搭配使用。

Maps API 網路服務會使用 HTTP(S) 要求傳送至特定網址,並將網址參數和/或 JSON 格式 POST 資料傳遞給服務。一般來說,這些服務會將 HTTP(S) 要求中的資料傳回 JSON 格式,以便應用程式進行剖析和/或處理。

一般 Roads API 網路服務要求的格式通常如下:

https://roads.googleapis.com/v1/snapToRoads?parameters&key=YOUR_API_KEY

其中 snapToRoads 代表所要求的特定服務。其他道路服務包括 nearestRoadsspeedLimits

注意:所有 Roads API 應用程式都需要進行驗證。進一步瞭解驗證憑證

安全資料傳輸層 (SSL)/傳輸層安全標準 (TLS) 存取權

使用 API 金鑰或包含使用者資料的所有 Google 地圖平台要求都必須採用 HTTPS。透過 HTTP 傳送含有機密資料的要求可能會遭到拒絕。

建立有效網址

您可能認為網址是否「有效」一眼就能判斷,但實際情況不然。例如,在瀏覽器的網址列內輸入的網址可能包含特殊字元 (例如 "上海+中國");瀏覽器必須在內部將這些字元轉譯為不同的編碼方式才能傳送。同理可證,任何產生或接受 UTF-8 輸入值的程式碼可能會將含有 UTF-8 字元的網址視為「有效網址」,但也需要先轉譯這些字元,才能將其向外傳送至網路伺服器。這個過程稱為網址編碼百分比編碼

特殊字元

我們必須翻譯特殊字元,因為所有網址都必須符合統一資源 ID (URI) 規格指定的語法。事實上,這表示網址必須僅包含一個特殊的 ASCII 字元子集:慣用的英數字元符號,以及用做網址內控制字元的部分預留字元。下表摘要列出這些字元:

有效網址字元摘要
字元集字元網址使用情況
英數字元 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 文字字串、結構用途 (http)、連接埠 (8080) 等。
非預留 - _ 。~ 文字字串
預留 ! * ' ( ) ; : @ & = + $ , / ? % # [ ] 控制字元和 (或) 文字字串

建立有效網址時,您必須確認網址僅包含「有效網址字元摘要」表格中列出的字元。網址如果使用這個字元集,通常會導致遺漏及代換兩個問題:

  • 您希望處理的字元不屬於上述字元集。舉例來說,外國語言的字元 (例如 上海+中國) 就需要使用上述字元加以編碼。依照普遍慣例,空格 (網址內不允許使用) 通常也用加號 '+' 字元來表示。
  • 如果是上述字元集中預留字元的字元,仍需依照字面意思使用。舉例來說,網址內會使用 ? 來表示查詢字串的開頭;如果您想使用「? and the Mysterions」這個字串,就必須對 '?' 字元進行編碼。

所有遵守網址編碼原則的字元都會利用 '%' 字元以及對應至其 UTF-8 字元的雙字元十六進位值編碼。舉例來說,採用 UTF-8 編碼的 上海+中國 改用網址編碼時,會成為 %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B。字串 ? and the Mysterians 會以網址編碼為 %3F+and+the+Mysterians%3F%20and%20the%20Mysterians

需要編碼的常見字元

必須編碼的部分常見字元如下:

不安全的字元 經過編碼的值
空格鍵 %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

轉換您從使用者輸入中取得的網址,有時並不容易處理。舉例來說,使用者輸入的地址可能是「5th&Main St.」。一般來說,您應該根據各組成部分來建立網址,並將任何使用者輸入內容當成常值字元來處理。

此外,所有 Google 地圖平台網路服務和靜態網路 API 的網址長度上限都是 8,192 個字元。對於大部分的服務而言,幾乎很少接近此字元限制的長度。但請注意,某些特定的服務具備幾個可能產生長網址的參數。

精簡使用 Google API

設計不當的 API 用戶端在網際網路和 Google 伺服器上放置的負載可能會超過所需上限。本節列舉 API 用戶端的一些最佳做法。按照這些最佳做法操作,您就能避免應用程式因意外濫用 API 而遭到封鎖。

指數型退讓

在極少數的情況下,你的要求可能會發生錯誤;你可能會收到 4XX 或 5XX HTTP 回應代碼,或者 TCP 連線可能在用戶端與 Google 的伺服器之間失敗。重新嘗試請求通常值得值得,因為後續的要求在成功時可能會成功。不過,請務必避免重複迴圈地向 Google 的伺服器發出要求。這種循環行為可能會導致用戶端和 Google 之間的網路超載,進而造成許多問題。

更理想的做法是重試次數增加。通常,每次嘗試增加延遲時間都會增加一倍,也就是所謂的指數輪詢

例如,假設某個應用程式想向 Time Zone API 發出這項要求:

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

以下 Python 範例顯示如何以指數輪詢方式發出要求:

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 API 傳送的同步要求看起來像是 Google 基礎架構的分散式阻斷服務 (DDoS) 攻擊,並採取相應處置。為避免這種情況,請確保 API 要求在用戶端之間未保持同步。

舉例來說,假設某個應用程式顯示目前時區的時間。此應用程式可能需要在用戶端作業系統喚醒時設定的鬧鐘,以便在一分鐘時喚醒。以便更新顯示的時間。應用程式不得在與此鬧鐘相關的處理過程中發出任何 API 呼叫。

發出 API 呼叫以回應固定的鬧鐘是錯誤的做法,因為這會使 API 呼叫在一分鐘的同步處理時保持同步,即使各裝置之間不同,也不會平均地平均分配。不如設計完善的應用程式流量,在每分鐘開始時達到六十倍的一般流量。

建議的良好做法是將第二個鬧鐘設為隨機選擇的時間。 當第二個鬧鐘觸發時,應用程式會呼叫所需的任何 API 並儲存結果。當應用程式想要在每分鐘就更新顯示時,會使用先前儲存的結果,而不是再次呼叫 API。使用這個方法時,API 呼叫會隨時間平均分配。此外,API 呼叫在顯示更新時不會延遲顯示。

除了分鐘以外,請不要指定其他常見的同步處理時間,例如一小時開始,以及每天午夜開始。

處理回應

本章節討論如何從網路服務回應中,以動態方式擷取這些值。

Google 地圖網路服務提供了容易理解,但不太容易理解的回應。執行查詢時,與其顯示一組資料,您可能需要擷取幾個特定值。通常,您需要剖析網路服務中的回應,並只擷取感興趣的值。

您使用的剖析配置取決於您是否傳回 JSON 中的輸出內容。已採用 JavaScript 物件形式的 JSON 回應,可在用戶端的 JavaScript 本身中處理。