gpt4 book ai didi

java - 如何将自定义指令添加到通过单例解决的查询中

转载 作者:行者123 更新时间:2023-11-30 06:05:12 28 4
gpt4 key购买 nike

我设法将自定义指令添加到GraphQL架构,但是我在努力找出如何向字段定义中添加自定义指令。关于正确实现的任何提示将非常有帮助。
我正在使用GraphQL SPQR 0.9.6生成我的架构

最佳答案

目前无法执行此操作。 GraphQL SPQR v0.9.9将首先支持自定义指令。

不过,在0.9.8中,可能有一种解决方法,具体取决于您要实现的目标。 SPQR自己有关字段或类型的元数据保存在自定义指令中。知道了这一点,您可以掌握GraphQL字段定义下面的Java方法/字段。如果您想要的是一个基于指令执行某项操作的工具,您可以取而代之获得底层元素的任何注释,从而拥有Java的全部功能。

获取该方法的方法类似于:

Operation operation = Directives.getMappedOperation(env.getField()).get();
Resolver resolver = operation.getApplicableResolver(env.getArguments().keySet());
Member underlyingElement = resolver.getExecutable().getDelegate();


更新:
我在 this GitHub issue上发布了一个巨大的答案。也将其粘贴到此处。

您可以这样注册其他指令:

generator.withSchemaProcessors(
(schemaBuilder, buildContext) -> schemaBuilder.additionalDirective(...));


但是(根据我目前的理解),这仅对查询指令有意义(客户端作为查询的一部分发送的内容,例如 @skip@deffered)。

@dateFormat这样的指令在SPQR中根本没有任何意义:它们在解析SDL并将其映射到您的代码时可以为您提供帮助。在SPQR中,没有SDL,您可以从代码开始。
例如。 @dateFormat用于告诉您在将其映射到Java时需要为特定字段提供日期格式。在SPQR中,您从Java部分开始,并且GraphQL字段是从Java方法生成的,因此该方法必须已经知道应返回的格式。或者它已经具有适当的注释。在SPQR中,Java是真理的源头。您可以使用注释来提供额外的映射信息。指令基本上是SDL中的注释。

尽管如此,字段或类型级别的指令(或注释)在检测中还是非常有用的。例如。如果要拦截字段解析并检查身份验证指令。
在这种情况下,我建议您仅将注释用于相同的目的。

public class BookService {

@Auth(roles= {"Admin"}) //example custom annotation
public Book addBook(Book book) { /*insert a Book into the DB */ }
}


由于每个GraphQLFieldDefinition都由Java方法(或字段)支持,因此您可以在拦截器中或任何地方获取基础对象:

GraphQLFieldDefinition field = ...;
Operation operation = Directives.getMappedOperation(field).get();

//Multiple methods can be hooked up to a single GraphQL operation. This gets the @Auth annotations from all of them
Set<Auth> allAuthAnnotations = operation.getResolvers().stream()
.map(res -> res.getExecutable().getDelegate()) //get the underlying method
.filter(method -> method.isAnnotationPresent(Auth.class))
.map(method -> method.getAnnotation(Auth.class))
.collect(Collectors.toSet());


或者,仅检查可以处理当前请求的方法:

DataFetchingEnvironment env = ...; //get it from the instrumentation params      
Auth auth = operation.getApplicableResolver(env.getArguments().keySet()).getExecutable().getDelegate().getAnnotation(Auth.class);


然后,您可以根据需要检查注释,例如

Set<String> allNeededRoles = allAuthAnnotations.stream()
.flatMap(auth -> Arrays.stream(auth.roles))
.collect(Collectors.toSet());

if (!currentUser.getRoles().containsAll(allNeededRoles)) {
throw new AccessDeniedException(); //or whatever is appropriate
}


当然,实际上并不需要真正以这种方式实现身份验证,因为您可能正在使用像Spring或Guice这样的框架(也许甚至Jersey都具有所需的安全性功能),该框架已经可以拦截所有方法并实现安全性。因此,您可以只使用它。更简单,更安全。例如。对于Spring Security,只需继续正常使用即可:

