如何为作为GraphQL参数的类设置全局默认值?
我不确定“全局默认值”是不是最佳表述,或许叫“默认的默认值”更合适?不管怎样,问题如下:
针对返回集合的GraphQL查询,我定义了如下PagingOptions类:
public class PagingOptions { public int start = 0; public int limit = 100; public SortOptions sort = null; }
示例查询方法为:
public Page<UserType> getUsers(@GraphQLArgument StandardCollectionArgs options)
PagingOptions字段的默认值已经在类声明里定义了,怎么把它设为查询中options参数的默认值?我可以用@GraphQLArgument(defaultValue="PagingOptions的JSON表示"),但每个用到该参数的地方都得重复配置,defaultValueProvider也是如此。有没有办法把默认值和作为GraphQLArgument的类关联起来?
可行解决方案
1. 自定义参数解析器(推荐)
实现GraphQL的参数解析逻辑,让所有PagingOptions类型的参数自动使用类的默认实例,不用逐个方法配置。
拿Spring GraphQL举例,自定义ArgumentValueResolver:
@Component public class PagingOptionsArgumentResolver implements ArgumentValueResolver { @Override public boolean supports(Argument argument) { return PagingOptions.class.isAssignableFrom(argument.getType().getJavaType()); } @Override public Object resolve(Argument argument, ValueFetchingEnvironment environment) { // 请求没传参数时,返回默认实例 Object providedValue = environment.getArgumentValue(); if (providedValue == null) { return new PagingOptions(); } // 传了部分参数的话,用默认值补全(可以用BeanUtils或MapStruct做属性拷贝) PagingOptions defaultOptions = new PagingOptions(); BeanUtils.copyProperties(providedValue, defaultOptions); return defaultOptions; } }
配置后,所有用到PagingOptions作为GraphQL参数的地方,都会自动套用类里的默认值,无需重复写配置。
2. 绑定默认值到GraphQL输入类型
直接在GraphQL的输入类型定义里把默认值和字段绑定,不管哪个查询用这个类型,都会继承这些默认值。
如果用SDL(Schema定义语言):
input PagingOptions { start: Int = 0 limit: Int = 100 sort: SortOptions } type Query { getUsers(options: PagingOptions = {}): Page<UserType> }
这里options: PagingOptions = {}会自动触发输入类型的字段默认值,对应到Java类的默认初始化值。
如果是代码优先的方式,直接在定义输入类型时指定默认值:
@Bean public GraphQLInputType pagingOptionsInputType() { return GraphQLInputObjectType.newInputObject() .name("PagingOptions") .field(field -> field.name("start").type(GraphQLInt).defaultValue(0)) .field(field -> field.name("limit").type(GraphQLInt).defaultValue(100)) .field(field -> field.name("sort").type(sortOptionsInputType())) .build(); }
3. 自定义注解+全局处理器
定义一个注解标记需要用默认实例的参数,再写全局处理器自动生成默认实例,扩展性更强,其他类也能复用。
先定义注解:
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface UseDefaultInstance { }
修改查询方法,给参数加注解:
public Page<UserType> getUsers(@GraphQLArgument @UseDefaultInstance PagingOptions options)
然后实现全局参数解析器:
@Component public class DefaultInstanceArgumentProcessor implements ArgumentValueResolver { @Override public boolean supports(Argument argument) { return argument.getAnnotations().stream() .anyMatch(anno -> anno.annotationType() == UseDefaultInstance.class); } @Override public Object resolve(Argument argument, ValueFetchingEnvironment environment) { try { Class<?> paramType = argument.getType().getJavaType(); return paramType.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new RuntimeException("无法创建默认实例", e); } } }
这样只要参数加了@UseDefaultInstance,就会自动用类的默认实例当参数默认值。
内容的提问来源于stack exchange,提问作者bjlevine

