Google Maps Platform 网络服务是一系列 HTTP 接口,可为您的地图应用提供地理数据。
本指南介绍了一些常见做法,这些做法有助于设置 Web 服务请求和处理服务响应。如需查看 Roads API 的完整文档,请参阅开发者指南。
什么是网络服务?
Google Maps Platform Web 服务是一个接口,用于从外部服务请求地图 API 数据,并在您的地图应用中使用这些数据。根据 Google Maps Platform 服务条款中的许可限制,这些服务应与地图结合使用。
Maps API Web 服务使用 HTTP(S) 请求访问特定网址,并将网址参数和/或 JSON 格式的 POST 数据作为参数传递给服务。通常,这些服务会在响应正文中以 JSON 格式返回数据,供您的应用解析和/或处理。
典型的 Roads API Web 服务请求通常采用以下形式:
https://roads.googleapis.com/v1/snapToRoads?parameters&key=YOUR_API_KEY
其中,snapToRoads 表示请求的特定服务。其他 Roads 服务包括 nearestRoads 和 speedLimits。
注意:所有 Roads API 应用都需要进行身份验证。 详细了解身份验证凭证。
SSL/TLS 访问
对于使用 API 密钥或包含用户数据的所有 Google Maps Platform 请求,必须采用 HTTPS 协议。通过 HTTP 发出的包含敏感数据的请求可能会被拒绝。
构建有效网址
您可能认为“有效”网址不言自明,但实际并非如此。例如,在浏览器地址栏中输入的网址可能包含特殊字符(例如 "上海+中國");浏览器需要先在内部将这些字符转换为其他编码,然后再进行传输。同样,任何生成或接受 UTF-8 输入的代码都可能会将包含 UTF-8 字符的网址视为“有效”,但同样需要先转换这些字符,然后再将其发送给网络服务器。该过程称为网址编码或百分号编码。
特殊字符
我们之所以需要转换特殊字符,是因为所有网址都需要符合统一资源标识符 (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 | 文本字符串、在 scheme 中使用 (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 Maps Platform 网络服务 API 或静态网络 API,网址最多可包含 16384 个字符。对于大多数服务,很少出现接近这一字符数限制的情况。但请注意,某些服务具有的若干参数可能会导致网址较长。
合理使用 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×tamp=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 调用同步到分钟的开始时间(即使在不同设备之间也是如此),而不是在一段时间内均匀分布。如果应用设计不当,在每分钟开始时,流量会激增至正常水平的 60 倍。
相反,一种可能的好设计是将第二个闹钟设置为随机选择的时间。 当第二个闹钟触发时,应用会调用所需的任何 API 并存储结果。当应用想要在分钟开始时更新其显示时,它会使用之前存储的结果,而不是再次调用 API。通过这种方法,API 调用会随着时间的推移均匀分布。此外,在更新显示内容时,API 调用不会延迟渲染。
除了分钟开始时间之外,您还应注意不要将小时开始时间和每天午夜开始时间作为同步时间。
处理响应
此部分介绍如何以动态方式从 Web 服务响应中提取这些值。
Google Maps Web 服务提供的响应易于理解,但对用户来说并不友好。执行查询时,您可能希望提取一些特定值,而不是显示一组数据。通常,您需要解析来自 Web 服务的响应,并仅提取您感兴趣的值。
您使用的解析方案取决于您是否以 JSON 格式返回输出。JSON 响应已经采用 Javascript 对象的形式,因此可以在客户端上通过 Javascript 本身进行处理。