Schema 教程
Schema 教程第5课:为不同用户定制内容

第5课:为不同用户定制内容

我们可以根据查询到的数据(例如登录用户的角色),在字段中返回不同的响应。

为不同用户定制内容的 GraphQL Query

以下 GraphQL query 用于获取文章内容,并仅为管理员用户在内容底部追加"编辑此文章"链接:

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
      value: "administrator",
      array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}

对于管理员用户,响应如下:

{
  "data": {
    "user": {
      "isAdminUser": true
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n<p><a href=\"https:\/\/mysite.com\/wp-admin\/post.php?post=1&amp;action=edit\">(Admin only) Edit post<\/a><\/p>"
    }
  }
}

对于非管理员用户,响应如下:

{
  "data": {
    "user": {
      "isAdminUser": false
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n"
    }
  }
}

让 GraphQL 服务器(在所有可能的条件下)动态计算字段所需的值,具有以下优势:

  • 简化应用程序逻辑:建立单一可信来源,代码变得 DRY,客户端无需再实现对应的逻辑
  • 提升应用程序可靠性:当多个客户端访问服务器数据时,同一逻辑的不同实现可能存在差异,从而导致 bug(尤其是当客户端基于不同技术时,例如网站用 JavaScript、Android 应用用 Java、iPhone 应用用 Swift 等)

逐步解析:创建 GraphQL Query

以下是对该 query 工作原理的详细分析。

判断用户是否为管理员

该 query 检查登录用户是否具有 "administrator" 角色,并将此条件导出到动态变量 $isAdminUser

query
{
  me {
    roleNames
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}

操作的条件执行

Multiple Query Execution 启用时,指令 @include@skip 也可以应用于操作。这样,我们就可以根据某个动态变量的值来决定是否执行某个操作。

在以下 query 中,两个操作中只有一个会被执行:

  • RetrieveContentForAdminUser 仅在 $isAdminUsertrue 时执行
  • RetrieveContentForNonAdminUser 仅在 $isAdminUserfalse 时执行
query RetrieveContentForAdminUser
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  # ...
}
 
query RetrieveContentForNonAdminUser
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  # ...
}

我们来为文章的 content 字段根据用户是否为管理员提供两种不同的响应:

  • 第一个操作将 content 用作别名,通过 _sprintforiginalContent 字段和 wpAdminEditURL 字段拼接在一起,动态计算字段值
  • 第二个操作直接获取 content 字段
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content
    wpAdminEditURL
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}

添加待执行的操作

现在我们有了两个可能被执行的操作,但在执行 query 时只能指定一个 ?operationName=...

因此,我们添加操作 ExecuteAll,使其同时依赖于 RetrieveContentForAdminUserRetrieveContentForNonAdminUser,并包含简单字段 id(因为操作中必须查询某些内容):

query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id
}

使用 ?operationName=ExecuteAll 调用端点时,两个操作都会被加载,但实际只有其中一个会被执行。

移除不需要的数据

最后一步是通过 @remove 移除所有辅助字段(即不需要在响应中输出的字段)。

完整的 GraphQL query 如下:

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}