GraphQL 请求(查询,变更或订阅)在验证通过后,GraphQL 服务器会执行这个请求,并返回与请求的结构相对应的结果,通常是 JSON 格式。GraphQL 服务端处理请求时依赖类型系统。
请求由几条信息组成:
ExecuteRequest(schema,document,operationName,variableValues,initialValue)
,执行请求的五要素:模式,请求文档,操作名称,变量,初始值。
要想执行请求,GraphQL 服务端必须要有一个解析过的请求文档,如果请求文档定义了多个操作还需要执行的操作的名称 operationName。
// 这是 GitHub Explorer 模拟时发送的数据。定义了2个查询请求 you 和 my,所以必须指定要执行的操作名称 operationName。
{
"query":"
query you{
viewer {
name
}
}
query my {
viewer {
name
}
}",
"variables":"",
"operationName":"my"
}
GetOperation(document,operationName)
operationName 为 null 时,如果文档只包含一个操作,返回文档中包含的操作。否则产生一个查询错误。
operationName 不为 null 时,返回文档中名称为 operationName 的操作。如果没有找到这个操作,则产生查询错误。
CoerceVariableValues(schema,operation,variableValues)
ExecuteRequest(schema,document,operationName,variableValues,initialValue)
如果操作是查询操作:
返回 ExecuteQuery(operation,schema,coercedVariableValues,initialValue)。
否则如果操作是一个变异操作:
返回 ExecuteMutation(operation,schema,coercedVariableValues,initialValue)。
验证规则
只有通过所有验证规则的请求才能被执行。如果验证错误,则应将其报告在响应的“错误”列表中,并且请求必须失败不能执行。
通常在执行请求之前先在请求的上下文中验证。然而,如果已经验证过完全相同的请求,则 GraphQL 服务可以在不验证的情况下执行请求。一个 GraphQL 服务应该只执行已经知道没有任何验证错误的请求。
例如:请求可以在开发过程中进行验证,前提是它稍后不会更改,或者服务可以验证请求一次并记录结果以避免将来再次验证相同的请求。
如果操作定义了变量,则这些变量的值需要用变量声明的类型按照转换规则进行强制转换。如果在变量值的强制转换过程中遇到查询错误,则操作失败而不执行。
CoerceVariableValues(schema, operation, variableValues)
类型系统必须提供一个查询根对象类型(通常是 Query)。如果支持变更或订阅,则还必须提供变更根对象类型(Mutation)或订阅根对象类型(Subscription)。
ExecuteQuery(query, schema, variableValues, initialValue)
查询操作使用查询根对象类型执行查询的顶级选择集(top level selection set)。执行查询时可能会提供一个初始值。
// queryType 必须是模式 Schema 中的根查询类型。用下面的查询操作可以获得 queryType 的名字
{
__schema {
queryType {
name
}
}
}
ExecuteMutation(突变,模式,variableValues,initialValue)
变更操作使用变更根对象类型执行变更的顶层选择集。多个变更会顺序执行而不是并发执行。要注意可能的相互影响,防止竞争。
// mutationType 必须是模式 Schema 中的根查询类型。用下面的查询操作可以获得 mutationType 的名字
{
__schema {
mutationType {
name
}
}
}
要执行一个选择集,需要知道正在被评估的对象值和对象类型,以及是否必须顺序执行还是可以并行执行。
选择集合首先要转换成分组的字段集合。然后,分组的字段集合中的每个字段都会生成一个对应响应的条目。
ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues)
通常,服务器可以按照它选择的顺序(默认并行)执行分组字段中的条目。因为查询操作始终是无副作用和幂等的,所以执行顺序不影响结果。
// GraphQL 执行器可以按照它的顺序来解析这四个字段(birthday 必须在 month 前解析,address 必须在 street 前解析)。:
{
birthday {
month
}
address {
street
}
}
服务器会按照收到的顺序串行执行变更操作。
在执行之前,需要调用 CollectFields()
将选择集转换为分组字段。分组字段集中的每个条目都是一个共享响应键(response key (alias or field name) )的字段列表。这确保了通过引用片段包含的相同响应键(response key (alias or field name) )中的所有字段可以同时执行。
作为一个例子,收集这个选择集的字段将收集该字段的两个实例a和一个字段b:
{
a {
subfield1
}
...ExampleFragment
}
fragment ExampleFragment on Query {
a {
subfield2
}
b
}
CollectFields(objectType, selectionSet, variableValues, visitedFragments)
:产生分组字段集。
执行过程中,CollectFields()
方法采用深度优先策略,以确保响应中的字段顺序是稳定和可预测的。
DoesFragmentTypeApply(objectType, fragmentType)
:判断两个参数的类型是否一致或具有继承关系。
分组字段集中所请求的每个字段,都对应响应数据中的一个条目。执行字段时,首先强制转换任何提供的参数值,然后解析字段值,如果字段值是标量则返回标量值,否则递归继续执行这个分组字段选择集。
ExecuteField(objectType, objectValue, fieldType, fields, variableValues)
:执行字段
字段可能包含参数。这些参数是由类型系统定义的,只能是这几种输入类型:Scalar 标量,Enum 枚举,Input Object 输入对象或 List 列表或 Non-Null 非空包装这三种变体。参数可以是常量或变量。
CoerceArgumentValues(objectType, field, variableValues)
:转换字段参数类型
最终暴露 GraphQL 接口的内部系统必须为所有字段提供值。
ResolveFieldValue(objectType, objectValue, fieldName, argumentValues)
:确定 fieldName 字段的解析值。
由于解析器依靠读取底层数据库或联网服务来产生一个值,所以通常是异步的。需要 GraphQL 执行程序来处理异步执行流程。
确定了一个字段的值后,如果确实是期望的返回类型,则结束计算。如果返回类型是另一个对象 Object 类型,则字段计算过程将递归执行。
CompleteValue(fieldType, fields, result, variableValues)
:计算字段的值。
当计算完成的一个字段的返回类型是抽象类型(接口或联合)时,抽象类型必须解析为相关的对象类型。
ResolveAbstractType(abstractType, objectValue)
:调用由类型系统提供的内部方法,以确定通过 objectValue 给定的抽象类型的具体的对象的类型。
当并行执行多个相同名称的字段时,它们的选择集在完成该值时合并在一起以便继续执行子选择集。
// GitHub 查询示例,选择集同名时响应会合并。
// 请求:
{
viewer {
name
}
viewer {
id
}
}
// 返回:
{
"data": {
"viewer": {
"name": null,
"id": "MDQ6VXNlcjE4NTMyODU5"
}
}
}
MergeSelectionSets(fields)
data
条目应为空。