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)單的方式:
- 把graphiql.html文件復(fù)制到項(xiàng)目中赠堵,用一個(gè)Controller提供GraphiQL服務(wù),這樣就可以去掉GraphiQL的相關(guān)依賴了
- 利用官方的GraphiQL的React組件自己搭建GraphiQL頁(yè)面法褥,這樣定制化更方便
GraphQL Voyager
強(qiáng)大的實(shí)體關(guān)系圖生成工具
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)看:
ok悄泥,再想想我們的需求是什么命迈。
- 對(duì)項(xiàng)目的現(xiàn)有流程改動(dòng)猩炊摇(最大化復(fù)用現(xiàn)有邏輯)
- 支持權(quán)限控制
- 自動(dòng)解析schema
- 不用硬編碼、不要底層細(xì)節(jié)
這么一看的話可以得出這樣的方案:
可以同時(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)題也是需要防范的。