首頁 > 軟體

從Hello World開始理解GraphQL背後處理及執行過程

2022-08-03 22:05:00

前言

在上篇文章《初識GraphQL》中我們大致的瞭解了GraphQL作用,並通過簡單範例初步體驗了GraphQL的使用。下面我們從Hello World開始來進一步瞭解GraphQL背後的處理。

Hello World

package com.graphqljava.tutorial.bookdetails;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.StaticDataFetcher;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
public class HelloWorld {
    public static void main(String[] args) {
        // 從最簡單的schema字串開始,省去對graphqls檔案的讀取
        String schema = "type Query{hello: String}";
        // 用於獲得graphql schema定義,並解析放入TypeDefinitionRegistry中,以便放置在SchemaGenerator中使用
        SchemaParser schemaParser = new SchemaParser();
        // 解析schema定義字串,並建立包含一組型別定義的TypeDefinitionRegistry
        TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema);
        // runtime wiring 是data fetchers、type resolves和客製化標量的規範,這些都需要連線到GraphQLSchema中
        RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
                // 新增一個型別連線
                .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world")))
                .build();
        //schemaGenerator物件可以使用typeDefinitionRegistry、runtimeWiring生成工作執行時schema
        SchemaGenerator schemaGenerator = new SchemaGenerator();
        //graphQLSchema代表graphql引擎的組合型別系統。
        GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
        //構建GraphQL用於執行查詢
        GraphQL build = GraphQL.newGraphQL(graphQLSchema).build();
        //執行並獲得結果
        ExecutionResult executionResult = build.execute("{hello}");
        System.out.println(executionResult.getData().toString());
    }
}

從上面的程式碼註釋可以看到GraphQL大致執行的過程:

  • 根據給定的schema內容使用SchemaParser進行解析獲得schema定義TypeDefinitionRegistry。
  • 拿到了schema定義之後還需要定義RuntimeWiring用於定義不同型別的type resolves和對應的資料提取器data fetchers。
  • 使用GraphQLSchema把TypeDefinitionRegistry和RuntimeWiring組合在一起便於以後的使用。
  • 使用GraphQLSchema構建出GraphQL用於後面的QL執行。
  • 傳入QL使用GraphQL執行並獲得結果ExecutionResult。

從外層使用程式碼可以得出核心處理類為:SchemaParser、TypeDefinitionRegistry、RuntimeWiring、GraphQLSchema、GraphQL。

下面我們分配看看核心類是怎麼處理的。

SchemaParser

解析schema字串定義並生成TypeDefinitionRegistry。

public TypeDefinitionRegistry parse(String schemaInput) throws SchemaProblem {
    try {
        Parser parser = new Parser();
        Document document = parser.parseDocument(schemaInput);
        return buildRegistry(document);
    } catch (ParseCancellationException e) {
        throw handleParseException(e);
    }
}

使用Document構建TypeDefinitionRegistry

public TypeDefinitionRegistry buildRegistry(Document document) {
    List<GraphQLError> errors = new ArrayList<>();
    TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
    List<Definition> definitions = document.getDefinitions();
    for (Definition definition : definitions) {
        if (definition instanceof SDLDefinition) {
            typeRegistry.add((SDLDefinition) definition).ifPresent(errors::add);
        }
    }
    if (errors.size() > 0) {
        throw new SchemaProblem(errors);
    } else {
        return typeRegistry;
    }
}

可以看的出來TypeDefinitionRegistry只是對Document的定義提取,重點還是在於Document的生成,我們可以先通過debugger來先看看Document的大致內容。

可以看到就是把schema字串解析成了方便後續使用的Document物件,我們還是詳細看看這個物件裡面的屬性和大概的生成過程。

Parser#parseDocument