public class BookService {

@PreAuth(...) //standard Spring Security
public Book addBook(Book book) { /*insert a Book into the DB */ }
}


如果您要这样做,请确保还阅读 my answer on implementing security in GraphQL

您可以使用工具以相同的方式动态过滤结果:在方法上添加注释,从工具访问它并动态处理结果:

public class BookService {

@Filter("title ~ 'Monkey'") //example custom annotation
public List<Book> findBooks(...) { /*get books from the DB */ }
}

new SimpleInstrumentation() {

// You can also use beginFieldFetch and then onCompleted instead of instrumentDataFetcher
@Override
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
GraphQLFieldDefinition field = parameters.getEnvironment().getFieldDefinition();
Optional<String> filterExpression = Directives.getMappedOperation(field)
.map(operation ->
operation.getApplicableResolver(parameters.getEnvironment().getArguments().keySet())
.getExecutable().getDelegate()
.getAnnotation(Filter.class).value()); //get the filtering expression from the annotation
return filterExpression.isPresent() ? env -> filterResultBasedOn Expression(dataFetcher.get(parameters.getEnvironment()), filterExpression) : dataFetcher;
}
}


同样,对于类型的指令,只需使用Java批注。您可以通过以下方式访问基础类型:

Directives.getMappedType(graphQLType).getAnnotation(...);


同样,这可能仅在仪器中才有意义。之所以这样说是因为通常情况下,指令会提供额外的信息以将SDL映射到GraphQL类型。在SPQR中,您将Java类型映射到GraphQL类型,因此在大多数情况下,在该上下文中,指令没有任何意义。

当然,如果您仍然需要某个类型上的实际GraphQL指令,则可以始终提供一个自定义的 TypeMapper并将其放置在此处。

对于字段中的指令,当前在0.9.8中是不可能的。

如果您仍然需要0.9.9,它将对任何元素提供完全的自定义指令支持。

更新2: GraphQL SPQR 0.9.9已退出。

现在支持自定义指令。有关详细信息,请参见问题 #200

使用 @GraphQLDirective进行元注释的任何自定义注释都将作为指令映射到被注释的元素上。

例如。想象一个用于表示访问限制的自定义注释 @Auth(requiredRole = "Admin")

@GraphQLDirective //Should be mapped as a GraphQLDirective
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) //Applicable to methods
public @interface Auth {
String requiredRole();
}


如果使用 @Auth注释了一个解析器方法:

@GraphQLMutation
@Auth(requiredRole = {"Admin"})
public Book addBook(Book newBook) { ... }


产生的GraphQL字段填充如下所示:

type Mutation {
addBook(newBook: BookInput): Book @auth(requiredRole : "Admin")
}


也就是说,由于 @Auth元注释的存在, @GraphQLDirective注释已映射到指令。

可以通过以下方式添加客户端指令: GraphQLSchemaGenerator#withAdditionalDirectives(java.lang.reflect.Type...)

SPQR 0.9.9还带有 ResolverInterceptor,可以拦截解析器方法调用并检查注释/指令。它们比 Instrumentation更加方便使用,但不那么通用(范围要有限得多)。有关详细信息,请参见问题 #180,有关用法示例,请参见 related tests

例如。从上面使用 @Auth注释(不是 @Auth不必为此工作的指令):

public class AuthInterceptor implements ResolverInterceptor {

@Override
public Object aroundInvoke(InvocationContext context, Continuation continuation) throws Exception {
Auth auth = context.getResolver().getExecutable().getDelegate().getAnnotation(Auth.class);
User currentUser = context.getResolutionEnvironment().dataFetchingEnvironment.getContext();
if (auth != null && !currentUser.getRoles().containsAll(Arrays.asList(auth.rolesRequired()))) {
throw new IllegalAccessException("Access denied"); // or return null
}
return continuation.proceed(context);
}
}


如果 @Auth是指令,您还可以通过常规API获取它,例如

List<GraphQLDirective> directives = dataFetchingEnvironment.getFieldDefinition().get.getDirectives();
DirectivesUtil.directivesByName(directives);

关于java - 如何将自定义指令添加到通过单例解决的查询中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51486035/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com