Cloud Endpoints Frameworks 注释和语法

Endpoints Frameworks 注释描述定义端点的属性和行为的 API 配置、方法、参数以及其他重要细节。

如需了解如何使用 Maven 项目添加注释,请参阅编写和注释代码。所提供的 Maven App Engine Cloud Endpoints 工件用于创建和构建后端 API,并从中生成客户端库。

在整个 API 中指定配置和行为的注释(影响 API 中公开的所有类及其所有公开的方法)是 @Api使用 @Api 注释的所有公共、非静态、非桥接的类方法都将在公共 API 中公开。

如果某特定方法需要特殊的 API 配置,您可以选择使用 @ApiMethod 为具体方法设置配置。如下表所示,您可以通过设置不同属性来配置这些注释。

@Api:API 专用注释

@Api 注释可配置整个 API,并应用于类的所有公共方法,除非被 @ApiMethod 替换。

如需在 API 中替换特定类的给定 @Api 注释,请参阅 @ApiClass@ApiReference

必要的导入

如需使用此功能,您需要执行以下导入:

import com.google.api.server.spi.config.Api;

特性

@Api 属性 说明 示例
audiences 如果您的 API 需要身份验证并且您支持 Android 客户端,则该参数是必需的。如需了解详情,请参阅客户端 ID 和目标设备 audiences = {"1-web-apps.apps.googleusercontent.com", "2-web-apps.apps.googleusercontent.com"}
apiKeyRequired 可选。用于将访问权限限制于那些提供了 API 密钥的请求。 apiKeyRequired = AnnotationBoolean.TRUE
authenticators 如果您的 API 使用 Firebase、Auth0 或服务帐号进行身份验证,那么该属性是必需的。如果您的 API 使用 Google ID 令牌进行身份验证,则此特性不是必需的。您可以在 API 级别或具体方法级别设置该特性。您可以将其设置为 {EspAuthenticator.class},也可以按照 Interface Authenticator 中的说明编写自己的自定义身份验证器。 authenticators = {EspAuthenticator.class}
backendRoot 已弃用。如需通过不同的路径提供您的 API,请更改 web.xml 文件中 EndpointsServlet 部分的 url-pattern <url-pattern>/example-api/*</url-pattern>
canonicalName 用于为客户端库中的 API 指定不同或较易理解的名称。该名称用于生成客户端库中的名称,而后端 API 会继续使用 name 属性中指定的值。

例如,如果您的 API 的 name 设置为 dfaanalytics,则您可以使用该属性来指定 DFA Group Analytics 这样一个规范化名称,生成的客户端类就将包含名称 DfaGroupAnalytics

您应该在名称之间包括相关的空格;这些空格将被相应的驼峰式大小写格式或下划线所取代。
canonicalName = "DFA Analytics:"n
clientIds 如果您的 API 使用身份验证,则该特性是必需的。允许请求令牌的客户端的 ID 列表。如需了解详情,请参阅客户端 ID 和目标设备 clientIds = {"1-web-apps.apps.googleusercontent.com", "2-android-apps.apps.googleusercontent.com"}
defaultVersion 指定在 version 特性中未提供任何值的情况下是否使用默认版本。 defaultVersion = AnnotationBoolean.TRUE
description API 的简短说明。该属性在发现服务中公开,用于描述您的 API,您也可以选择使用该属性生成文档。 description = "Sample API for a simple game"
documentationLink 用户可以在其中找到此版本 API 的相关文档的网址。此链接显示在 API Explorer 页面顶部的 API Explorer “了解详情”突出显示部分中,让用户能够了解您的服务。 documentationLink = "http://link_to/docs"
issuers 自定义 JWT 签发者配置。 issuers = { @ApiIssuer(name = "auth0", issuer = "https://test.auth0.com/authorize", jwksUri = "https://test.auth0.com/.well-known/jwks.json") }
issuerAudiences 各个签发者的目标设备。 issuerAudiences = { @ApiIssuerAudience(name = "auth0", audiences = {"aud-1.auth0.com", "aud-2.auth0.com"}) }
limitDefinitions 可选。用于定义 API 的配额。请参阅 @ApiLimitMetric limitDefinitions = { @ApiLimitMetric(name = "read-requests", displayName = "Read requests", limit = 1000)}
name API 的名称,用作所有 API 方法和路径的前缀。name 值:
  • 必须以小写字母开头
  • 必须匹配正则表达式 [a-z]+[A-Za-z0-9]*
如果您未指定 name,则系统会使用默认值 myapi
name = "foosBall"
namespace 为生成的客户端配置命名空间。请参阅 @ApiNamespace namespace=@ApiNamespace(ownerDomain="your-company.com", ownerName="YourCo", packagePath="cloud/platform")
root 已弃用。如需从另一路径提供您的 API,请更改 web.xml 文件的 EndpointsServlet 部分中的 url-pattern <url-pattern>/example-api/*</url-pattern>
scopes 如果未提供值,则默认为电子邮件范围 (https://www.googleapis.com/auth/userinfo.email),OAuth 需要该参数。您可以根据需要替换该值,以指定更多 OAuth 2.0 范围。不过请注意,如果您定义了多个范围,那么为任何指定范围生成令牌,都会让整个范围检查成功通过。如果需要多个范围,则应指定一个 String,并在各个范围之间留有空格。如需替换此处为特定 API 方法指定的范围,请在 @ApiMethod 注释中指定不同的范围。 scopes = {"ss0", "ss1 and_ss2"}
title 作为 API 的标题显示在 API Explorer 中,并在发现服务和目录服务中公开。 title = "My Backend API"
transformers 指定自定义转换器的列表。请注意,有一种更合适的替代注释,即 @ApiTransformer。此特性已被 @ApiTransformer 替换。 transformers = {BazTransformer.class}
version 指定您的端点版本。如果您没有提供该特性,则系统会使用默认值 v1 version = "v2"

示例 @Api 注释

该注释位于类定义之前:

/** An endpoint class we are exposing. */
@Api(name = "myApi",
    version = "v1",
    namespace = @ApiNamespace(ownerDomain = "helloworld.example.com",
        ownerName = "helloworld.example.com",
        packagePath = ""))

