如何在GraphQL DGS中为输入类型实现@trim自定义指令?
在GraphQL DGS中实现支持输入类型的@trim自定义指令
问题背景
我在Java Spring Boot的GraphQL DGS中创建了一个用于输入类型转换/验证的自定义指令@trim,Schema声明如下:
directive @trim on FIELD_DEFINITION | INPUT_FIELD_DEFINITION type Subscriber { id: ID name: String @trim email: String @trim } input SubscriberInput { name: String @trim email: String @trim }
@trim指令在Subscriber类型上可以正常工作,参考官方示例实现的代码如下:
@DgsDirective(name = "trim") public class TrimDirective implements SchemaDirectiveWiring { @Override public GraphQLFieldDefinition onField(SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> env) { GraphQLObjectType fieldsContainer = (GraphQLObjectType) env.getFieldsContainer(); GraphQLFieldDefinition fieldDefinition = env.getFieldDefinition(); DataFetcher<?> originalDataFetcher = env.getCodeRegistry().getDataFetcher(fieldsContainer, fieldDefinition); DataFetcher<?> dataFetcher = DataFetcherFactories.wrapDataFetcher( originalDataFetcher, (dataFetchingEnvironment, value) -> { if (value instanceof String) { return ((String) value).trim(); } return value; }); env.getCodeRegistry().dataFetcher(fieldsContainer, fieldDefinition, dataFetcher); return fieldDefinition; } }
但尝试重写onInputObjectField方法以支持SubscriberInput输入类型时,env.getFieldsContainer()和env.getFieldDefinition()返回null,无法实现和onField方法相同的效果。
解决方案
输入对象字段的处理逻辑和普通对象字段不同,不能直接复用onField的方式,需要通过**修改输入对象字段的转换器(Coercing)**来实现字符串修剪逻辑。具体实现如下:
@DgsDirective(name = "trim") public class TrimDirective implements SchemaDirectiveWiring { // 处理普通对象字段(保留原有正常逻辑) @Override public GraphQLFieldDefinition onField(SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> env) { GraphQLObjectType fieldsContainer = (GraphQLObjectType) env.getFieldsContainer(); GraphQLFieldDefinition fieldDefinition = env.getFieldDefinition(); DataFetcher<?> originalDataFetcher = env.getCodeRegistry().getDataFetcher(fieldsContainer, fieldDefinition); DataFetcher<?> dataFetcher = DataFetcherFactories.wrapDataFetcher( originalDataFetcher, (dataFetchingEnvironment, value) -> { if (value instanceof String) { return ((String) value).trim(); } return value; }); env.getCodeRegistry().dataFetcher(fieldsContainer, fieldDefinition, dataFetcher); return fieldDefinition; } // 处理输入对象字段 @Override public GraphQLInputObjectField onInputObjectField(SchemaDirectiveWiringEnvironment<GraphQLInputObjectField> env) { GraphQLInputObjectType inputContainer = (GraphQLInputObjectType) env.getFieldsContainer(); GraphQLInputObjectField inputField = env.getFieldDefinition(); // 获取原有的String类型Scalar GraphQLScalarType originalScalar = (GraphQLScalarType) inputField.getType().unwrap(); // 自定义Coercing,在解析输入时执行trim操作 Coercing<String, String> trimCoercing = new Coercing<>() { @Override public String serialize(Object dataFetcherResult) throws CoercingSerializeException { // 序列化阶段复用原有逻辑,不修改输出 return originalScalar.getCoercing().serialize(dataFetcherResult); } @Override public String parseValue(Object input) throws CoercingParseValueException { // 处理客户端通过变量传入的字符串 Object parsed = originalScalar.getCoercing().parseValue(input); return parsed instanceof String ? ((String) parsed).trim() : null; } @Override public String parseLiteral(Object input) throws CoercingParseLiteralException { // 处理查询字符串中的字面量字符串 Object parsed = originalScalar.getCoercing().parseLiteral(input); return parsed instanceof String ? ((String) parsed).trim() : null; } }; // 构建新的Scalar类型,替换原有Coercing GraphQLScalarType trimmedScalar = GraphQLScalarType.newScalar() .name(originalScalar.getName()) .description(originalScalar.getDescription()) .coercing(trimCoercing) .build(); // 创建新的输入字段,使用自定义Scalar GraphQLInputObjectField newInputField = GraphQLInputObjectField.newInputObjectField(inputField) .type(trimmedScalar) .build(); // 更新Schema中的输入对象字段 env.getCodeRegistry().replaceInputObjectField(inputContainer, inputField, newInputField); return newInputField; } }
关键说明
- 输入对象字段的处理发生在输入解析阶段,而非数据获取阶段,因此需要修改字段类型的
Coercing逻辑,而非包裹DataFetcher parseValue和parseLiteral分别处理两种输入场景:客户端变量传入的字符串、查询语句中的字面量字符串,两者都需要实现trim逻辑- 序列化阶段复用原有Scalar的逻辑,避免影响输出结果的格式
内容的提问来源于stack exchange,提问作者titanium
相关产品推荐
相关产品推荐

