美文网首页graphql-java 手册
graphql-java使用手册:part7 拦截器Instru

graphql-java使用手册:part7 拦截器Instru

作者: MarkZhu | 来源:发表于2017-11-17 23:40 被阅读0次

    原文:http://blog.mygraphql.com/wordpress/?p=112

    拦截器Instrumentation

    通过实现 graphql.execution.instrumentation.Instrumentation
    接口,你可以在执行查询的过程中注入定制代码。并可以修改运行期的行为。

    它的主要用途是性能监控和定制日志,但也可以完成其它任务。

    当创建 `Graphql 对象时,可以绑定相关的 Instrumentation

    GraphQL.newGraphQL(schema)
            .instrumentation(new TracingInstrumentation())
            .build();
    

    定制拦截器(Custom Instrumentation)

    要实现 Instrumentation ,需要实现多个 “begin”
    开头的方法。这方法会在查询执行过程中,每一步骤开始前被调用。

    所有回调方法,都应该返回
    graphql.execution.instrumentation.InstrumentationContext
    对象,这个对象会在本步骤完成时被回调用,回调用时会告知数据的获取结果,如果出错,可以获取
    Throwable 对象。.

    下面是一个定制的 Instrumentation 。作用是测量执行时间。

    class CustomInstrumentationState implements InstrumentationState {
        private Map<String, Object> anyStateYouLike = new HashMap<>();
    
        void recordTiming(String key, long time) {
            anyStateYouLike.put(key, time);
        }
    }
    
    class CustomInstrumentation implements Instrumentation {
        @Override
        public InstrumentationState createState() {
            //
            // instrumentation state is passed during each invocation of an Instrumentation method
            // and allows you to put stateful data away and reference it during the query execution
            //
            return new CustomInstrumentationState();
        }
    
        @Override
        public InstrumentationContext<ExecutionResult> beginExecution(InstrumentationExecutionParameters parameters) {
            long startNanos = System.nanoTime();
            return (result, throwable) -> {
    
                CustomInstrumentationState state = parameters.getInstrumentationState();
                state.recordTiming(parameters.getQuery(), System.nanoTime() - startNanos);
            };
        }
    
        @Override
        public InstrumentationContext<Document> beginParse(InstrumentationExecutionParameters parameters) {
            //
            // You MUST return a non null object but it does not have to do anything and hence
            // you use this class to return a no-op object
            //
            return new NoOpInstrumentation.NoOpInstrumentationContext<>();
        }
    
        @Override
        public InstrumentationContext<List<ValidationError>> beginValidation(InstrumentationValidationParameters parameters) {
            return new NoOpInstrumentation.NoOpInstrumentationContext<>();
        }
    
        @Override
        public InstrumentationContext<ExecutionResult> beginDataFetch(InstrumentationDataFetchParameters parameters) {
            return new NoOpInstrumentation.NoOpInstrumentationContext<>();
        }
    
        @Override
        public InstrumentationContext<CompletableFuture<ExecutionResult>> beginExecutionStrategy(InstrumentationExecutionStrategyParameters parameters) {
            return new NoOpInstrumentation.NoOpInstrumentationContext<>();
        }
    
        @Override
        public InstrumentationContext<ExecutionResult> beginField(InstrumentationFieldParameters parameters) {
            return new NoOpInstrumentation.NoOpInstrumentationContext<>();
        }
    
        @Override
        public InstrumentationContext<Object> beginFieldFetch(InstrumentationFieldFetchParameters parameters) {
            return new NoOpInstrumentation.NoOpInstrumentationContext<>();
        }
    
        @Override
        public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
            //
            // this allows you to intercept the data fetcher used ot fetch a field and provide another one, perhaps
            // that enforces certain behaviours or has certain side effects on the data
            //
            return dataFetcher;
        }
    
        @Override
        public CompletableFuture<ExecutionResult> instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) {
            //
            // this allows you to instrument the execution result some how.  For example the Tracing support uses this to put
            // the `extensions` map of data in place
            //
            return CompletableFuture.completedFuture(executionResult);
        }
    }
    

    链式拦截(Chaining Instrumentation)

    你可以用 graphql.execution.instrumentation.ChainedInstrumentation
    把多个 Instrumentation 连接起来。这些 Instrumentation
    对象会按顺序被调用。

    List<Instrumentation> chainedList = new ArrayList<>();
    chainedList.add(new FooInstrumentation());
    chainedList.add(new BarInstrumentation());
    ChainedInstrumentation chainedInstrumentation = new ChainedInstrumentation(chainedList);
    
    GraphQL.newGraphQL(schema)
            .instrumentation(chainedInstrumentation)
            .build();
    

    Apollo跟踪与拦截( Tracing Instrumentation)

    graphql.execution.instrumentation.tracing.TracingInstrumentation
    是一个可以收集跟踪信息的拦截器。

    它按照 Apollo 跟踪格式 https://github.com/apollographql/apollo-tracing
    来收集跟踪信息。

    详细的跟踪信息( tracing map)会放在查询结果的 extensions(扩展)
    部分。

    如以下的查询:

    query {
      hero {
        name
        friends {
          name
        }
      }
    }
    

    会返回如下的结果:

    {
      "data": {
        "hero": {
          "name": "R2-D2",
          "friends": [
            {
              "name": "Luke Skywalker"
            },
            {
              "name": "Han Solo"
            },
            {
              "name": "Leia Organa"
            }
          ]
        }
      },
      "extensions": {
        "tracing": {
          "version": 1,
          "startTime": "2017-08-14T23:13:39.362Z",
          "endTime": "2017-08-14T23:13:39.497Z",
          "duration": 135589186,
          "execution": {
            "resolvers": [
              {
                "path": [
                  "hero"
                ],
                "parentType": "Query",
                "returnType": "Character",
                "fieldName": "hero",
                "startOffset": 105697585,
                "duration": 79111240
              },
              {
                "path": [
                  "hero",
                  "name"
                ],
                "parentType": "Droid",
                "returnType": "String",
                "fieldName": "name",
                "startOffset": 125010028,
                "duration": 20213
              },
              {
                "path": [
                  "hero",
                  "friends"
                ],
                "parentType": "Droid",
                "returnType": "[Character]",
                "fieldName": "friends",
                "startOffset": 133352819,
                "duration": 7927560
              },
              {
                "path": [
                  "hero",
                  "friends",
                  0,
                  "name"
                ],
                "parentType": "Human",
                "returnType": "String",
                "fieldName": "name",
                "startOffset": 134105887,
                "duration": 6783
              },
              {
                "path": [
                  "hero",
                  "friends",
                  1,
                  "name"
                ],
                "parentType": "Human",
                "returnType": "String",
                "fieldName": "name",
                "startOffset": 134725922,
                "duration": 7016
              },
              {
                "path": [
                  "hero",
                  "friends",
                  2,
                  "name"
                ],
                "parentType": "Human",
                "returnType": "String",
                "fieldName": "name",
                "startOffset": 134875089,
                "duration": 6342
              }
            ]
          }
        }
      }
    }
    

    字段校验拦截器(Field Validation Instrumentation)

    graphql.execution.instrumentation.fieldvalidation.FieldValidationInstrumentation
    拦截器,可以在执行查询前校验字段和字段参数。如果校验失败,查询将停止,并返回错误信息。

    你可以编写自己的FieldValidation 实现,或者直接用
    SimpleFieldValidation 去为每个field定义校验逻辑。

    ExecutionPath fieldPath = ExecutionPath.parse("/user");
    FieldValidation fieldValidation = new SimpleFieldValidation()
            .addRule(fieldPath, new BiFunction<FieldAndArguments, FieldValidationEnvironment, Optional<GraphQLError>>() {
                @Override
                public Optional<GraphQLError> apply(FieldAndArguments fieldAndArguments, FieldValidationEnvironment environment) {
                    String nameArg = fieldAndArguments.getFieldArgument("name");
                    if (nameArg.length() > 255) {
                        return Optional.of(environment.mkError("Invalid user name", fieldAndArguments));
                    }
                    return Optional.empty();
                }
            });
    
    FieldValidationInstrumentation instrumentation = new FieldValidationInstrumentation(
            fieldValidation
    );
    
    GraphQL.newGraphQL(schema)
            .instrumentation(instrumentation)
            .build();
    

    相关文章

      网友评论

        本文标题:graphql-java使用手册:part7 拦截器Instru

        本文链接:https://www.haomeiwen.com/subject/cbvsvxtx.html