AMP

amp-access

描述

提供 AMP 付费墙和订阅支持。

 

必需的脚本

<script async custom-element="amp-access" src="https://cdn.ampproject.org/v0/amp-access-0.1.js"></script>

AMP Access 或“AMP 付费墙和订阅支持”使发布商可以控制读者可以访问哪些内容以及基于读者的订阅状态、查看次数和其他因素的限制。

与 amp-subscriptions 的关系

amp-subscriptions 扩展提供了与 amp-access 类似的功能。但是,它支持更专业的访问付费墙协议。一些值得注意的区别是

  1. amp-subscriptions 权利响应类似于 amp-access 授权,但它是严格定义和标准化的。
  2. amp-subscriptions 扩展允许为页面配置多个服务,以参与访问/付费墙决策。它们会并发执行,并根据哪个服务返回肯定响应来确定优先级。
  3. 允许 AMP 查看器根据与发布商的独立协议,为 amp-subscriptions 提供已签名的授权响应,作为访问凭证。
  4. amp-subscriptions 中,内容标记是标准化的,允许应用程序和爬网程序轻松检测到高级内容部分。

由于标记的标准化、多提供程序支持和改进的查看器支持,建议新的发布商和付费墙提供程序实现使用 amp-subscriptions

解决方案

提议的解决方案使发布商可以控制以下决策和流程

  • 创建和维护用户
  • 计量控制(允许一定数量的免费查看)
  • 负责登录流程
  • 负责验证用户身份
  • 负责访问规则和授权
  • 在每个文档的基础上灵活设置访问参数

该解决方案包含以下组件

  1. AMP 阅读器 ID:由 AMP 生态系统提供,这是 AMP 所见的读者的唯一标识符。
  2. 访问内容标记:由发布商编写,定义文档的哪些部分在哪些情况下可见。
  3. 授权端点:由发布商提供,返回解释读者可以消费文档的哪一部分的响应。
  4. 回传端点:由发布商提供,用于发送文档的“查看”展示。
  5. 登录链接和登录页面:允许发布商验证读者的身份,并将其身份与 AMP 阅读器 ID 相关联。

Google AMP 缓存将文档返回给读者,其中一些部分使用访问内容标记模糊处理。AMP 运行时调用授权端点,并使用响应来隐藏或显示访问内容标记定义的不同部分。在文档显示给读者后,AMP 运行时调用回传端点,发布商可以使用该端点更新倒数计数器(已使用的免费查看次数)。

该解决方案还允许发布商在 AMP 文档中放置一个登录链接,该链接会启动登录/订阅页面,发布商可以在该页面中验证读者的身份,并在其系统中将读者的身份与 AMP 阅读器 ID 相关联。

在其基本形式中,此解决方案将完整的(尽管是模糊的)文档发送给读者,并根据授权响应简单地显示/隐藏受限制的部分。但是,该解决方案还提供了“服务器”选项,其中受限制的部分可以从初始文档交付中排除,并且仅在确认授权后才下载。

支持 AMP Access 需要发布商实现上述组件。需要访问内容标记和授权端点。回传端点和登录页面是可选的。

AMP 阅读器 ID

为了帮助访问服务和用例,AMP Access 引入了阅读器 ID 的概念。

阅读器 ID 是由 AMP 生态系统创建的匿名唯一 ID。对于每个读者/发布商对,它是唯一的 - 对于两个不同的发布商,读者的身份是不同的。这是一个不可逆的 ID。阅读器 ID 包含在所有 AMP/发布商通信中,并具有很高的熵。发布商可以使用阅读器 ID 来识别读者,并将其映射到他们自己的身份系统。

阅读器 ID 在用户设备上构建,旨在长期存在。但是,它遵循正常的浏览器存储规则,包括针对隐身窗口的规则。阅读器 ID 的预期生命周期是两次使用之间 1 年,或者直到用户清除其 Cookie。阅读器 ID 目前不在设备之间共享。

阅读器 ID 的构造方式与构建此处 描述 的 ExternalCID 的机制类似。一个示例阅读器 ID 是 amp-OFsqR4pPKynymPyMmplPNMvxSTsNQob3TnK-oE3nwVT0clORaZ1rkeEz8xej-vV6

AMP Access 和 Cookie

发布商可以使用他们自己的身份验证 Cookie,或者他们可以依赖阅读器 ID,或者两者结合使用。

