

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# AWS AppSync 解析器映射模板上下文参考
<a name="resolver-context-reference"></a>

**注意**  
我们现在主要支持 APPSYNC\$1JS 运行时系统及其文档。请考虑使用 APPSYNC\$1JS 运行时系统和[此处](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html)的指南。

AWS AppSync 定义了一组用于处理解析器映射模板的变量和函数。这样，就可以更轻松地通过 GraphQL 对数据进行逻辑操作。本文档将介绍这些函数，并提供使用模板的示例。

## 使用 `$context`
<a name="accessing-the-context"></a>

`$context` 变量是一个映射，它保留进行解析器调用的所有上下文信息。它具有以下结构：

```
{
   "arguments" : { ... },
   "source" : { ... },
   "result" : { ... },
   "identity" : { ... },
   "request" : { ... },
   "info": { ... }
}
```

**注意**  
如果您尝试通过键访问 dictionary/map 条目（例如中的条目`context`）来检索该值，Velocity 模板语言 (VTL) 允许您直接使用该符号`<dictionary-element>.<key-name>`。但是，这可能不适用于所有情况，例如当键名称具有特殊字符时（例如，下划线“\$1”）。建议您始终使用 `<dictionary-element>.get("<key-name>")` 表示法。

`$context` 映射中每个字段的定义如下所示：

### `$context` 字段
<a name="accessing-the-context-list"></a>

** `arguments` **  
包含该字段的所有 GraphQL 参数的映射。

