Roads API ウェブサービスを使用する際のベスト プラクティス

Google Maps Platform ウェブサービスは、地図アプリケーションの地理データを提供する Google サービスへの HTTP インターフェースのコレクションです。

このガイドでは、ウェブサービス リクエストの設定とサービス レスポンスを処理する際に役立つ一般的な方法について説明します。Roads API の詳細なドキュメントについては、デベロッパー ガイドをご覧ください。

ウェブサービスとは

Google Maps Platform ウェブサービスは、外部サービスから Maps API データをリクエストし、地図アプリケーション内でそのデータを使用するためのインターフェースです。これらのサービスは、Google Maps Platform 利用規約のライセンス制限に従い、地図と組み合わせて使用するように設計されています。

Maps API ウェブサービスは、特定の URL に対する HTTP(S) リクエストを使用して、URL パラメータや JSON 形式の POST データをサービスに引数として渡します。通常、これらのサービスは、アプリケーションによる解析や処理のために、レスポンスの本文でデータを JSON として返します。

一般的な Roads API ウェブサービス リクエストは、通常次の形式になります。

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

ここで、snapToRoads はリクエストされた特定のサービスを示します。その他の道路サービスには、nearestRoadsspeedLimits があります。

: すべての Roads API アプリケーションで認証が必要です。認証情報の詳細を確認する。

SSL/TLS アクセス

API キーを使用するか、ユーザーデータを含むすべての Google Maps Platform リクエストには HTTPS が必要です。HTTP 経由で送信されたリクエストに機密データが含まれている場合、リクエストは拒否される可能性があります。

有効な URL の作成

「有効」な URL とは何か、説明の必要はないと考えられるかもしれませんが、それほど単純なことではありません。ブラウザのアドレスバーに入力される URL には特殊文字("上海+中國" など)が含まれている場合があります。このような特殊文字は、ブラウザで別のエンコードに内部的に変換してから送信する必要があります。同様に、UTF-8 入力を生成または受け付けるコードでは、UTF-8 の文字が使用された URL を「有効」な URL として扱うことがありますが、それらの文字はウェブサーバーに送信する前に変換する必要があります。このプロセスは、URL エンコードまたはパーセント エンコードと呼ばれます。

特殊文字

すべての URL は URI(Uniform Resource Identifier)仕様で規定されている構文に従う必要があるため、特殊文字を変換する必要があります。つまり、URL には、ASCII 文字の特別なサブセット(よく使用される英数記号および URL 内で制御文字として使用される予約文字)のみを含める必要があります。次の表は、こうした特殊記号をまとめたものです。

有効な URL 文字の概要
セット文字URL での使用法
英数字 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)など
未予約 - _ . ~ テキスト文字列
予約済み ! * ' ( ) ; : @ & = + $ , / ? % # [ ] 制御文字やテキスト文字列

有効な URL を作成するときは、表に記載されている文字のみを使用する必要があります。しかし、URL での使用がこの文字セットだけに制限された場合、通常は 2 つの問題が発生します。1 つは省略、もう 1 つは置き換えです。

  • 処理する文字が上記のセットに含まれない場合。たとえば、「上海+中國」のような英語以外の文字は、上記の文字を使用してエンコードする必要があります。一般的な命名規則では、URL 内で使用できないスペースもプラス記号 '+' を使用して表します。
  • 上記のセットに予約文字として含まれる文字を、リテラル文字として使用する必要がある場合。たとえば、「?」は URL 内でクエリ文字列の先頭を示すために使用されます。文字列「? and the Mysterions」を使用する場合は、文字 '?' をエンコードする必要があります。

URL エンコードが必要なすべての文字を、'%' と、UTF-8 文字に対応する 2 文字の 16 進数値を使用してエンコードします。たとえば、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 の変換には、場合によって注意が必要です。たとえば、ユーザーが住所を「5th&Main St.」と入力することも考えられます。通常は、ユーザー入力をリテラル文字として処理して、URL をパーツから作成する必要があります。

さらに、URL は、すべての Google Maps Platform ウェブサービスと Static Web API で 16,384 文字に制限されています。ほとんどのサービスでは、この文字制限に達することはめったにありません。ただし、複数のパラメータを持つ特定のサービスでは、URL が長くなる可能性があります。

Google API の適切な使用

不適切に設計された API クライアントは、インターネットと Google のサーバーの両方に必要以上の負荷をかける可能性があります。このセクションでは、API クライアント向けのおすすめの方法について説明します。これらのベスト プラクティスに従うことで、意図せず API を不適切に使用してアプリケーションがブロックされるのを防止できます。

指数関数的バックオフ

まれに、リクエストの処理で問題が発生する場合があります。4XX または 5XX の HTTP レスポンス コードを受信したり、クライアントと Google のサーバー間の TCP 接続が失敗したりする場合があります。多くの場合、元のリクエストが失敗した場合でも、再試行したリクエストが成功する可能性があるため、リクエストを再試行することをおすすめします。ただし、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 リクエストを同期しないようにする必要があります。

たとえば、現在のタイムゾーンの時刻を表示するアプリケーションを考えます。このアプリケーションでは、表示時刻を更新できるように、クライアントのオペレーティング システムで毎分 0 秒に起動するアラームを設定するものとします。アプリケーションでは、そのアラームに関連する処理の一部として API 呼び出しを行わないようにしてください。

固定のアラームに応答して API 呼び出しを行うと、時間の経過とともに API 呼び出しが均等に分散されるのではなく、異なるデバイス間でも毎分 0 秒に同期されます。これは適切ではありません。設計が不適切なアプリケーションでこのような処理が行われると、毎分 0 秒に通常の 60 倍のレベルでトラフィックが急増します。

対策の 1 つとして、ランダムに選択した時刻に 2 つ目のアラームを設定する方法が考えられます。この 2 つ目のアラームが起動した際にアプリケーションが必要な API を呼び出し、結果を保存します。アプリケーションが毎分 0 秒に表示を更新する場合、API を再度呼び出すのではなく、前回保存した結果を使用します。この方法では API 呼び出しが均等に分散されます。さらに、表示が更新される際に API 呼び出しでレンダリングが遅延することもありません。

毎分 0 秒以外に、同期された処理を行わないように注意する必要がある一般的な時刻は、毎時の開始時点と一日の開始時点(午前 0 時)です。

レスポンスの処理

このセクションでは、これらの値をウェブサービス レスポンスから動的に抽出する方法について説明します。

Google マップのウェブサービスは、理解しやすいレスポンスを提供しますが、ユーザー フレンドリーではありません。クエリを実行するときに、データセットを表示するのではなく、特定の値を抽出することがよくあります。通常は、ウェブサービスからのレスポンスを解析し、必要な値のみを抽出します。

使用する解析スキームは、出力を JSON で返すかどうかによって異なります。JSON レスポンスはすでに JavaScript オブジェクトの形式になっているため、クライアントの JavaScript 内で処理できます。