您可以使用 Google API Discovery Service 构建各种不同的工具,以便与 Google API 搭配使用。不过,发现文档的主要目的是允许 Google 创建采用各种编程语言的客户端库。本文档介绍了如何构建适用于 Google API 的自定义客户端库。
一个稳定且功能齐全的客户端库是一款复杂的工具,开发时间可能长达数月。不过,构建适用于 Google API 的简单客户端库的一般说明可以分解为三个简单步骤:
- 提取发现文档并构建 API 接口
- 编写请求
- 发出调用并获取响应
以下各部分对这些步骤进行了更详细的说明。 您还可以查看“示例”部分中的简单 API 客户端示例,了解这些说明如何映射到代码。
提取发现文档
在开始实现客户端库之前,有一些基本要求会影响您的后续开发路径。例如,您选择的编程语言可以是类型化的,也可以是非类型化的;如果是类型化的,则可以是静态或动态类型化的。它可以是编译的,也可以是解释的。这些要求将指导您使用发现文档。
第一个开发任务是提取发现文档。对于何时获取文档的具体策略取决于您确定的要求。例如,在静态类型语言中,您可以在流程的早期提取发现文档,然后生成代码来处理发现文档中所述的特定 API。对于强类型语言,您可以生成一些代码并构建编译库。对于动态类型语言,您可以延迟构建编程结构,以便在使用编程接口时即时连接到 API。
编写请求
编写请求涉及两个单独的步骤:
- 编写请求正文。
- 构建请求网址。
如果有请求正文,您需要将请求正文从相应语言的表示法转换为正确的传输格式。例如,在 Java 客户端库中,每种请求类型可能都有一个类,该类允许对请求数据进行类型安全的操作,并且可序列化为 JSON。
构建请求网址的过程稍微复杂一些。
API 中每个方法的 path
属性都使用 URI 模板 v04 语法。此属性可能包含变量,这些变量由大括号括起。下面是一个包含变量的 path
属性示例:
/example/path/var
在上面的路径中,var
是一个变量。此变量的值来自该方法的发现文档的 parameters
部分。每个变量名称在 parameters
对象中都有一个对应的值。在上面的示例中,parameters
部分有一个名为 var
的参数(并且其 location
属性为 path
,表示它是路径变量)。
发出请求时,应将 var
的值替换为网址。 例如,如果库的用户选择将 var
设置为值 foo
,则新网址将为 /example/path/foo
。
另请注意,path
属性是一个相对 URI。如需计算绝对 URI,请按以下步骤操作:
-
如果您知道自己的位置(区域),并且发现文档具有
endpoints
属性,请检查您的位置是否存在于endpoints
列表中。如果是,请从endpoints
列表中获取其location
与您的位置匹配的endpointUrl
。 -
如果发现文档中没有
endpoints
属性,或者endpoints
列表中不存在您的位置,或者您想要定位全局端点,请从发现文档的顶层获取rootUrl
属性。例如,Service Usage API 的发现文档中的
rootUrl
属性为:https://serviceusage.googleapis.com/
-
从发现文档的顶层获取
servicePath
。例如,Service Usage API 的发现文档中的servicePath
属性为空。 -
将它们连接在一起即可得到:
https://serviceusage.googleapis.com/
-
获取
path
属性,将其展开为 URI 模板,然后将该扩展的结果与上一步中的 URI 组合起来。例如,在 v1 Service Usage API 的serviceusage.services.enable
方法中,path
属性的值为v1/{+name}:enable
。因此,该方法的完整 URI 为:https://serviceusage.googleapis.com/v1/{+name}:enable
您无需 API 密钥即可调用 Service Usage API。但是,如果您调用的 API 需要 API 密钥,则可以将 API 密钥添加到 URI 的查询字符串中:
REQUEST_URI?key=API_KEY
调用并处理响应
发送请求后,您需要将响应反序列化为相应的语言表示形式,并注意处理可能出现的错误情况 — 无论是在底层 HTTP 传输中发生的错误还是从 API 服务生成的错误消息。Google JSON 风格指南中记录了错误的格式。
示例
以下部分提供了一个 API 客户端库的简单示例。
简单的 API 客户端
下面的示例展示了用 Python3 编写的非常简单的客户端库。该客户端构建一个用于与 Service Usage API 交互的接口,然后使用该界面在项目 my-project
中启用 Compute Engine API (compute.googleapis.com
)。
import httplib2 import json import uritemplate import urllib # Step 1: Fetch Discovery document DISCOVERY_URI = "https://serviceusage.googleapis.com/$discovery/rest?version=v1" h = httplib2.Http() resp, content = h.request(DISCOVERY_URI) discovery = json.loads(content) location = None # Set this to your location if appropriate use_global_endpoint = True # Set this to False if you want to target the endpoint for your location # Step 2.a: Construct base URI BASE_URL = None if not use_global_endpoint and location: if discovery['endpoints']: BASE_URL = next((item['endpointUrl'] for item in discovery['endpoints'] if item['location'] == location), None) if not BASE_URL: BASE_URL = discovery['rootUrl'] BASE_URL += discovery['servicePath'] class Collection(object): pass def createNewMethod(name, method): # Step 2.b Compose request def newMethod(**kwargs): body = kwargs.pop('body', None) url = urllib.parse.urljoin(BASE_URL, uritemplate.expand(method['path'], kwargs)) for pname, pconfig in method.get('parameters', {}).items(): if pconfig['location'] == 'path' and pname in kwargs: del kwargs[pname] if kwargs: url = url + '?' + urllib.parse.urlencode(kwargs) return h.request(url, method=method['httpMethod'], body=body, headers={'content-type': 'application/json'}) return newMethod # Step 3.a: Build client surface def build(discovery, collection): for name, resource in discovery.get('resources', {}).items(): setattr(collection, name, build(resource, Collection())) for name, method in discovery.get('methods', {}).items(): setattr(collection, name, createNewMethod(name, method)) return collection # Step 3.b: Use the client service = build(discovery, Collection()) print (serviceusage.services.enable(name='projects/my-project/services/compute.googleapis.com'))
客户端的关键组件包括:
- 第 1 步:提取发现文档。系统会检索 Service Usage API 的发现文档并将其解析为数据结构。由于 Python 是动态类型化语言,因此发现文档可以在运行时提取。
- 第 2.a 步:构建基本 URI。系统会计算基本 URI。
-
第 2.b 步:编写请求。对集合调用方法时,URI 模板会使用传入到方法中的参数进行展开,并且位置为
query
的参数会被放入网址的查询参数中。最后,使用发现文档中指定的 HTTP 方法向组合后的网址发送请求。 -
第 3.a 步:构建客户端接口。客户端接口根据解析的发现文档以递归方式进行构建。对于
methods
部分中的每个方法,系统都会为Collection
对象附加一个新方法。由于集合可以嵌套,因此我们查找resources
,并以递归方式为其所有找到的成员构建一个Collection
对象。每个嵌套集合还会作为一个属性附加到Collection
对象。 -
第 3.b 步:使用客户端。它演示了如何使用构建的 API 接口。首先,根据 Discovery 文档构建服务对象,然后使用 Service Usage API 在项目
my-project
中启用 Compute Engine API。