访问内容标记

访问内容标记根据从授权端点返回的授权响应确定哪些部分可见或隐藏。它通过特殊的标记属性进行描述。

授权端点

授权是发布商提供的,由 AMP 运行时或 Google AMP 缓存调用的端点。它是一个凭据 CORS GET 端点。此端点返回可由内容标记用来隐藏或显示文档不同部分的访问参数。

回传端点

回传是发布商提供的,由 AMP 运行时或 Google AMP 缓存调用的端点。它是一个凭据 CORS POST 端点。当读者开始查看文档时,AMP 运行时会自动调用此端点。在读者成功完成登录流程后,也会调用此端点。回传的主要目标之一是让发布商更新计量信息。

回传是可选的。可以通过将 noPingback 配置属性设置为 true 来禁用它。

登录页面由发布商实现和提供,并由 AMP 运行时调用。它通常显示为浏览器对话框。

当读者点击可以由发布商放置在文档中任何位置的登录链接时,会触发登录页面。

规范 v0.1

配置

所有端点都在 AMP 文档中配置为文档 HEAD 中的 JSON 对象

<script id="amp-access" type="application/json">
  {
    "property": value,
    ...
  }
</script>

此配置中定义了以下属性

属性 描述
授权 <URL> 授权端点的 HTTPS URL。
回传 <URL> 回传端点的 HTTPS URL。
noPingback true/false 为 true 时,禁用回传。
登录 <URL> 或
<Map[字符串, URL]>
登录页面的 HTTPS URL 或不同类型的登录页面的 URL 集。
authorizationFallbackResponse <对象> 如果授权失败,则用作授权响应的 JSON 对象。
authorizationTimeout <数字> 授权请求被视为失败后的超时时间(以毫秒为单位)。默认值为 3000。仅在开发环境中允许大于 3000 的值。
类型 “client”或“server” 默认值为“client”。“server”选项正在设计讨论中,这些文档将在准备就绪后更新。
命名空间 字符串 默认值为空。如果指定了多个访问提供程序,则需要命名空间。

<URL> 值指定带有替换变量的 HTTPS URL。替换变量在下面的访问 URL 变量部分中会更详细地介绍。

以下是 AMP Access 配置的示例

<script id="amp-access" type="application/json">
  {
    "authorization": "https://pub.com/amp-access?rid=READER_ID&url=SOURCE_URL",
    "pingback": "https://pub.com/amp-ping?rid=READER_ID&url=SOURCE_URL",
    "login": "https://pub.com/amp-login?rid=READER_ID&url=SOURCE_URL",
    "authorizationFallbackResponse": {"error": true}
  }
</script>

多个访问提供程序

可以使用数组而不是单个对象,并为每个条目提供一个 namespace,从而指定多个访问提供程序。

<script id="amp-access" type="application/json">
  [
    {
      "property": value,
      ...
      "namespace": value
    },
    ...
  ]
</script>

访问 URL 变量

在为各种端点配置 URL 时,发布者可以使用替换变量。这些变量的完整列表在 AMP Var Spec 中定义。此外,本规范还添加了一些特定于访问的变量,例如 READER_IDAUTHDATA。下表描述了一些最相关的变量

变量 描述
READER_ID AMP 阅读器 ID。
AUTHDATA(字段) 授权响应中字段的值。
RETURN_URL AMP 运行时为登录对话框指定的返回 URL 的占位符。
SOURCE_URL 此 AMP 文档的源 URL。如果文档是从 CDN 提供的,则 AMPDOC_URL 将是 CDN URL,而 SOURCE_URL 将是原始源 URL。
AMPDOC_URL 此 AMP 文档的 URL。
CANONICAL_URL 此 AMP 文档的规范 URL。
DOCUMENT_REFERRER 引用 URL。
VIEWER AMP 查看器的 URL。
RANDOM 一个随机数。有助于避免浏览器缓存。

以下是一个使用阅读器 ID、规范 URL、引用信息和随机缓存破坏器扩展 URL 的示例

https://pub.com/access?
   rid=READER_ID
  &url=CANONICAL_URL
  &ref=DOCUMENT_REFERRER
  &_=RANDOM

