[译] GraphQL - 学习 - 验证

news/2024/7/3 20:25:08

原文地址: Validation

通过使用类型系统,可以预先确定一个 GraphQL 查询是否有效。这允许服务器和客户机在创建无效查询时有效地通知开发人员,而不必依赖于运行时检查。

对于我们的星球大战示例,文件 starwarsvalidtest.js 包含大量的查询,这些查询演示了各种各样的失效,并且是一个可以运行来执行引用实现验证器的测试文件。

首先,让我们使用一个复杂的有效查询。这是一个嵌套的查询,类似于上一节的示例,但是将复制的字段分解为一个片段:

{
  hero {
    ...NameAndAppearances
    friends {
      ...NameAndAppearances
      friends {
        ...NameAndAppearances
      }
    }
  }
}

fragment NameAndAppearances on Character {
  name
  appearsIn
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "friends": [
        {
          "name": "Luke Skywalker",
          "appearsIn": [
            "NEWHOPE",
            "EMPIRE",
            "JEDI"
          ],
          "friends": [
            {
              "name": "Han Solo",
              "appearsIn": [
                "NEWHOPE",
                "EMPIRE",
                "JEDI"
              ]
            },
            {
              "name": "Leia Organa",
              "appearsIn": [
                "NEWHOPE",
                "EMPIRE",
                "JEDI"
              ]
            },
            ...
          ]
        },
        {
          "name": "Han Solo",
          "appearsIn": [
            "NEWHOPE",
            "EMPIRE",
            "JEDI"
          ],
          "friends": [
            {
              "name": "Luke Skywalker",
              "appearsIn": [
                "NEWHOPE",
                "EMPIRE",
                "JEDI"
              ]
            },
            ...
          ]
        },
        {
          "name": "Leia Organa",
          "appearsIn": [
            "NEWHOPE",
            "EMPIRE",
            "JEDI"
          ],
          "friends": [
            {
              "name": "Luke Skywalker",
              "appearsIn": [
                "NEWHOPE",
                "EMPIRE",
                "JEDI"
              ]
            },
            ...
            {
              "name": "R2-D2",
              "appearsIn": [
                "NEWHOPE",
                "EMPIRE",
                "JEDI"
              ]
            }
          ]
        }
      ]
    }
  }
}

这个查询是有效的。让我们看看一些无效的查询。

片段不能引用自身或创建一个循环,因为这可能导致一个无限循环!下面是相同的查询,但是没有明确的三层嵌套:

{
  hero {
    ...NameAndAppearancesAndFriends
  }
}

fragment NameAndAppearancesAndFriends on Character {
  name
  appearsIn
  friends {
    ...NameAndAppearancesAndFriends
  }
}

结果

{
  "errors": [
    {
      "message": "Cannot spread fragment \"NameAndAppearancesAndFriends\" within itself.",
      "locations": [
        {
          "line": 11,
          "column": 5
        }
      ]
    }
  ]
}

当我们查询字段时,我们必须查询给定类型中存在的字段。因此,当 hero 返回一个 Character 时,我们必须查询 Character 的字段。该类型没有一个 favoriteSpaceship,因此这个查询是无效的:

# INVALID: favoriteSpaceship does not exist on Character
{
  hero {
    favoriteSpaceship
  }
}
{
  "errors": [
    {
      "message": "Cannot query field \"favoriteSpaceship\" on type \"Character\".",
      "locations": [
        {
          "line": 4,
          "column": 5
        }
      ]
    }
  ]
}

每当我们查询一个字段,它返回的不是标量或枚举时,我们需要指定要从该字段返回的数据。英雄返回一个 Character,我们一直在请求 nameappearsIn 之类的字段;如果我们省略了这个,这个查询就不会有效:

# INVALID: hero is not a scalar, so fields are needed
{
  hero
}
{
  "errors": [
    {
      "message": "Field \"hero\" of type \"Character\" must have a selection of subfields. Did you mean \"hero { ... }\"?",
      "locations": [
        {
          "line": 3,
          "column": 3
        }
      ]
    }
  ]
}

类似地,如果字段是标量,在它上查询额外的字段是没有意义的,这样做会使查询无效:

# INVALID: name is a scalar, so fields are not permitted
{
  hero {
    name {
      firstCharacterOfName
    }
  }
}
{
  "errors": [
    {
      "message": "Field \"name\" must not have a selection since type \"String!\" has no subfields.",
      "locations": [
        {
          "line": 4,
          "column": 10
        }
      ]
    }
  ]
}