public Document parseDocument(String input, String sourceName) {
    CharStream charStream;
    if(sourceName == null) {
        charStream = CharStreams.fromString(input);
    } else{
        charStream = CharStreams.fromString(input, sourceName);
    }
    GraphqlLexer lexer = new GraphqlLexer(charStream);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    GraphqlParser parser = new GraphqlParser(tokens);
    parser.removeErrorListeners();
    parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
    parser.setErrorHandler(new BailErrorStrategy());
    //詞法分析從schema中解析出tokens(每個關鍵字、最後一個為EOF),documentContext包含children、start/stop字元等相當於結構。
    GraphqlParser.DocumentContext documentContext = parser.document();
    GraphqlAntlrToLanguage antlrToLanguage = new GraphqlAntlrToLanguage(tokens);
    // 生成document
    Document doc = antlrToLanguage.createDocument(documentContext);
    Token stop = documentContext.getStop();
    List<Token> allTokens = tokens.getTokens();
    if (stop != null && allTokens != null && !allTokens.isEmpty()) {
        Token last = allTokens.get(allTokens.size() - 1);
        //
        // do we have more tokens in the stream than we consumed in the parse?
        // if yes then its invalid.  We make sure its the same channel
        boolean notEOF = last.getType() != Token.EOF;
        boolean lastGreaterThanDocument = last.getTokenIndex() > stop.getTokenIndex();
        boolean sameChannel = last.getChannel() == stop.getChannel();
        if (notEOF && lastGreaterThanDocument && sameChannel) {
            throw new ParseCancellationException("There are more tokens in the query that have not been consumed");
        }
    }
    return doc;
}

tokens&documentContext

可以看到,主要是通過提取schema的關鍵字、識別結構最後生成Document主要內容為型別定義定義和型別定義中的欄位定義。

RuntimeWiring

runtime wiring 是data fetchers、type resolves和客製化標量的規範,這些都需要連線到GraphQLSchema中。

RuntimeWiring.Builder#type

這種形式允許使用lambda作為type wiring的構建器。

public Builder type(String typeName, UnaryOperator<TypeRuntimeWiring.Builder> builderFunction) {
    TypeRuntimeWiring.Builder builder = builderFunction.apply(TypeRuntimeWiring.newTypeWiring(typeName));
    return type(builder.build());
}

新增type wiring。

public Builder type(TypeRuntimeWiring typeRuntimeWiring) {
    String typeName = typeRuntimeWiring.getTypeName();
    Map<String, DataFetcher> typeDataFetchers = dataFetchers.computeIfAbsent(typeName, k -> new LinkedHashMap<>());
    typeRuntimeWiring.getFieldDataFetchers().forEach(typeDataFetchers::put);
    defaultDataFetchers.put(typeName, typeRuntimeWiring.getDefaultDataFetcher());
    TypeResolver typeResolver = typeRuntimeWiring.getTypeResolver();
    if (typeResolver != null) {
        this.typeResolvers.put(typeName, typeResolver);
    }
    EnumValuesProvider enumValuesProvider = typeRuntimeWiring.getEnumValuesProvider();
    if (enumValuesProvider != null) {
        this.enumValuesProviders.put(typeName, enumValuesProvider);
    }
    return this;
}

可以看到主要就是網RuntimeWiring裡面新增了dataFetchers、defaultDataFetchers、typeResolvers、enumValuesProviders。下面分別介紹下各屬性的含義:

  • DataFetcher:負責返回給定graphql欄位資料值。graphql引擎使用datafetcher將邏輯欄位解析/獲取到執行時物件,該物件將作為整個graphql grapql.ExecutionResult的一部分傳送回來。