AUTHDATA 变量可用于 Pingback 和登录 URL。它允许将授权响应中的任何字段作为 URL 参数传递。例如,AUTHDATA(isSubscriber)。也允许使用嵌套表达式,例如 AUTHDATA(other.isSubscriber)。如果使用命名空间,可以将命名空间添加到字段,例如 AUTHDATA(anamespace.afield)

访问内容标记

访问内容标记描述了哪些部分可见或隐藏。它由两个 AMP 属性组成:可以放置在任何 HTML 元素上的 amp-accessamp-access-hide

amp-access 属性提供了一个表达式,该表达式基于授权端点返回的授权响应产生 true 或 false。结果值指示元素及其内容是否可见。

amp-access 值是一个以类似 SQL 的语言定义的布尔表达式。语法在附录 A中定义。它的定义如下

<div amp-access="expression">...</div>

属性和值是指授权端点返回的授权响应的属性和值。这提供了一个灵活的系统来支持不同的访问场景。如果使用命名空间,只需将命名空间添加到属性名称,例如 anamespace.aproperty

amp-access-hide 属性可用于在接收到授权响应之前以乐观的方式隐藏元素,之后可以显示该元素。它提供了“默认不可见”的语义。授权稍后返回的授权响应可能会撤销此默认设置并使该部分可见。当省略 amp-access-hide 属性时,默认情况下将显示/包含该部分。amp-access-hide 属性只能与 amp-access 属性一起使用。

<div amp-access="expression" amp-access-hide>...</div>

如果授权请求失败,则不会评估 amp-access 表达式,并且某个部分是可见还是隐藏取决于文档最初提供的 amp-access-hide 属性是否存在。

我们可以根据需要扩展 amp-access-* 属性集,以支持不同的混淆和渲染需求。

如果授权请求失败并且文档中未指定“authorizationFallbackResponse”响应,则不会评估 amp-access 表达式,并且某个部分是可见还是隐藏将取决于文档最初提供的 amp-access-hide 属性是否存在。

以下示例根据订阅状态显示登录链接或完整内容

<header>
  Title of the document
</header>

<div>
  First snippet in the document.
</div>

<div amp-access="NOT subscriber" amp-access-hide>
  <a on="tap:amp-access.login">Become a subscriber now!</a>
</div>

<div amp-access="subscriber">
  Full content.
</div>

这里

  • subscriber 是授权端点返回的授权响应中的布尔字段。默认情况下会隐藏此部分,这是可选的。
  • 此示例选择乐观地显示全部内容。

以下是另一个向读者显示关于计量状态的免责声明的示例

<section amp-access="views <= maxViews">
  <template amp-access-template type="amp-mustache">
    You are reading article {{views}} out of {{maxViews}}.
  </template>
</section>

以下是一个向高级订阅者显示额外内容的示例

<section amp-access="subscriptonType = 'premium'">
  Shhh… No one but you can read this content.
</section>

授权端点

授权是通过AMP Access 配置部分中的 authorization 属性配置的。它是一个凭据式 CORS GET 端点。有关如何保护此请求的安全的信息,请参阅CORS 来源安全

授权可以接受访问 URL 变量部分中定义的任何参数。例如,它可以传递 AMP 阅读器 ID 和文档 URL。必须包含 READER_ID

此端点生成授权响应,该响应可用于内容标记表达式中,以显示/隐藏内容的不同部分。

请求格式为

https://publisher.com/amp-access.json?
   rid=READER_ID
  &url=SOURCE_URL

响应是一个自由格式的 JSON 对象:它可以包含任何属性和值,但有一些限制。限制是

  • 属性名称必须符合 amp-access 表达式语法定义的限制(请参阅附录 A)。这主要意味着属性名称不能包含空格、破折号和其他不符合“amp-access”规范的字符。
  • 属性值只能是以下类型之一:字符串、数字、布尔值。
  • 值也可以嵌套为具有相同类型值(字符串、数字、布尔值)的对象。
  • 序列化授权响应的总大小不能超过 500 字节。
  • 请确保响应不包含任何个人身份信息 (PII) 或个人数据。

以下是从授权端点返回的属性的可能想法的简短列表

  • 计量信息:允许的最大浏览次数和当前的浏览次数。
  • 读者是否已登录或是否为订阅者。
  • 更详细的订阅类型:基本、高级
  • 地理位置:国家、地区、自定义出版地区

以下是读者不是订阅者且每月计量为 10 篇文章且已浏览 6 篇文章时的响应示例