之前,有人注意到,查询只能查询所涉及类型的字段;当我们查询返回一个 Characterhero 时,我们只能查询存在于字符的字段。但是,如果我们想要查询 R2-D2s 的主函数,会发生什么呢?

# INVALID: primaryFunction does not exist on Character
{
  hero {
    name
    primaryFunction
  }
}
{
  "errors": [
    {
      "message": "Cannot query field \"primaryFunction\" on type \"Character\". Did you mean to use an inline fragment on \"Droid\"?",
      "locations": [
        {
          "line": 5,
          "column": 5
        }
      ]
    }
  ]
}

这个查询是无效的,因为 primaryFunction 不是一个 Character 的字段。我们希望通过某种方式表明,如果 CharacterDroid,我们希望获取 primaryFunction,否则将忽略该字段。我们可以使用前面介绍的片段来实现这一点。通过在 Droid 上设置一个片段并包含它,我们可以确保只查询定义的 primaryFunction

{
  hero {
    name
    ...DroidFields
  }
}

fragment DroidFields on Droid {
  primaryFunction
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "primaryFunction": "Astromech"
    }
  }
}

这个查询是有效的,但是有点冗长;当我们多次使用它们时,命名的片段是很有价值的,但是我们只使用一次。我们可以使用一个内联片段,而不是使用一个命名的片段;这仍然允许我们指出我们正在查询的类型,但是没有指定一个单独的片段:

{
  hero {
    name
    ... on Droid {
      primaryFunction
    }
  }
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "primaryFunction": "Astromech"
    }
  }
}

这仅仅触及了验证系统的表面;这里有许多验证规则,以确保 GraphQL 查询具有语义上的意义。规范在 验证 部分中详细讨论了这个主题,以及 GraphQL 中的验证目录。js 包含实现符合规范的 GraphQL 验证器的代码。


http://www.niftyadmin.cn/n/2608923.html

相关文章

bezierMaker.js——N阶贝塞尔曲线生成器

写在最前 由于原生的Canvas最高只支持到三阶贝塞尔曲线,那么我想添加多个控制点怎么办呢?(即便大部分复杂曲线都可以用3阶贝塞尔来模拟)与此同时,关于贝塞尔控制点的位置我们很难非常直观的清楚到底将控制点设置为多少…

整理下 Swift 的 Access Control

整理下 Swift 的 Access Control~属性访问级别。 众所周知,Swift 并没有像 Objective-C 那样,有着泾渭分明的 .h 和 .m 文件可以来决定哪些参数、方法需要暴露,哪些不需要暴露。Swift 提供了五种访问级别(assess level…

JS ES6 对象字面量增强写法

目录对象属性字面量增强写法对象函数字面量增强写法对象属性字面量增强写法 属性增强写法:就是对象中代表键值的变量名称与键名一致,则可以直接缩写为一个let name "张三"let age 13let obj {name,age}console.log(obj) // {name: "…

vue v-if v-else-if v-else 在简单场景中使用computed计算属性的替换写法

<template><div class"hello"><!-- 写法1 --><div><label v-if"count < 0">我还没出生</label><label v-else-if"count 0">我诞生了</label><label v-else-if"count < 3"&…

修改IDEAL 快捷键风格

选择File------Setting-----进入到设置窗口-------keymap--------选择自己适应的快捷键风格 选择Eclipse后Ideal的快捷键就和Eclipse相同 转载于:https://www.cnblogs.com/kuoAT/p/8267196.html

vue input框的复用 使用key值进行控制

当input框复用的时候&#xff0c;切换框会将隐藏掉的框的内容带过去加上key属性之后&#xff0c;既不会出现上述情况&#xff0c;应用面比较窄<template><div class"hello"><!-- 复用写法 --><label v-if"type false">联系电话&l…

使用parcel替代webpack制作omi-tap插件

使用parcel替代webpack制作omi-tap插件 Parcel 是一个零配置的网络应用打包工具, 适用于经验不同的开发者, 它利用多核处理提供了极快的速度。对比 webpack 最大的两个亮点: 零配置极快速omi 相关的插件都是基于 webpack 进行开发&#xff0c;今天正式投入 Parcel 怀抱。 准备工…

flume拦截器分析

为什么80%的码农都做不了架构师&#xff1f;>>> fluem拦截器 有的时候希望通过Flume将读取的文件再细分存储&#xff0c;比如讲source的数据按照业务类型分开存储&#xff0c;具体一点比如类似&#xff1a;将source中web、wap、media等的内容分开存储&#xff1b;比…