GraphQLScalarType:scalar type是graphql樹型別的葉節點。該型別允許你定義新的scalar type。

  • TypeResolver:這在型別解析期間被呼叫,以確定在執行時GraphQLInterfaceTypes和GraphQLUnionTypes應該動態使用哪些具體的GraphQLObjectType。
    • GraphQLInterfaceTypes:在graphql中,介面是一種抽象型別,它定義了一組欄位,型別必須包含這些欄位才能實現該介面。在執行時,TypeResolver用於獲取一個介面物件值,並決定哪個GraphQLObjectType表示此介面型別。關於這個概念的更多細節,請參見graphql.org/learn/schem…
    • GraphQLUnionTypes:聯合型別,相當於組合。
    • GraphQLObjectType:這是工作馬型別,表示一個物件,它具有一個或多個欄位值,這些欄位可以根據物件型別等進行自身的處理,直到到達由GraphQLScalarTypes表示的型別樹的葉節點。關於這個概念的更多細節,請參見graphql.org/learn/schem…
  • SchemaDirectiveWiring:SchemaDirectiveWiring負責基於schema定義語言(SDL)中放置在該元素上的指令增強執行時元素。它可以增強graphql執行時元素並新增新的行為,例如通過更改欄位graphql.schema. datafetcher。
  • WiringFactory:WiringFactory允許您基於IDL定義更動態的連線TypeResolvers和DataFetchers。
  • EnumValuesProvider:為每個graphql Enum值提供Java執行時值。用於IDL驅動的schema建立。Enum值被認為是靜態的:在建立schema時呼叫。在執行查詢時不使用。
  • GraphqlFieldVisibility:這允許您控制graphql欄位的可見性。預設情況下,graphql-java使每個定義的欄位可見,但您可以實現此介面的範例並減少特定欄位的可見性。

GraphQL

build

例子中通過傳入GraphQLSchema構建GraphQL。

public GraphQL build() {
    assertNotNull(graphQLSchema, "graphQLSchema must be non null");
    assertNotNull(queryExecutionStrategy, "queryStrategy must be non null");
    assertNotNull(idProvider, "idProvider must be non null");
    return new GraphQL(graphQLSchema, queryExecutionStrategy, mutationExecutionStrategy, subscriptionExecutionStrategy, idProvider, instrumentation, preparsedDocumentProvider);
}

除了graphQLSchema都是預設值,我們大概看看各個成員分別是用來幹嘛的:

  • queryExecutionStrategy:非同步非阻塞地執行欄位的標準graphql執行策略。
  • mutationExecutionStrategy:非同步非阻塞執行,但序列:當時只有一個欄位將被解析。關於每個欄位的非序列(並行)執行,請參閱AsyncExecutionStrategy。
  • subscriptionExecutionStrategy:通過使用reactive-streams作為訂閱查詢的輸出結果來實現graphql訂閱。
  • idProvider:executionid的提供者
  • instrumentation:提供了檢測GraphQL查詢執行步驟的功能。
  • preparsedDocumentProvider:使用者端連線檔案快取和/或查詢白名單的介面。

execute

下面我們還是來看看具體的執行:

public ExecutionResult execute(ExecutionInput executionInput) {
    try {
        return executeAsync(executionInput).join();
    } catch (CompletionException e) {
        if (e.getCause() instanceof RuntimeException) {
            throw (RuntimeException) e.getCause();
        } else {
            throw e;
        }
    }
}

用提供的輸入物件執行graphql query。這將返回一個承諾(又名CompletableFuture),以提供一個ExecutionResult,這是執行所提供查詢的結果。

public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionInput) {
    try {
        log.debug("Executing request. operation name: '{}'. query: '{}'. variables '{}'", executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables());
        // 建立InstrumentationState物件,這是一個跟蹤Instrumentation全生命週期的物件
        InstrumentationState instrumentationState = instrumentation.createState(new InstrumentationCreateStateParameters(this.graphQLSchema, executionInput));
        InstrumentationExecutionParameters inputInstrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState);
        // 檢測輸入物件
        executionInput = instrumentation.instrumentExecutionInput(executionInput, inputInstrumentationParameters);
        InstrumentationExecutionParameters instrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState);
        // 在執行檢測 chain前呼叫
        InstrumentationContext<ExecutionResult> executionInstrumentation = instrumentation.beginExecution(instrumentationParameters);
        // 檢測GraphQLSchema
        GraphQLSchema graphQLSchema = instrumentation.instrumentSchema(this.graphQLSchema, instrumentationParameters);
        // 對使用者端傳遞的query進行驗證並執行
        CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(executionInput, graphQLSchema, instrumentationState);
        //
        // finish up instrumentation
        executionResult = executionResult.whenComplete(executionInstrumentation::onCompleted);
        //
        // allow instrumentation to tweak the result
        executionResult = executionResult.thenCompose(result -> instrumentation.instrumentExecutionResult(result, instrumentationParameters));
        return executionResult;
    } catch (AbortExecutionException abortException) {
        return CompletableFuture.completedFuture(abortException.toExecutionResult());
    }
}