客户端 ID 和目标设备

对于 OAuth2 身份验证,系统会向特定客户端 ID 签发 OAuth2 令牌,这意味着您可以使用该客户端 ID 来限制对您 API 的访问。在 Google Cloud Console 中注册 Android 应用时,您可以为其创建客户端 ID。该客户端 ID 会向 Google 请求 OAuth2 令牌以进行身份验证。当后端 API 受身份验证保护时,Endpoints 会发送并打开 OAuth2 访问令牌,从该令牌中提取客户端 ID,然后将该 ID 与后端已声明可接受的客户端 ID 列表(clientIds 列表)进行比较。

如果您希望 Endpoints API 对调用方进行身份验证,则需要提供一份可以请求令牌的 clientIds 列表。此列表应包含您通过控制台为 Web 或 Android 客户端获取的所有客户端 ID。这意味着在构建 API 时必须提供这些客户端。如果您指定空列表 {},则没有客户端可以访问受身份验证保护的方法。

如果您使用 clientIds 特性,并且希望使用 Google API Explorer 来测试经过身份验证的 API 调用,则必须在 clientIds 列表中提供其客户端 ID:要使用的值是 com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID

关于目标设备

clientIds 列表可保护后端 API 免遭未经授权的客户端访问。不过,还需要采取进一步的防护措施来保护客户端,以使其身份验证令牌仅用于预期的后端 API。对于 Android 客户端,该机制是 audiences 特性,您可以在该特性中指定后端 API 的客户端 ID。

请注意,当您创建控制台项目时,系统会自动创建默认的客户端 ID 并为其命名,以供项目使用。当您将后端 API 上传到 App Engine 时,API 会使用该客户端 ID。这是 API 身份验证中提到的 Web 客户端 ID。

@ApiMethod:方法专用注释

注释 @ApiMethod 用于提供与 @Api@ApiClass 注释提供的默认值不同的 API 配置。请注意,该注释是可选的:类中带有 @Api 注释的所有公共、非静态、非桥接方法均会在 API 中公开,无论其是否具有 @ApiMethod 注释。

