GraphQL(四):GraphQL工程化實踐

Demo到產品化還有很長的路要走,尤爲是要在儘可能小的影響當前框架的前提下引入新的方法。html

GraphiQL

GraphiQL是整個GraphQL優點的重要一環,然而默認的GraphiQL不容許配置graphql服務的地址(就是點擊GraphiQL上的運行按鈕去請求數據的地址),要弄明白這一點很容易,找到graphiql-spring-boot-autoconfigure包,裏面有graphiql.html文件,請求數據的endpoint是硬編碼的:java

function graphQLFetcher(graphQLParams) {
    // This example expects a GraphQL server at the path /graphql.
    // Change this to point wherever you host your GraphQL server.
    return fetch('/graphql', {
        method: 'post',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(graphQLParams),
        credentials: 'include',
    }).then(function (response) {
        return response.text();
    }).then(function (responseBody) {
        try {
            return JSON.parse(responseBody);
        } catch (error) {
            return responseBody;
        }
    });
}
複製代碼

顯然這不能知足項目工程化的要求,解決這個問題有兩種比較簡單的方式:git

  1. 把graphiql.html文件複製到項目中,用一個Controller提供GraphiQL服務,這樣就能夠去掉GraphiQL的相關依賴了
  2. 利用官方的GraphiQL的React組件本身搭建GraphiQL頁面,這樣定製化更方便

GraphQL Voyager

強大的實體關係圖生成工具 github

image

GitHub已經提供了GraphQL的接口,實體關係圖能夠在GraphQL Voyager裏查看,Custom Schema容許提供本身的實體關係數據生成實體關係圖,簡直不能更好用。spring

身份認證和權限控制

GraphQL(三):GraphQL集成SpringBoot原理中提到GraphQL自己不帶身份認證和權限控制(這也確實不是它該作的事兒),可是它對查詢提供了回調方法(Instrumentation接口),在GraphQL.java文件中能夠看到這部分邏輯:sql

public ExecutionResult execute(String requestString, String operationName, Object context, Map<String, Object> arguments) {
        //執行回調
        InstrumentationContext<ExecutionResult> executionCtx = instrumentation.beginExecution(new ExecutionParameters(requestString, operationName, context, arguments));

        assertNotNull(arguments, "arguments can't be null");
        log.debug("Executing request. operation name: {}. Request: {} ", operationName, requestString);

        //解析回調
        InstrumentationContext<Document> parseCtx = instrumentation.beginParse(new ExecutionParameters(requestString, operationName, context, arguments));
        Parser parser = new Parser();
        Document document;
        try {
            document = parser.parseDocument(requestString);
            parseCtx.onEnd(document);
        } catch (ParseCancellationException e) {
            RecognitionException recognitionException = (RecognitionException) e.getCause();
            SourceLocation sourceLocation = new SourceLocation(recognitionException.getOffendingToken().getLine(), recognitionException.getOffendingToken().getCharPositionInLine());
            InvalidSyntaxError invalidSyntaxError = new InvalidSyntaxError(sourceLocation);
            return new ExecutionResultImpl(Collections.singletonList(invalidSyntaxError));
        }

        //驗證回調
        InstrumentationContext<List<ValidationError>> validationCtx = instrumentation.beginValidation(new ValidationParameters(requestString,operationName,context,arguments,document));

        Validator validator = new Validator();
        List<ValidationError> validationErrors = validator.validateDocument(graphQLSchema, document);

        validationCtx.onEnd(validationErrors);

        if (validationErrors.size() > 0) {
            return new ExecutionResultImpl(validationErrors);
        }
        ExecutionId executionId = idProvider.provide(requestString, operationName, context);

        Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation);
        ExecutionResult result = execution.execute(executionId, graphQLSchema, context, document, operationName, arguments);

        executionCtx.onEnd(result);

        return result;
    }
複製代碼

經過對每次執行GraphQL查詢進行攔截能夠實現相似SpringMVC攔截器的效果,可是須要本身實現SpringMVC中 @RequiredRole 註解的功能。json

GraphQL(二):GraphQL服務搭建中提到有兩種搭建GraphQL服務的方式,當時並無考慮身份認證和權限控制的問題,假如要在那兩種方法的基礎上加入身份認證和權限控制,有哪些成本呢?api

  • graphql-java + graphql-java-spring 此方案使用了SpringMVC的DispatcherServlet,因此徹底能夠複用原有的攔截器機制,權限認證須要基於Instrumentation本身實現,工做量大
  • graphql-spring-boot-starter + graphql-java-tools 此方案用了本身的GraphQLServlet,沒有包含攔截器機制,要加上身份認證能夠本身重寫GraphQLServlet加入攔截器機制,或者在Instrumentation接口上作文章,兩種方案都須要較大改動,且後期維護難度大,權限控制也須要基於Instrumentation本身實現

綜合來看: bash

image

ok,再想一想咱們的需求是什麼。app

  1. 對項目的現有流程改動小(最大化複用現有邏輯)
  2. 支持權限控制
  3. 自動解析schema
  4. 不用硬編碼、不要底層細節

這麼一看的話能夠得出這樣的方案:

image

能夠同時使用SpringMVC的攔截器和graphql-java-tools的優點。

彷佛這種方案能知足咱們的需求,可是有一個潛在的風險:

「A用戶容許訪問ApiA,ApiA可以訪問到實體A,可是A用戶沒有權限訪問實體A」

這時工程上就難以控制了,若是非要控制須要對實體進行權限,能作到,可是須要另一套設計,工做量也不小。

風險

工程化實踐時風險是必需要考慮的問題,GraphQL強大的自省功能(查詢整個實體圖的結構)能方便開發,也帶來了相應的風險,同時嵌套循環查詢、sql注入等問題也是須要防範的。

相關文章
相關標籤/搜索