parseValidateAndExecute(executionInput, graphQLSchema, instrumentationState)進行驗證並執行,驗證我們就不看了直接看執行:

private CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {
    String query = executionInput.getQuery();
    String operationName = executionInput.getOperationName();
    Object context = executionInput.getContext();
    Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation);
    ExecutionId executionId = idProvider.provide(query, operationName, context);
    log.debug("Executing '{}'. operation name: '{}'. query: '{}'. variables '{}'", executionId, executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables());
    CompletableFuture<ExecutionResult> future = execution.execute(document, graphQLSchema, executionId, executionInput, instrumentationState);
    future = future.whenComplete((result, throwable) -> {
        if (throwable != null) {
            log.error(String.format("Execution '%s' threw exception when executing : query : '%s'. variables '%s'", executionId, executionInput.getQuery(), executionInput.getVariables()), throwable);
        } else {
            int errorCount = result.getErrors().size();
            if (errorCount > 0) {
                log.debug("Execution '{}' completed with '{}' errors", executionId, errorCount);
            } else {
                log.debug("Execution '{}' completed with zero errors", executionId);
            }
        }
    });
    return future;
}

這裡列印紀錄檔為

Executing '9c81e267-c55a-4ebd-9f9c-3a2270b28103'. operation name: 'null'. query: '{hello}'. variables '{}'

還要繼續往下看:

Execution#execute

public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSchema graphQLSchema, ExecutionId executionId, ExecutionInput executionInput, InstrumentationState instrumentationState) {
    // 獲得要執行的操作
    NodeUtil.GetOperationResult getOperationResult = NodeUtil.getOperation(document, executionInput.getOperationName());
    Map<String, FragmentDefinition> fragmentsByName = getOperationResult.fragmentsByName;
    OperationDefinition operationDefinition = getOperationResult.operationDefinition;
    ValuesResolver valuesResolver = new ValuesResolver();
    // 獲得輸入的引數
    Map<String, Object> inputVariables = executionInput.getVariables();
    List<VariableDefinition> variableDefinitions = operationDefinition.getVariableDefinitions();
    Map<String, Object> coercedVariables;
    try {
        coercedVariables = valuesResolver.coerceArgumentValues(graphQLSchema, variableDefinitions, inputVariables);
    } catch (RuntimeException rte) {
        if (rte instanceof GraphQLError) {
            return completedFuture(new ExecutionResultImpl((GraphQLError) rte));
        }
        throw rte;
    }
    ExecutionContext executionContext = newExecutionContextBuilder()
            .instrumentation(instrumentation)
            .instrumentationState(instrumentationState)
            .executionId(executionId)
            .graphQLSchema(graphQLSchema)
            .queryStrategy(queryStrategy)
            .mutationStrategy(mutationStrategy)
            .subscriptionStrategy(subscriptionStrategy)
            .context(executionInput.getContext())
            .root(executionInput.getRoot())
            .fragmentsByName(fragmentsByName)
            .variables(coercedVariables)
            .document(document)
            .operationDefinition(operationDefinition)
            // 放入dataloder
            .dataLoaderRegistry(executionInput.getDataLoaderRegistry())
            .build();
    InstrumentationExecutionParameters parameters = new InstrumentationExecutionParameters(
            executionInput, graphQLSchema, instrumentationState
    );
    // 獲得執行上下文
    executionContext = instrumentation.instrumentExecutionContext(executionContext, parameters);
    return executeOperation(executionContext, parameters, executionInput.getRoot(), executionContext.getOperationDefinition());
}