** `identity` **  
包含有关调用方的信息的对象。有关该字段结构的更多信息，请参阅[身份](#aws-appsync-resolver-context-reference-identity)。

** `source` **  
包含父字段解析的映射。

** `stash` **  
存储区是一个在每个解析器和函数映射模板中提供的映射。同一存储实例通过单个解析程序生效。这意味着，您可以使用存储区来跨请求和响应映射模板以及管道解析器中的函数传递任意数据。存储区公开与 [Java 映射](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html)数据结构相同的方法。

** `result` **  
此解析器结果的容器。该字段仅适用于响应映射模板。  
例如，如果要解析以下查询的 `author` 字段：  

```
query {
    getPost(id: 1234) {
        postId
        title
        content
        author {
            id
            name
        }
    }
}
```
那么在处理响应映射模板时，完整的 `$context` 变量可以是：  

```
{
  "arguments" : {
    id: "1234"
  },
  "source": {},
  "result" : {
      "postId": "1234",
      "title": "Some title",
      "content": "Some content",
      "author": {
        "id": "5678",
        "name": "Author Name"
      }
  },
  "identity" : {
      "sourceIp" : ["x.x.x.x"],
      "userArn" : "arn:aws:iam::123456789012:user/appsync",
      "accountId" : "666666666666",
      "user" : "AIDAAAAAAAAAAAAAAAAAA"
  }
}
```

** `prev.result` **  
在管道解析器中执行的任何以前操作的结果。  
如果上一个操作是管道解析器的之前映射模板，则 `$ctx.prev.result` 表示模板评估的输出，并提供给管道中的第一个函数。  
如果上一个操作是第一个函数，则 `$ctx.prev.result` 表示第一个函数的输出，并且可供管道中的第二个函数使用。  
如果上一个操作是最后一个函数，则 `$ctx.prev.result` 表示最后一个函数的输出，并提供给管道解析器的之后映射模板。

** `info` **  
包含有关 GraphQL 请求的信息的对象。有关该字段的结构，请参阅[信息](#aws-appsync-resolver-context-reference-info)。

### 身份
<a name="aws-appsync-resolver-context-reference-identity"></a>

`identity` 部分包含调用方的相关信息。此部分的形式取决于您的 AWS AppSync API 的授权类型。

有关 AWS AppSync 安全选项的更多信息，请参阅[授权和身份验证](security-authz.md#aws-appsync-security)。

** `API_KEY` 授权**  
不填充 `identity` 字段。

**`AWS_LAMBDA` 授权**  
`identity` 包含 `resolverContext` 密钥，其中包含为请求授权的 Lambda 函数返回的相同 `resolverContext` 内容。

** `AWS_IAM` 授权**  
`identity` 采用以下格式：  

```
{
    "accountId" : "string",
    "cognitoIdentityPoolId" : "string",
    "cognitoIdentityId" : "string",
    "sourceIp" : ["string"],
    "username" : "string", // IAM user principal
    "userArn" : "string",
    "cognitoIdentityAuthType" : "string", // authenticated/unauthenticated based on the identity type
    "cognitoIdentityAuthProvider" : "string" // the auth provider that was used to obtain the credentials
}
```

** `AMAZON_COGNITO_USER_POOLS` 授权**  
`identity` 采用以下格式：  

```
{
    "sub" : "uuid",
    "issuer" : "string",
    "username" : "string"
    "claims" : { ... },
    "sourceIp" : ["x.x.x.x"],
    "defaultAuthStrategy" : "string"
}
```

每个字段的定义如下所示：

** `accountId` **  
来电者的 AWS 账户 ID。

** `claims` **  
用户拥有的声明。

** `cognitoIdentityAuthType` **  
根据身份类型确定经过身份验证或未经身份验证。

** `cognitoIdentityAuthProvider` **  
外部身份提供程序信息的逗号分隔列表，用于获取对请求进行签名时使用的凭证。

** `cognitoIdentityId` **  
调用方的 Amazon Cognito 身份 ID。

** `cognitoIdentityPoolId` **  
与调用方关联的 Amazon Cognito 身份池 ID。

** `defaultAuthStrategy` **  
此调用方的默认授权策略（`ALLOW` 或 `DENY`）。

** `issuer` **  
令牌发布者。

** `sourceIp` **  
 AWS AppSync 接听的来电者的源 IP 地址。如果请求不包含 `x-forwarded-for` 标头，则源 IP 值仅包含来自 TCP 连接的单个 IP 地址。如果请求中包含 `x-forwarded-for` 标头，那么源 IP 将是来自 `x-forwarded-for` 标头的 IP 地址列表，以及来自 TCP 连接的 IP 地址。

** `sub` **  
经过验证的用户的 UUID。

** `user` **  
IAM 用户。

** `userArn` **  
IAM 用户的 Amazon 资源名称 (ARN)。

** `username` **  
已验证的用户的用户名。对于 `AMAZON_COGNITO_USER_POOLS` 授权，*用户名*的值是 *cognito:username* 属性的值。在`AWS_IAM`授权的情况下，用户名的值就是* AWS 用户*委托人的值。如果您将 IAM 授权与从 Amazon Cognito 身份池提供的凭证一起使用，我们建议您使用 `cognitoIdentityId`。

### 访问请求标头
<a name="aws-appsync-resolver-context-reference-util"></a>

AWS AppSync 支持从客户端传递自定义标头，并使用在 GraphQL 解析器中访问它们。`$context.request.headers`然后，您可以使用标头值执行操作，例如，将数据插入到数据来源或进行授权检查。您可以在命令行中使用 `$curl` 将一个或多个请求标头与 API 密钥一起使用，如以下示例中所示：

**单标头示例** 

假设您设置一个 `custom` 标头，值为 `nadia`，如下所示：

```
curl -XPOST -H "Content-Type:application/graphql" -H "custom:nadia" -H "x-api-key:<API-KEY-VALUE>" -d '{"query":"mutation { createEvent(name: \"demo\", when: \"Next Friday!\", where: \"Here!\") {id name when where description}}"}' https://<ENDPOINT>/graphql
```

然后可使用 `$context.request.headers.custom` 访问它。例如，对于 DynamoDB，它可能位于以下 VTL 中：

```
"custom": $util.dynamodb.toDynamoDBJson($context.request.headers.custom)
```

**多标头示例** 

您还可以在一个请求中传递多个标头，并在解析器映射模板中访问它们。例如，如果为 `custom` 标头设置了两个值：

```
curl -XPOST -H "Content-Type:application/graphql" -H "custom:bailey" -H "custom:nadia" -H "x-api-key:<API-KEY-VALUE>" -d '{"query":"mutation { createEvent(name: \"demo\", when: \"Next Friday!\", where: \"Here!\") {id name when where description}}"}' https://<ENDPOINT>/graphql
```

它们可以作为一个数组访问，例如 `$context.request.headers.custom[1]`。

**注意**  
AWS AppSync 不会在中公开 cookie 标头`$context.request.headers`。

### 访问请求自定义域名
<a name="aws-access-requested-custom-domain-names"></a>

AWS AppSync 支持配置自定义域，您可以使用该域名访问您的 GraphQL 和实时终端节点。 APIs在使用自定义域名发出请求时，您可以使用 `$context.request.domainName` 获取域名。

在使用默认 GraphQL 终端节点域名时，值为 `null`。

### 信息
<a name="aws-appsync-resolver-context-reference-info"></a>

`info` 部分包含有关 GraphQL 请求的信息。该部分采用以下格式：

```
{
    "fieldName": "string",
    "parentTypeName": "string",
    "variables": { ... },
    "selectionSetList": ["string"],
    "selectionSetGraphQL": "string"
}
```

每个字段的定义如下所示：

** `fieldName` **  
当前正在解析的字段的名称。

** `parentTypeName` **  
当前正在解析的字段的父类型的名称。

** `variables` **  
保留传递到 GraphQL 请求的所有变量的映射。

** `selectionSetList` **  
GraphQL 选择集中字段的列表表示形式。具有别名的字段仅按别名进行引用，而不按字段名称进行引用。以下示例对此详细进行了介绍。

** `selectionSetGraphQL` **  
选择集的字符串表示形式，格式为 GraphQL 架构定义语言 (SDL)。尽管片段不会合并到选择集中，但会保留内联片段，如以下示例中所示。

**注意**  
默认情况下，在 `context.info` 上使用 `$utils.toJson()` 时，不会序列化 `selectionSetGraphQL` 和 `selectionSetList` 返回的值。

例如，如果您要解析以下查询的 `getPost` 字段：

```
query {
  getPost(id: $postId) {
    postId
    title
    secondTitle: title
    content
    author(id: $authorId) {
      authorId
      name
    }
    secondAuthor(id: "789") {
      authorId
    }
    ... on Post {
      inlineFrag: comments: {
        id
      }
    }
    ... postFrag
  }
}

fragment postFrag on Post {
  postFrag: comments: {
    id
  }
}
```

那么，在处理映射模板时可用的完整 `$context.info` 变量可以是：

```
{
  "fieldName": "getPost",
  "parentTypeName": "Query",
  "variables": {
    "postId": "123",
    "authorId": "456"
  },
  "selectionSetList": [
    "postId",
    "title",
    "secondTitle"
    "content",
    "author",
    "author/authorId",
    "author/name",
    "secondAuthor",
    "secondAuthor/authorId",
    "inlineFragComments",
    "inlineFragComments/id",
    "postFragComments",
    "postFragComments/id"
  ],
  "selectionSetGraphQL": "{\n  getPost(id: $postId) {\n    postId\n    title\n    secondTitle: title\n    content\n    author(id: $authorId) {\n      authorId\n      name\n    }\n    secondAuthor(id: \"789\") {\n      authorId\n    }\n    ... on Post {\n      inlineFrag: comments {\n        id\n      }\n    }\n    ... postFrag\n  }\n}"
}
```

`selectionSetList` 仅公开属于当前类型的字段。如果当前类型是接口或联合，则仅公开属于该接口的选定字段。例如，给定以下架构：

```
type Query {
    node(id: ID!): Node
}

interface Node {
    id: ID
}

type Post implements Node {
    id: ID
    title: String
    author: String
}

type Blog implements Node {
    id: ID
    title: String
    category: String
}
```

以及以下查询：

```
query {
    node(id: "post1") {
        id
        ... on Post {
            title
        }

        ... on Blog {
            title
        }
    }
}
```

如果在进行 `Query.node` 字段解析时调用 `$ctx.info.selectionSetList`，则仅公开 `id`：

```
"selectionSetList": [
    "id"
]
```

## 清理输入
<a name="sanitizing-inputs"></a>

应用程序必须对不可信的输入进行清理，以防止任何外部方在应用程序的预期用途之外使用该应用程序。由于 `$context` 包含 `$context.arguments`、`$context.identity`、`$context.result`、`$context.info.variables` 和 `$context.request.headers` 等属性中的用户输入，因此，务必谨慎在映射模板中清理它们的值。

由于映射模板代表 JSON，因此输入清理采用从表示用户输入的字符串转义 JSON 保留字符的形式。将 JSON 保留字符放入映射模板时，最佳做法是使用 `$util.toJson()` 实用程序从敏感字符串值转义 JSON 保留字符。

例如，在以下 Lambda 请求映射模板中，由于我们访问了不安全的客户输入字符串 (`$context.arguments.id`)，因此，我们使用 `$util.toJson()` 将其包装起来，以防止未转义的 JSON 字符破坏 JSON 模板。

```
{
    "version": "2017-02-28",
    "operation": "Invoke",
    "payload": {
        "field": "getPost",
        "postId": $util.toJson($context.arguments.id)
    }
}
```

下面的映射模板与之相反，我们直接插入 `$context.arguments.id` 而不进行清理。这不适用于包含未转义引号或其他 JSON 保留字符的字符串，并且可能使您的模板很容易失败。

```
## DO NOT DO THIS
{
    "version": "2017-02-28",
    "operation": "Invoke",
    "payload": {
        "field": "getPost",
        "postId": "$context.arguments.id" ## Unsafe! Do not insert $context string values without escaping JSON characters.
    }
}
```