在对我们的GraphQL API进行更新时,仅需要模型_id
字段,因此下面的SDL语言代码中的!
。更新中不必包括其他字段,例如name
,但也不能具有null
值。当前,从名称字段中排除!
可使最终用户不必在更新中传递name
,但允许最终用户传递{{1} },这是不允许的。
一个null
值使我们知道需要从数据库中删除一个字段。
下面是一个可能导致问题的模型示例-name
自定义标量不允许空值,但GraphQL仍允许它们通过:
null
当传入null值时,它会绕过我们的Scalars,因此如果null值不是允许值,我们就不会引发用户输入验证错误。
我知道Name
的意思是type language {
_id: ObjectId
iso: Language_ISO
auto_translate: Boolean
name: Name
updated_at: Date_time
created_at: Date_time
}
input language_create {
iso: Language_ISO!
auto_translate: Boolean
name: Name!
}
input language_update {
_id: ObjectId!
iso: Language_ISO!
auto_translate: Boolean
name: Name
}
,缺少!
的意思是该字段可以为空,但据我所知,我们无法指定该字段令人沮丧如果不需要/可选字段,则为字段的确切值。此问题仅在更新时发生。
是否有任何方法可以通过自定义Scalars解决此问题,而不必在每个更新解析器中启动硬编码逻辑,这似乎很麻烦?
应该失败的示例更改
non-nullable
变量
!
UPDATE 9/11/18:供参考,我找不到解决方法,因为使用自定义标量,自定义指令和验证规则存在问题。我已经在GitHub上打开了一个问题:https://github.com/apollographql/apollo-server/issues/1942
答案 0 :(得分:4)
您真正要寻找的是自定义验证逻辑。您可以在构建架构时通常包含的“默认”集之上添加所需的任何验证规则。这是一个粗略的示例,说明如何添加一个规则,以在将特定类型或标量用作参数时检查空值:
Tab
编辑:仅当您不使用变量时,以上方法才有效,这在大多数情况下不会很有帮助。解决方法是,我能够利用FIELD_DEFINITION指令来实现所需的行为。您可能有多种方法可以解决此问题,但这是一个基本示例:
import "package:flutter/material.dart";
import "package:flutter/services.dart";
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return MaterialApp(
home: MyHome()
);
}
}
class MyHomeState extends State<MyHome> with TickerProviderStateMixin {
TabController tabController;
@override
void initState() {
tabController = tabController?? TabController(
vsync: this,
length: 2
);
super.initState();
}
@override
void dispose() {
tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TabBar(
controller: tabController,
tabs: [
Container(
color: Colors.purple,
width: 100.0,
height: 50.0
),
Container(
color: Colors.pink,
width: 100.0,
height: 50.0
)
]
),
Expanded(
child: TabBarView(
controller: tabController,
children: [
MyPageView(),
MyPageView(),
],
)
)
]
)
);
}
}
class MyHome extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyHomeState();
}
}
class MyPageViewState extends State<MyPageView> {
PageController pageController;
@override
void initState() {
pageController = pageController?? PageController();
super.initState();
}
@override
void dispose() {
pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return PageView(
controller: pageController,
children: [
Container(
color: Colors.blue
),
Container(
color: Colors.green
)
]
);
}
}
class MyPageView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyPageViewState();
}
}
然后在您的架构中:
const { specifiedRules } = require('graphql/validation')
const { GraphQLError } = require('graphql/error')
const typesToValidate = ['Foo', 'Bar']
// This returns a "Visitor" whose properties get called for
// each node in the document that matches the property's name
function CustomInputFieldsNonNull(context) {
return {
Argument(node) {
const argDef = context.getArgument();
const checkType = typesToValidate.includes(argDef.astNode.type.name.value)
if (checkType && node.value.kind === 'NullValue') {
context.reportError(
new GraphQLError(
`Type ${argDef.astNode.type.name.value} cannot be null`,
node,
),
)
}
},
}
}
// We're going to override the validation rules, so we want to grab
// the existing set of rules and just add on to it
const validationRules = specifiedRules.concat(CustomInputFieldsNonNull)
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules,
})
假设每次在架构中使用class NonNullInputDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
const { args: { paths } } = this
field.resolve = async function (...resolverArgs) {
const fieldArgs = resolverArgs[1]
for (const path of paths) {
if (_.get(fieldArgs, path) === null) {
throw new Error(`${path} cannot be null`)
}
}
return resolve.apply(this, resolverArgs)
}
}
}
时,“ non null”输入字段都是相同的,则可以将每个directive @nonNullInput(paths: [String!]!) on FIELD_DEFINITION
input FooInput {
foo: String
bar: String
}
type Query {
foo (input: FooInput!): String @nonNullInput(paths: ["input.foo"])
}
的名称映射到应为已验证。因此,您也可以执行以下操作:
input
然后在您的架构中:
input