獲得了執行上下文並執行,下面繼續看executeOperation

private CompletableFuture<ExecutionResult> executeOperation(ExecutionContext executionContext, InstrumentationExecutionParameters instrumentationExecutionParameters, Object root, OperationDefinition operationDefinition) {
    // ...
    ExecutionStrategyParameters parameters = newParameters()
            .executionStepInfo(executionStepInfo)
            .source(root)
            .fields(fields)
            .nonNullFieldValidator(nonNullableFieldValidator)
            .path(path)
            .build();
    CompletableFuture<ExecutionResult> result;
    try {
        ExecutionStrategy executionStrategy;
        if (operation == OperationDefinition.Operation.MUTATION) {
            executionStrategy = mutationStrategy;
        } else if (operation == SUBSCRIPTION) {
            executionStrategy = subscriptionStrategy;
        } else {
            executionStrategy = queryStrategy;
        }
        log.debug("Executing '{}' query operation: '{}' using '{}' execution strategy", executionContext.getExecutionId(), operation, executionStrategy.getClass().getName());
        result = executionStrategy.execute(executionContext, parameters);
    } catch (NonNullableFieldWasNullException e) {
          // ...
    }
    // ...
    return deferSupport(executionContext, result);
}

紀錄檔輸出:

Executing '9c81e267-c55a-4ebd-9f9c-3a2270b28103' query operation: 'QUERY' using 'graphql.execution.AsyncExecutionStrategy' execution strategy

最終使用AsyncExecutionStrategy策略執行,繼續往下看:

AsynExecutionStrategy#execute

public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
    Instrumentation instrumentation = executionContext.getInstrumentation();
    InstrumentationExecutionStrategyParameters instrumentationParameters = new InstrumentationExecutionStrategyParameters(executionContext, parameters);
    ExecutionStrategyInstrumentationContext executionStrategyCtx = instrumentation.beginExecutionStrategy(instrumentationParameters);
    Map<String, List<Field>> fields = parameters.getFields();
    // 欄位名稱
    List<String> fieldNames = new ArrayList<>(fields.keySet());
    List<CompletableFuture<FieldValueInfo>> futures = new ArrayList<>();
    List<String> resolvedFields = new ArrayList<>();
    for (String fieldName : fieldNames) {
        List<Field> currentField = fields.get(fieldName);
        ExecutionPath fieldPath = parameters.getPath().segment(mkNameForPath(currentField));
        ExecutionStrategyParameters newParameters = parameters
                .transform(builder -> builder.field(currentField).path(fieldPath).parent(parameters));
        if (isDeferred(executionContext, newParameters, currentField)) {
            executionStrategyCtx.onDeferredField(currentField);
            continue;
        }
        resolvedFields.add(fieldName);
        // 處理欄位,這裡處理的是"hello"
        CompletableFuture<FieldValueInfo> future = resolveFieldWithInfo(executionContext, newParameters);
        futures.add(future);
    }
    CompletableFuture<ExecutionResult> overallResult = new CompletableFuture<>();
    executionStrategyCtx.onDispatched(overallResult);
    //並行執行所有filed處理的futures
    Async.each(futures).whenComplete((completeValueInfos, throwable) -> {
        BiConsumer<List<ExecutionResult>, Throwable> handleResultsConsumer = handleResults(executionContext, resolvedFields, overallResult);
        if (throwable != null) {
            handleResultsConsumer.accept(null, throwable.getCause());
            return;
        }
        List<CompletableFuture<ExecutionResult>> executionResultFuture = completeValueInfos.stream().map(FieldValueInfo::getFieldValue).collect(Collectors.toList());
        executionStrategyCtx.onFieldValuesInfo(completeValueInfos);
        Async.each(executionResultFuture).whenComplete(handleResultsConsumer);
    }).exceptionally((ex) -> {
        // if there are any issues with combining/handling the field results,
        // complete the future at all costs and bubble up any thrown exception so
        // the execution does not hang.
        overallResult.completeExceptionally(ex);
        return null;
    });
    overallResult.whenComplete(executionStrategyCtx::onCompleted);
    return overallResult;
}