通过该注释中的特性,您可以配置单个 API 方法的详情。如果在 @Api@ApiMethod 中指定了相同的特性,则将应用 @ApiMethod 中指定的值。

必要的导入

如需使用此功能,您需要执行以下导入:

import com.google.api.server.spi.config.AnnotationBoolean;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiMethod.HttpMethod;

特性

@ApiMethod 属性 说明 示例
apiKeyRequired 可选。用于将访问权限限制于那些提供了 API 密钥的请求。 apiKeyRequired = AnnotationBoolean.TRUE
audiences 如果您希望替换 @API 中的配置,请提供该特性。如需了解详情,请参阅客户端 ID 和目标设备 audiences = {"1-web-apps.apps.googleusercontent.com", "2-web-apps.apps.googleusercontent.com"}
authenticators 如果您的 API 使用 Firebase、Auth0 或服务帐号进行身份验证,并且您没有在 API 级别设置此特性,那么此特性是必需的。如果您的 API 使用 Google ID 令牌进行身份验证,则此特性不是必需的。您可以将其设置为 {EspAuthenticator.class},也可以按照 Interface Authenticator 中的说明编写自己的自定义身份验证器 authenticators = {EspAuthenticator.class}
clientIds 可以请求令牌的客户端 ID 列表。如果您的 API 使用身份验证,则该属性是必需的。 clientIds = {"1-web-apps.apps.googleusercontent.com", "2-android-apps.apps.googleusercontent.com"}
httpMethod 要使用的 HTTP 方法。如果您未设置该特性,则系统会根据方法的名称选择默认值。 httpMethod = HttpMethod.GET
issuerAudiences 如果您希望替换 @Api 中的配置,请提供该特性。 issuerAudiences = { @ApiIssuerAudience(name = "auth0", audiences = {"aud-1.auth0.com", "aud-2.auth0.com"}) }
metricCosts 可选。表示该方法具有配额限制。请将 @ApiMetricCost 注释分配给 metricCosts。您还必须指定 limitDefinitions 特性,以在 @Api 注释中定义配额。@ApiMetricCost 注释采用以下特性:
  • name:您在 ApiLimitMetric 注释中指定的名称。
  • cost:一个整数,用于指定每个请求的耗费。耗费的值让方法按不同的速率消耗同一配额。例如,如果配额上限为 1000 且耗费为 1,那么在超出该上限之前,调用方应用每分钟可发出 1000 个请求。对于相同配额,如果耗费为 2,则在超出上限之前,调用方应用每分钟只能发出 500 个请求。
