Schema 教程
Schema 教程第18课:通过 Webhook 与外部服务交互

第18课:通过 Webhook 与外部服务交互

Webhook 是一种基于 HTTP 的回调函数,外部服务通过调用它来通知某个事件,并随之传递一个数据载荷(payload)。Webhook 使不同的 Web 应用和服务能够相互通信。

调用 Webhook 的服务示例包括:

  • GitHub:当仓库有提交被推送时
  • Dropbox:当文档被更新时
  • WooCommerce:当有新订单添加时
  • Microsoft Teams:接收富文本消息并发布到公共频道时
  • ConvertKit:当订阅者被激活时

使用 Gato GraphQL,我们可以创建充当 Webhook 的 Persisted Query:

  • 由于 Persisted Query 在其独立 URL 下公开,因此可以用作 Webhook 的目标地址
  • 每个 Persisted Query 可以专门处理某个特定的 Webhook

在本教程课程中,我们将创建一个 Persisted Query 来与 ConvertKit 进行交互。

查阅 Webhook 文档

第一步是阅读该网站的文档,了解通过载荷发送的数据内容。

分析 ConvertKit 中的 Webhook,与订阅者相关的事件会向我们的 URL 发送一个 POST 请求,并携带如下 JSON 载荷:

{
  "subscriber": {
    "id": 1,
    "first_name": "John",
    "email_address": "John@example.com",
    "state": "active",
    "created_at": "2018-02-15T19:40:24.913Z",
    "fields": {
      "My Custom Field": "Value"
    }
  }
}

从载荷中提取数据

由于请求通过 POST 发送,我们必须从 HTTP 请求的正文中提取数据(这由 HTTP Request via Schema 扩展提供支持)。

如果 HTTP 请求通过 GET 执行,则 Persisted Query 可以直接从 URL 参数中获取数据项

此 GraphQL query 获取请求的正文并将其转换为 JSON 对象。然后从 JSON 对象中提取 "subscriber.first_name""subscriber.email_address" 两项,并将它们导出为动态变量:

query ExtractPayloadData {
  body: _httpRequestBody
  bodyJSONObject: _strDecodeJSONObject(string: $__body)
 
  subscriberFirstName: _objectProperty(
    object: $__bodyJSONObject,
    by: { path: "subscriber.first_name" }
  )
    @export(as: "subscriberFirstName")
  
  subscriberEmail: _objectProperty(
    object: $__bodyJSONObject,
    by: { path: "subscriber.email_address" }
  )
    @export(as: "subscriberEmail")
}

HTTP Request via Schema 扩展允许我们通过以下字段获取当前 HTTP 请求的所有数据:

  • _httpRequestBody:HTTP 请求的正文
  • _httpRequestClientHost:客户端主机
  • _httpRequestClientIP:客户端 IP 地址(若服务器未正确配置则为 null
  • _httpRequestCookie:请求的 Cookie 值
  • _httpRequestCookies:请求的所有 Cookie
  • _httpRequestDomain:请求 URL 的域名
  • _httpRequestFullURL:请求的 URL(含查询参数)
  • _httpRequestHasCookie:请求是否包含特定 Cookie?
  • _httpRequestHasHeader:请求是否包含特定请求头?
  • _httpRequestHasParam:请求是否包含特定参数?
  • _httpRequestHeader:请求头的值
  • _httpRequestHeaders:请求的所有请求头
  • _httpRequestHost:请求 URL 的主机
  • _httpRequestMethod:请求方法
  • _httpRequestStringParam?param=value 形式的参数值(通过 POST 或 GET 传递)
  • _httpRequestStringListParam?param[]=value1&param[]=value2 形式的参数值(通过 POST 或 GET 传递)
  • _httpRequestParams:通过 POST 或 URL 查询传递的参数
  • _httpRequestProtocol:请求协议
  • _httpRequestQuery:查询参数字符串
  • _httpRequestReferer:请求来源页(Referer)
  • _httpRequestRequestTime:请求开始时的时间戳
  • _httpRequestScheme:请求 URL 的协议方案
  • _httpRequestServerIP:服务器 IP 地址
  • _httpRequestURL:请求的 URL(不含查询参数)
  • _httpRequestURLPath:请求 URL 的绝对路径(以"/"开头)
  • _httpRequestUserAgent:用户代理

使用数据执行操作

从载荷中提取数据后,我们可以使用这些数据执行某些操作。

此 GraphQL query 处理 subscriber.subscriber_unsubscribe 事件,向取消订阅的用户发送电子邮件以征求反馈。

query CreateEmailMessage
  @depends(on: "ExtractPayloadData")
{
  emailMessageTemplate: _strConvertMarkdownToHTML(
    text: """
 
Hey {$subscriberFirstName}, it's sad to let you go!
 
Please be welcome to complete [this form](https://forms.gle/FpXNromWAsZYC1zB8) and let us know if there is anything we can do better.
 
Thanks. Hope to see you back!
 
    """
  )
  emailMessage: _strReplaceMultiple(
    search: ["{$subscriberFirstName}"],
    replaceWith: [$subscriberFirstName],
    in: $__emailMessageTemplate
  )
    @export(as: "emailMessage")
}
 
mutation SendEmail @depends(on: "CreateEmailMessage") {
  _sendEmail(
    input: {
      to: $subscriberEmail
      subject: "Would you like to give us feedback on how we can improve?"
      messageAs: {
        html: $emailMessage
      }
    }
  ) {
    status
  }
}