可以看到這裡會遍歷所有fileds拿到每個filed future,最後並行執行,下面具體看看:

ExecutionStrategy#resolveFieldWithInfo

呼叫該函數來獲取欄位的值及額外的執行時資訊,並根據graphql query內容進一步處理它。

protected CompletableFuture<FieldValueInfo> resolveFieldWithInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
    GraphQLFieldDefinition fieldDef = getFieldDef(executionContext, parameters, parameters.getField().get(0));
    Instrumentation instrumentation = executionContext.getInstrumentation();
    InstrumentationContext<ExecutionResult> fieldCtx = instrumentation.beginField(
            new InstrumentationFieldParameters(executionContext, fieldDef, createExecutionStepInfo(executionContext, parameters, fieldDef))
    );
    CompletableFuture<Object> fetchFieldFuture = fetchField(executionContext, parameters);
    CompletableFuture<FieldValueInfo> result = fetchFieldFuture.thenApply((fetchedValue) ->
            completeField(executionContext, parameters, fetchedValue));
    CompletableFuture<ExecutionResult> executionResultFuture = result.thenCompose(FieldValueInfo::getFieldValue);
    fieldCtx.onDispatched(executionResultFuture);
    executionResultFuture.whenComplete(fieldCtx::onCompleted);
    return result;
}

呼叫該函數獲取filed值,使用從filed GraphQlFiledDefinition關聯的DataFetcher。

protected CompletableFuture<Object> fetchField(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
    Field field = parameters.getField().get(0);
    GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType();
    GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field);
    GraphqlFieldVisibility fieldVisibility = executionContext.getGraphQLSchema().getFieldVisibility();
    Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldVisibility, fieldDef.getArguments(), field.getArguments(), executionContext.getVariables());
    GraphQLOutputType fieldType = fieldDef.getType();
    DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, parameters.getField());
    // ...
    CompletableFuture<Object> fetchedValue;
    // 獲得dataFetcher,這裡為HelloWorld的`new StaticDataFetcher("world")`
    DataFetcher dataFetcher = fieldDef.getDataFetcher();
    dataFetcher = instrumentation.instrumentDataFetcher(dataFetcher, instrumentationFieldFetchParams);
    ExecutionId executionId = executionContext.getExecutionId();
    try {
        log.debug("'{}' fetching field '{}' using data fetcher '{}'...", executionId, executionStepInfo.getPath(), dataFetcher.getClass().getName());
        // 執行dataFetcher獲取值,enviroment為上下文環境包含引數
        Object fetchedValueRaw = dataFetcher.get(environment);
        log.debug("'{}' field '{}' fetch returned '{}'", executionId, executionStepInfo.getPath(), fetchedValueRaw == null ? "null" : fetchedValueRaw.getClass().getName());
        // 如果是具體值就返回已經有值的CompletableFuture,如果是CompletionStage就直接返回
        fetchedValue = Async.toCompletableFuture(fetchedValueRaw);
    } catch (Exception e) {
        log.debug(String.format("'%s', field '%s' fetch threw exception", executionId, executionStepInfo.getPath()), e);
        fetchedValue = new CompletableFuture<>();
        fetchedValue.completeExceptionally(e);
    }
    fetchCtx.onDispatched(fetchedValue);
    // 對結果的後續處理
    return fetchedValue
            .handle((result, exception) -> {
                fetchCtx.onCompleted(result, exception);
                if (exception != null) {
                    handleFetchingException(executionContext, parameters, field, fieldDef, argumentValues, environment, exception);
                    return null;
                } else {
                    return result;
                }
            })
            .thenApply(result -> unboxPossibleDataFetcherResult(executionContext, parameters, result))
            .thenApply(this::unboxPossibleOptional);
}

總體執行過程

以上就是從Hello World開始理解GraphQL背後處理的詳細內容,更多關於GraphQL處理的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com