metricCosts = { @ApiMetricCost(name = read-requests", cost = 1) }
name 该方法在生成的客户端库中的名称。此名称将自动添加您的 API 名称作为前缀,从而为方法指定一个唯一的名称。name 值:
  • 必须以小写字母开头
  • 必须匹配正则表达式 [a-z]+[A-Za-z0-9]*
如果您未指定 name,则系统会使用默认值 myapi
name = "foosBall.list"
path 用于访问此方法的 URI 路径。如果您未设置该特性,则系统会根据 Java 方法名称使用默认路径。如果您计划添加 API 管理,请勿在路径中包含尾部斜杠。 path = "foos"
scopes 指定一个或多个 OAuth 2.0 范围,其中一个范围必须用于调用该方法。如果您为方法设置 scopes,则该设置将替换 @Api 注释中的设置。如果您定义了多个范围,那么为任何指定范围生成令牌,都会让整个范围检查成功通过。如果需要多个范围,则应指定单一 String,并用空格分隔各个范围。 scopes = {"ss0", "ss1 and_ss2"}

示例 @ApiMethod 注释

该注释位于类中的方法定义之前:

/** A simple endpoint method that takes a name and says Hi back. */
@ApiMethod(
    name = "sayHiUser",
    httpMethod = ApiMethod.HttpMethod.GET)
public MyBean sayHiUser(@Named("name") String name, User user)
    throws OAuthRequestException, IOException {
  MyBean response = new MyBean();
  response.setData("Hi, " + name + "(" + user.getEmail() + ")");

  return response;
}

将实体作为参数的方法应使用 HttpMethod.POST(用于插入操作)或 HttpMethod.PUT(用于更新操作):

@ApiMethod(
    name = "mybean.insert",
    path = "mybean",
    httpMethod = ApiMethod.HttpMethod.POST
)
public void insertFoo(MyBean foo) {
}

@Named

对于传递给服务器端方法的所有非实体类型参数,都必须使用 @Named 注释。该注释指定了在此处注入的请求中的参数名称。未使用 @Named 注释的参数将被注入整个请求对象。

必要的导入

如需使用此功能,您需要执行以下导入:

import javax.inject.Named;

该示例演示了如何使用 @Named

/** A simple endpoint method that takes a name and says Hi back. */
@ApiMethod(name = "sayHi")
public MyBean sayHi(@Named("name") String name) {
  MyBean response = new MyBean();
  response.setData("Hi, " + name);

  return response;
}

其中 @Named 指定仅在请求中注入 id 参数。

@ApiLimitMetric

本部分介绍了为 API 定义配额所需的注释。如需查看设置配额所需的所有步骤,请参阅配置配额

请将 @ApiLimitMetric 注释分配给 API 专用注释limitDefinitions 特性。对于要应用配额的每个方法,您还必须将 @ApiMetricCost 添加到 @ApiMethod 注释

必要的导入

如需使用此功能,您需要执行以下导入:

import com.google.api.server.spi.config.ApiLimitMetric;

特性

@ApiLimitMetric 特性

说明
name 配额的名称。通常,这是对配额进行唯一标识的请求类型(例如“read-requests”或“write-requests”)。
displayName 显示的文本用于标识控制台的 Endpoint > Services 页面上 Quota 标签页上的配额。您的 API 使用方还会在“IAM 和管理”以及“API 和服务”的配额页面上看到此文本。显示名不得超过 40 个字符。
为确保可读性,文本“per minute per project”会自动附加到配额页面上的显示名末尾。
为了与 API 使用方在配额页面上看到的 Google 服务的显示名保持一致,我们针对显示名提出以下建议:
  • 只有一个指标时,请使用“Requests”。
  • 如果您有多个指标,则每个指标都应描述请求的类型并包含“requests”一词(例如“Read requests”或“Write requests”)。
  • 当该配额的任何耗费大于 1 时,请使用“quota units”,而不是“requests”。
limit 一个整数值,是指每个使用方项目每分钟针对配额发出的请求次数上限。

示例

limitDefinitions = {
      @ApiLimitMetric(
        name = "read-requests",
        displayName = "Read requests",
        limit = 1000),
      @ApiLimitMetric(
        name = "write-requests",
        displayName = "Write requests",
        limit = 50),
    }

@ApiNamespace

通过 @ApiNamespace 注释,生成的客户端库可具备您指定的命名空间,而不是在生成客户端库期间构造的默认命名空间。

默认情况下,如果您不使用该注释,则所用的命名空间是 your-project-id.appspot.com 的反转值。也就是说,软件包路径为 com.appspot.your-project-id.yourApi

您可以在 @Api 注释中提供 @ApiNamespace 注释,由此更改默认命名空间:

/** An endpoint class we are exposing. */
@Api(name = "myApi",
    version = "v1",
    namespace = @ApiNamespace(ownerDomain = "helloworld.example.com",
        ownerName = "helloworld.example.com",
        packagePath = ""))

请将 ownerDomain 特性设置为您的公司网域,并将 ownerName 设置为您的公司名称,例如 your-company.com。将 ownerDomain 的反转值用于软件包路径:com.your-company.yourApi

您可以选择使用 packagePath 特性进一步限定范围。例如,通过将 packagePath 设置为 cloud,客户端库中使用的软件包路径为 com.your-company.cloud.yourApi。您可以向软件包路径添加更多值,方法是添加分隔符 /packagePath="cloud/platform"

@Nullable

该注释表示方法的参数为可选参数(因此是查询参数)。@Nullable 只能与 @Named 参数搭配使用。

@ApiClass

多类 API 中,您可以使用 @ApiClass 为给定类指定不同的属性,并替换 @Api 配置中的等效属性。如需查看该注释的完整说明,请参阅使用 @ApiClass 指定因类而异的属性

@ApiReference

多类 API 中,您可以使用 @ApiReference 提供备用的注释继承方法。如需查看该注释的完整说明,请参阅使用 @ApiReference 继承

@ApiResourceProperty

@ApiResourceProperty 可用于控制 API 中资源属性的公开方式。您可以对属性 getter 或 setter 使用该注释,以忽略 API 资源中的该属性。如果字段不公开,您也可以对字段本身使用该注释,以在 API 中公开字段。您还可以使用该注释更改 API 资源中的属性名称。

必要的导入

如需使用此功能,您需要执行以下导入:

import com.google.api.server.spi.config.ApiResourceProperty;
import com.google.api.server.spi.config.AnnotationBoolean;

属性

@ApiResourceProperty 属性 说明 示例
ignored 如果此特性设置为 AnnotationBoolean.TRUE,则系统会忽略相应属性。如果此特性未指定或设置为 AnnotationBoolean.FALSE,则系统不会忽略相应属性。 @ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
name 如若提供,则此特性会指定将在 API 中公开的属性名称。 @ApiResourceProperty(name = "baz")

含有 @ApiResourceProperty 的类示例

以下代码段显示了 getter 属性带有 @ApiResourceProperty 注释的类:


class Resp {
  private String foobar = "foobar";
  private String bin = "bin";

  @ApiResourceProperty
  private String visible = "nothidden";

  @ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
  public String getBin() {
    return bin;
  }

  public void setBin(String bin) {
    this.bin = bin;
  }

  @ApiResourceProperty(name = "baz")
  public String getFoobar() {
    return foobar;
  }

  public void setFoobar(String foobar) {
    this.foobar = foobar;
  }
}

public Resp getResp() {
  return new Resp();
}

在前面的代码段中,@ApiResourceProperty 应用于 bin 属性的 getBin getter,并且 ignored 特性设置告诉 Endpoints Frameworks 在 API 资源中忽略此属性。

@ApiResourceProperty 还被应用于不公开字段 visible,其中没有 getter 或 setter。使用此注释后,该字段会作为属性在 API 资源中公开。

在同一代码段中,@ApiResourceProperty 也被应用于其他 getter,即 getFoobar,这将为 foobar 属性返回一个属性值。该注释中的 name 特性告诉 Endpoints Frameworks 更改 API 资源中的属性名称。属性值本身保持不变。

在前面的示例代码段中,Resp 对象的 JSON 表示法类似于以下形式:

{"baz": "foobar", "visible": "nothidden"}

@ApiTransformer

@ApiTransformer 注释可通过在不同的类型之间进行转换来自定义各类型在 Endpoints 中的公开方式。(指定的转换器必须是 com.google.api.server.spi.config.Transformer 的实现。)

对类使用 @ApiTransformer 注释是指定转换器的首选方式。不过,您也可以在 @Api 注释的 transformer 特性中指定自定义转换器。

必要的导入

如需使用此功能,您需要执行以下导入:

import com.google.api.server.spi.config.ApiTransformer;

带有 @ApiTransformer 的类示例

以下代码段显示了一个使用 @ApiTransformer 注释的类:


@ApiTransformer(BarTransformer.class)
public class Bar {
  private final int x;
  private final int y;

  public Bar(int x, int y) {
    this.x = x;
    this.y = y;
  }

  public int getX() {
    return x;
  }

  public int getY() {
    return y;
  }
}

该类通过 BarTransformer 类转换。

Endpoints 转换器类示例

以下代码段展示了一个名为 BarTransformer 的转换器类示例。这是上一个代码段中 @ApiTransformer 引用的转换器:

public class BarTransformer implements Transformer<Bar, String> {
  public String transformTo(Bar in) {
    return in.getX() + "," + in.getY();
  }

  public Bar transformFrom(String in) {
    String[] xy = in.split(",");
    return new Bar(Integer.parseInt(xy[0]), Integer.parseInt(xy[1]));
  }
}

假设某对象有一个 Bar 类型的 bar 属性,如果没有使用上述转换器,则该对象将表示为:

{"bar": {"x": 1, "y": 2}}

如果使用转换器,该对象将表示为:

{"bar": "1,2"}