GraphQL(四):GraphQL工程化實(shí)踐

Demo到產(chǎn)品化還有很長(zhǎng)的路要走,尤其是要在盡量小的影響當(dāng)前框架的前提下引入新的方法危尿。

GraphiQL

GraphiQL是整個(gè)GraphQL優(yōu)勢(shì)的重要一環(huán),然而默認(rèn)的GraphiQL不允許配置graphql服務(wù)的地址(就是點(diǎn)擊GraphiQL上的運(yùn)行按鈕去請(qǐng)求數(shù)據(jù)的地址)馁痴,要弄明白這一點(diǎn)很容易谊娇,找到graphiql-spring-boot-autoconfigure包,里面有g(shù)raphiql.html文件,請(qǐng)求數(shù)據(jù)的endpoint是硬編碼的:

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;
        }
    });
}

顯然這不能滿足項(xiàng)目工程化的要求济欢,解決這個(gè)問(wèn)題有兩種比較簡(jiǎn)單的方式:

  1. 把graphiql.html文件復(fù)制到項(xiàng)目中赠堵,用一個(gè)Controller提供GraphiQL服務(wù),這樣就可以去掉GraphiQL的相關(guān)依賴了
  2. 利用官方的GraphiQL的React組件自己搭建GraphiQL頁(yè)面法褥,這樣定制化更方便

GraphQL Voyager

強(qiáng)大的實(shí)體關(guān)系圖生成工具

image

GitHub已經(jīng)提供了GraphQL的接口茫叭,實(shí)體關(guān)系圖可以在GraphQL Voyager里查看,Custom Schema允許提供自己的實(shí)體關(guān)系數(shù)據(jù)生成實(shí)體關(guān)系圖半等,簡(jiǎn)直不能更好用揍愁。

身份認(rèn)證和權(quán)限控制

GraphQL(三):GraphQL集成SpringBoot原理中提到GraphQL本身不帶身份認(rèn)證和權(quán)限控制(這也確實(shí)不是它該做的事兒),但是它對(duì)查詢提供了回調(diào)方法(Instrumentation接口)杀饵,在GraphQL.java文件中可以看到這部分邏輯:

 public ExecutionResult execute(String requestString, String operationName, Object context, Map<String, Object> arguments) {
        //執(zhí)行回調(diào)
        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);

        //解析回調(diào)
        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));
        }

        //驗(yàn)證回調(diào)
        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;
    }

通過(guò)對(duì)每次執(zhí)行GraphQL查詢進(jìn)行攔截可以實(shí)現(xiàn)類(lèi)似SpringMVC攔截器的效果莽囤,但是需要自己實(shí)現(xiàn)SpringMVC中 @RequiredRole 注解的功能。

GraphQL(二):GraphQL服務(wù)搭建中提到有兩種搭建GraphQL服務(wù)的方式切距,當(dāng)時(shí)并沒(méi)有考慮身份認(rèn)證和權(quán)限控制的問(wèn)題朽缎,假如要在那兩種方法的基礎(chǔ)上加入身份認(rèn)證和權(quán)限控制,有哪些成本呢谜悟?

  • graphql-java + graphql-java-spring
    此方案使用了SpringMVC的DispatcherServlet话肖,所以完全可以復(fù)用原有的攔截器機(jī)制,權(quán)限認(rèn)證需要基于Instrumentation自己實(shí)現(xiàn)葡幸,工作量大
  • graphql-spring-boot-starter + graphql-java-tools
    此方案用了自己的GraphQLServlet狼牺,沒(méi)有包含攔截器機(jī)制,要加上身份認(rèn)證可以自己重寫(xiě)GraphQLServlet加入攔截器機(jī)制礼患,或者在Instrumentation接口上做文章是钥,兩種方案都需要較大改動(dòng),且后期維護(hù)難度大缅叠,權(quán)限控制也需要基于Instrumentation自己實(shí)現(xiàn)

綜合來(lái)看:


image

ok悄泥,再想想我們的需求是什么命迈。

  1. 對(duì)項(xiàng)目的現(xiàn)有流程改動(dòng)猩炊摇(最大化復(fù)用現(xiàn)有邏輯)
  2. 支持權(quán)限控制
  3. 自動(dòng)解析schema
  4. 不用硬編碼、不要底層細(xì)節(jié)

這么一看的話可以得出這樣的方案:


image

可以同時(shí)使用SpringMVC的攔截器和graphql-java-tools的優(yōu)勢(shì)秒啦。

似乎這種方案能滿足我們的需求领曼,但是有一個(gè)潛在的風(fēng)險(xiǎn):

“A用戶允許訪問(wèn)ApiA鸥鹉,ApiA能夠訪問(wèn)到實(shí)體A,但是A用戶沒(méi)有權(quán)限訪問(wèn)實(shí)體A”

這時(shí)工程上就難以控制了庶骄,如果非要控制需要對(duì)實(shí)體進(jìn)行權(quán)限毁渗,能做到,但是需要另外一套設(shè)計(jì)单刁,工作量也不小灸异。

風(fēng)險(xiǎn)

工程化實(shí)踐時(shí)風(fēng)險(xiǎn)是必須要考慮的問(wèn)題,GraphQL強(qiáng)大的自省功能(查詢整個(gè)實(shí)體圖的結(jié)構(gòu))能方便開(kāi)發(fā),也帶來(lái)了相應(yīng)的風(fēng)險(xiǎn)肺樟,同時(shí)嵌套循環(huán)查詢檐春、sql注入等問(wèn)題也是需要防范的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末么伯,一起剝皮案震驚了整個(gè)濱河市疟暖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌田柔,老刑警劉巖俐巴,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凯楔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)锦募,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)摆屯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人糠亩,你說(shuō)我怎么就攤上這事虐骑。” “怎么了赎线?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵廷没,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我垂寥,道長(zhǎng)颠黎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任滞项,我火速辦了婚禮狭归,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘文判。我一直安慰自己过椎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布戏仓。 她就那樣靜靜地躺著疚宇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赏殃。 梳的紋絲不亂的頭發(fā)上敷待,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音仁热,去河邊找鬼讼撒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的根盒。 我是一名探鬼主播钳幅,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼炎滞!你這毒婦竟也來(lái)了敢艰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤册赛,失蹤者是張志新(化名)和其女友劉穎钠导,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體森瘪,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牡属,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扼睬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逮栅。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖窗宇,靈堂內(nèi)的尸體忽然破棺而出措伐,到底是詐尸還是另有隱情,我是刑警寧澤军俊,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布侥加,位于F島的核電站,受9級(jí)特大地震影響粪躬,放射性物質(zhì)發(fā)生泄漏担败。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一镰官、第九天 我趴在偏房一處隱蔽的房頂上張望氢架。 院中可真熱鬧,春花似錦朋魔、人聲如沸岖研。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)孙援。三九已至,卻和暖如春扇雕,著一層夾襖步出監(jiān)牢的瞬間拓售,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工镶奉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留础淤,地道東北人崭放。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鸽凶,于是被迫代替她去往敵國(guó)和親币砂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容