{
  "maxViews": 10,
  "currentViews": 6,
  "subscriber": false
}

以下是读者已登录并具有高级订阅类型时的响应示例

{
  "loggedIn": true,
  "subscriptionType": "premium"
}

此 RPC 可能会在预渲染阶段被调用,因此不应将其用于计量倒计时,因为读者可能永远不会真正看到该文档。

另一个重要的考虑因素是,在某些情况下,AMP 运行时可能需要每个文档展示多次调用授权端点。当 AMP 运行时认为读者的访问参数已发生显著变化时,可能会发生这种情况,例如在成功登录流程之后。

AMP 运行时和扩展可以使用授权响应来实现三种不同的目的

  1. 评估 amp-access 表达式时。
  2. 评估 <template> 模板时,例如 amp-mustache
  3. 使用 AUTHDATA(field) 向 pingback 和登录 URL 提供其他变量时。

AMP 运行时将授权端点作为凭据式 CORS 端点调用。因此,它必须实现 CORS 协议。它应使用 CORS 来源和源来源来限制对此服务的访问,如CORS 来源安全中所述。此端点可以使用发布者 Cookie 来满足其需求。例如,它可以关联阅读器 ID 与发布者自己的用户身份之间的绑定。AMP 本身不需要了解这一点(并且最好不了解)。有关详细信息,请参阅AMP 阅读器 IDAMP Access 和 Cookie 文档。

AMP 运行时(或者更确切地说,浏览器)在调用授权端点时会观察缓存响应标头。因此,可以重用缓存的响应。这可能是可取的,也可能不是可取的。如果不可取,发布者可以使用适当的缓存控制标头和/或端点 URL 的 RANDOM 变量替换。

如果授权请求失败,则 AMP 运行时将回退到“authorizationFallbackResponse”(如果在配置中指定)。在这种情况下,授权流程将以“authorizationFallbackResponse”属性值代替授权响应的值继续进行。如果未指定“authorizationFallbackResponse”,则授权流程将失败,在这种情况下,将不会评估 amp-access 表达式,并且某个部分是否可见或隐藏将取决于文档最初提供的 amp-access-hide 属性是否存在。

授权请求会自动超时,并在 3 秒后被假定为失败。

AMP 运行时在授权流程期间使用以下 CSS 类

  1. 当授权流程开始时,amp-access-loading CSS 类设置在文档根目录上,并在流程完成或失败时移除。
  2. 当授权流程失败时,amp-access-error CSS 类设置在文档根目录上。

server 选项中,对授权端点的调用由 Google AMP 缓存作为简单的 HTTPS 端点进行。这意味着在这种情况下,无法传递发布者的 Cookie。

回传端点

Pingback 是通过AMP Access 配置部分中的 pingback 属性配置的。它是一个凭据式 CORS POST 端点。有关如何保护此请求的安全的信息,请参阅CORS 来源安全

Pingback URL 是可选的。可以使用 "noPingback": true 将其禁用。

Pingback URL 可以接受访问 URL 变量部分中定义的任何参数。例如,它可以传递 AMP 阅读器 ID 和文档 URL。必须包含 READER_ID

Pingback 不会产生响应 - AMP 运行时会忽略任何响应。

当读者开始查看文档并且读者成功完成登录流程后,会调用 Pingback 端点。

发布者可以选择使用 pingback

  • 来倒数页面的免费浏览次数
  • 将 AMP 阅读器 ID 映射到发布者的身份,因为作为凭据式 CORS 端点,Pingback 可能包含发布者的 Cookie

请求格式为

https://publisher.com/amp-pingback?
   rid=READER_ID
  &url=SOURCE_URL

登录页面

登录页面的 URL 是通过AMP Access 配置部分中的 login 属性配置的。

配置可以指定单个登录 URL,也可以指定按登录类型键控的登录 URL 映射。以下是单个登录 URL 的示例

{
  "login": "https://publisher.com/amp-login.html?rid={READER_ID}"
}

以下是多个登录 URL 的示例

{
  "login": {
    "signin": "https://publisher.com/signin.html?rid={READER_ID}",
    "signup": "https://publisher.com/signup.html?rid={READER_ID}"
  }
}

