You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:06:08