构建客户端库

您可以使用 Google API Discovery Service 构建各种不同的工具,以便与 Google API 搭配使用。不过,发现文档的主要目的是允许 Google 创建采用各种编程语言的客户端库。本文档介绍了如何构建适用于 Google API 的自定义客户端库。

一个稳定且功能齐全的客户端库是一款复杂的工具,开发时间可能长达数月。不过,构建适用于 Google API 的简单客户端库的一般说明可以分解为三个简单步骤:

  1. 提取发现文档并构建 API 接口
  2. 编写请求
  3. 发出调用并获取响应

以下各部分对这些步骤进行了更详细的说明。 您还可以查看“示例”部分中的简单 API 客户端示例,了解这些说明如何映射到代码。

提取发现文档

在开始实现客户端库之前,有一些基本要求会影响您的后续开发路径。例如,您选择的编程语言可以是类型化的,也可以是非类型化的;如果是类型化的,则可以是静态或动态类型化的。它可以是编译的,也可以是解释的。这些要求将指导您使用发现文档。

第一个开发任务是提取发现文档。对于何时获取文档的具体策略取决于您确定的要求。例如,在静态类型语言中,您可以在流程的早期提取发现文档,然后生成代码来处理发现文档中所述的特定 API。对于强类型语言,您可以生成一些代码并构建编译库。对于动态类型语言,您可以延迟构建编程结构,以便在使用编程接口时即时连接到 API。

编写请求

编写请求涉及两个单独的步骤:

  1. 编写请求正文。
  2. 构建请求网址。

如果有请求正文,您需要将请求正文从相应语言的表示法转换为正确的传输格式。例如,在 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,请按以下步骤操作:

  1. 如果您知道自己的位置(区域),并且发现文档具有 endpoints 属性,请检查您的位置是否存在于 endpoints 列表中。如果是,请从 endpoints 列表中获取其 location 与您的位置匹配的 endpointUrl
  2. 如果发现文档中没有 endpoints 属性,或者 endpoints 列表中不存在您的位置,或者您想要定位全局端点,请从发现文档的顶层获取 rootUrl 属性。

    例如,Service Usage API发现文档中的 rootUrl 属性为:

    https://serviceusage.googleapis.com/
  3. 从发现文档的顶层获取 servicePath。例如,Service Usage API 的发现文档中的 servicePath 属性为空。
  4. 将它们连接在一起即可得到:

    https://serviceusage.googleapis.com/
  5. 获取 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。