URL 可以接受 访问 URL 变量 部分中定义的任何参数。例如,它可以传递 AMP 阅读器 ID 和文档 URL。RETURN_URL 查询替换可用于指定返回 URL 的查询参数,例如 ?ret=RETURN_URL。返回 URL 是必需的,如果未指定 RETURN_URL 替换,则将自动注入默认查询参数名称“return”。

登录页面只是一个普通的网页,没有特殊的约束,除了它应该能很好地作为浏览器对话框运行。有关更多详细信息,请参阅登录流程部分。

请求格式为

https://publisher.com/amp-login.html?
   rid=READER_ID
  &url=SOURCE_URL
  &return=RETURN_URL

请注意,如果未指定 RETURN_URL 替换,则 AMP 运行时会自动添加“return”URL 参数。登录页面完成工作后,必须以以下格式重定向回指定的“返回 URL”

RETURN_URL#success=true|false

请注意 URL 哈希参数“success”的使用。该值是“true”或“false”,具体取决于登录是成功还是放弃。理想情况下,登录页面在可能的情况下会在成功或失败的情况下发送信号。

如果返回 success=true 信号,AMP 运行时将重复调用授权和回传端点以更新文档的状态,并使用新的访问配置文件报告“视图”。

发布者可以选择将登录链接放置在文档内容的任何位置。

一个或多个登录 URL 通过 AMP 访问配置 部分中的“login”属性进行配置。

登录链接可以在任何允许“on”属性的 HTML 元素上声明。通常这将是一个锚点或按钮元素。配置单个登录 URL 时,格式如下

<a on="tap:amp-access.login">Login or subscribe</a>

配置多个登录 URL 时,格式为 tap:amp-access.login-{type}。示例

<a on="tap:amp-access.login-signup">Subscribe</a>

当使用命名空间时,格式为 tap:amp-access.login-{namespace}tap:amp-access.login-{namespace}-{type}

AMP 不区分登录和订阅。这种区别可以由发布者使用多个登录 URL/链接或在发布者端进行配置。

amp-analytics 集成

amp-analytics 的集成记录在amp-access-analytics.md中。

CORS 来源安全性

授权和回传端点是 CORS 端点,它们必须实现 AMP CORS 安全规范中描述的安全协议。

计量

计量系统是指在一段时间内,读者可以免费查看一些文档视图的高级内容。一旦达到某个配额,付费墙就会启动,读者将看到带有追加销售消息和注册/登录链接的部分内容。例如,计量可以定义为“读者每月可以免费阅读 10 篇文章”。

AMP Access 提供以下工具来实现计量访问

  1. READER_ID 应用于存储计量信息。由于发布者不能依赖于始终能够在第三方环境中设置 Cookie,因此此数据应存储在服务器端。
  2. “阅读计数”只能在回传端点中更新。
  3. 只有唯一的文档可以计入配额。也就是说,刷新同一个文档十次构成单个视图。为此,授权和回传端点可以注入 SOURCE_URL 或类似的 URL 变量。请参阅 访问 URL 变量

首次点击免费

Google 的首次点击免费 (FCF) 政策在此处进行了描述,最近的更新在此处进行了更详细的描述。

为了实现 FCF,发布者必须 (1) 能够确定每次视图的引用服务,以及 (2) 能够计算每个读者每天的视图数量。

这两个步骤都包含在 AMP Access 规范中。可以使用 访问 URL 变量 中描述的 DOCUMENT_REFERRER URL 替换将引用者注入到授权和回传 URL 中。可以使用服务器端的回传端点完成视图计数。这与 计量中描述的计量实现非常相似。

登录流程

AMP 将登录对话框作为第一方窗口或弹出窗口或选项卡启动。在可能的情况下,AMP 查看器应尝试在浏览器上下文中启动登录对话框,以便它可以利用顶级的浏览器 API。

当读者激活登录链接时,AMP 运行时会启动登录流程,并且从描述上讲,它遵循以下步骤

  1. AMP 运行时或查看器为指定的登录 URL 打开登录对话框(第一方窗口)。该 URL 包含一个额外的“返回 URL”URL 查询参数(&return=RETURN_URL)。许多其他参数也可以扩展到 URL 中,例如读者 ID。有关更多详细信息,请参阅登录页面部分。
  2. 发布者会显示一个自由格式的登录页面。
  3. 读者按照登录步骤操作,例如输入用户名/密码或使用社交登录。
  4. 读者提交登录。发布者完成身份验证,设置 cookie,最后将读者重定向到先前请求的“返回 URL”。该重定向包含一个 URL 哈希参数 success,它可以是 truefalse
  5. 登录对话框将重定向到“返回 URL”。
  6. AMP 运行时重新授权该文档。

只有步骤 2-5 需要发布者处理:发布者只提供他们自己的登录页面,并确保在完成后正确重定向。登录页面没有施加特殊的约束,除了它应该能很好地作为对话框运行。

像往常一样,读者 ID 应该包含在对登录页面的调用中,并且可以被发布者用于身份映射。作为第一方窗口,发布者还将收到他们的 cookie,并且能够设置它们。如果发现读者已经在发布者端登录,建议发布者立即重定向回“返回 URL”,并使用 success=true 响应。

AMP 词汇表

  • AMP 文档 - 符合 AMP 格式并由 AMP 验证器验证的 HTML 文档。AMP 文档可由 Google AMP 缓存进行缓存。
  • AMP 验证器 - 对 HTML 文档执行静态分析的计算机程序,并根据文档是否符合 AMP 格式返回成功或失败。
  • AMP 运行时 - 执行 AMP 文档的 JavaScript 运行时。
  • Google AMP 缓存 - AMP 文档的代理缓存。
  • AMP 查看器 - 显示/嵌入 AMP 文档的 Web 或本机应用程序。
  • Publisher.com - AMP 发布者的网站。
  • CORS 端点 - 跨域 HTTPS 端点。有关更多信息,请参阅https://mdn.org.cn/en-US/docs/Web/HTTP/Access_control_CORS。有关如何保护此类请求的安全,请参阅CORS 来源安全
  • 读者 - 查看 AMP 文档的实际人员。
  • AMP 预渲染 - AMP 查看器可以利用预渲染,它会在显示之前渲染一个隐藏的文档。这会显著提高性能。但重要的是要考虑到文档预渲染不构成视图的事实,因为读者可能永远不会真正看到该文档。

修订

  • 2016-Sep-02:“noPingback”配置属性和可选回传。
  • 2016-Mar-03:登录后重新发送回传 (v0.5)。
  • 2016-Feb-19:更正了示例,以从 URL 变量替换中删除 {}
  • 2016-Feb-15:配置授权端点现在允许“authorizationFallbackResponse”属性,该属性可以在授权失败时使用。
  • 2016-Feb-11:授权端点中的授权请求超时。
  • 2016-Feb-11:现在允许嵌套字段引用,例如 object.field
  • 2016-Feb-09:首次点击免费计量部分。
  • 2016-Feb-03:添加到CORS 来源安全的“来源来源”安全规范。
  • 2016-Feb-01:可以使用 RETURN_URL URL 替换自定义登录页面的“return”查询参数。

附录 A:“amp-access”表达式语法

最新的 BNF 语法可在 access-expr-impl.jison 文件中找到。

此语法的关键摘录如下

search_condition:
    search_condition OR search_condition
  | search_condition AND search_condition
  | NOT search_condition
  | '(' search_condition ')'
  | predicate

predicate:
    comparison_predicate | truthy_predicate

comparison_predicate:
    scalar_exp '=' scalar_exp
  | scalar_exp '!=' scalar_exp
  | scalar_exp '<' scalar_exp
  | scalar_exp '<=' scalar_exp
  | scalar_exp '>' scalar_exp
  | scalar_exp '>=' scalar_exp

truthy_predicate: scalar_exp

scalar_exp: literal | field_ref

field_ref: field_ref '.' field_name | field_name

literal: STRING | NUMERIC | TRUE | FALSE | NULL

请注意,amp-access 表达式由 AMP 运行时和 Google AMP 缓存评估。这不是发布者需要实现的规范的一部分。此处仅供参考。

详细讨论

本节将详细介绍 amp-access 规范背后的设计,并阐明设计选择。即将推出。

验证

请参阅 AMP 验证器规范中的 amp-access 规则

需要更多帮助?

您已经阅读过本文档十几次了,但它并没有真正涵盖您所有的问题?也许其他人也有同样的感觉:在 Stack Overflow 上联系他们。

前往 Stack Overflow
发现错误或缺少功能?

AMP 项目强烈鼓励您的参与和贡献!我们希望您成为我们开源社区的持续参与者,但我们也欢迎您为特别感兴趣的问题做出一次性贡献。

前往 GitHub