GraphQL(四):GraphQL工程化实践

柔情只为你懂 2022-10-01 05:39 267阅读 0赞

Demo到产品化还有很长的路要走,尤其是要在尽量小的影响当前框架的前提下引入新的方法。

GraphiQL

GraphiQL是整个GraphQL优势的重要一环,然而默认的GraphiQL不允许配置graphql服务的地址(就是点击GraphiQL上的运行按钮去请求数据的地址),要弄明白这一点很容易,找到graphiql-spring-boot-autoconfigure包,里面有graphiql.html文件,请求数据的endpoint是硬编码的:

  1. function graphQLFetcher(graphQLParams) {
  2. // This example expects a GraphQL server at the path /graphql.
  3. // Change this to point wherever you host your GraphQL server.
  4. return fetch('/graphql', {
  5. method: 'post',
  6. headers: {
  7. 'Accept': 'application/json',
  8. 'Content-Type': 'application/json',
  9. },
  10. body: JSON.stringify(graphQLParams),
  11. credentials: 'include',
  12. }).then(function (response) {
  13. return response.text();
  14. }).then(function (responseBody) {
  15. try {
  16. return JSON.parse(responseBody);
  17. } catch (error) {
  18. return responseBody;
  19. }
  20. });
  21. }
  22. 复制代码

显然这不能满足项目工程化的要求,解决这个问题有两种比较简单的方式:

  1. 把graphiql.html文件复制到项目中,用一个Controller提供GraphiQL服务,这样就可以去掉GraphiQL的相关依赖了
  2. 利用官方的GraphiQL的React组件自己搭建GraphiQL页面,这样定制化更方便

GraphQL Voyager

强大的实体关系图生成工具

GitHub已经提供了GraphQL的接口,实体关系图可以在GraphQL Voyager里查看,Custom Schema允许提供自己的实体关系数据生成实体关系图,简直不能更好用。

身份认证和权限控制

在GraphQL(三):GraphQL集成SpringBoot原理中提到GraphQL本身不带身份认证和权限控制(这也确实不是它该做的事儿),但是它对查询提供了回调方法(Instrumentation接口),在GraphQL.java文件中可以看到这部分逻辑:

  1. public ExecutionResult execute(String requestString, String operationName, Object context, Map<String, Object> arguments) {
  2. //执行回调
  3. InstrumentationContext<ExecutionResult> executionCtx = instrumentation.beginExecution(new ExecutionParameters(requestString, operationName, context, arguments));
  4. assertNotNull(arguments, "arguments can't be null");
  5. log.debug("Executing request. operation name: {}. Request: {} ", operationName, requestString);
  6. //解析回调
  7. InstrumentationContext<Document> parseCtx = instrumentation.beginParse(new ExecutionParameters(requestString, operationName, context, arguments));
  8. Parser parser = new Parser();
  9. Document document;
  10. try {
  11. document = parser.parseDocument(requestString);
  12. parseCtx.onEnd(document);
  13. } catch (ParseCancellationException e) {
  14. RecognitionException recognitionException = (RecognitionException) e.getCause();
  15. SourceLocation sourceLocation = new SourceLocation(recognitionException.getOffendingToken().getLine(), recognitionException.getOffendingToken().getCharPositionInLine());
  16. InvalidSyntaxError invalidSyntaxError = new InvalidSyntaxError(sourceLocation);
  17. return new ExecutionResultImpl(Collections.singletonList(invalidSyntaxError));
  18. }
  19. //验证回调
  20. InstrumentationContext<List<ValidationError>> validationCtx = instrumentation.beginValidation(new ValidationParameters(requestString,operationName,context,arguments,document));
  21. Validator validator = new Validator();
  22. List<ValidationError> validationErrors = validator.validateDocument(graphQLSchema, document);
  23. validationCtx.onEnd(validationErrors);
  24. if (validationErrors.size() > 0) {
  25. return new ExecutionResultImpl(validationErrors);
  26. }
  27. ExecutionId executionId = idProvider.provide(requestString, operationName, context);
  28. Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation);
  29. ExecutionResult result = execution.execute(executionId, graphQLSchema, context, document, operationName, arguments);
  30. executionCtx.onEnd(result);
  31. return result;
  32. }
  33. 复制代码

通过对每次执行GraphQL查询进行拦截可以实现类似SpringMVC拦截器的效果,但是需要自己实现SpringMVC中 @RequiredRole 注解的功能。

在GraphQL(二):GraphQL服务搭建中提到有两种搭建GraphQL服务的方式,当时并没有考虑身份认证和权限控制的问题,假如要在那两种方法的基础上加入身份认证和权限控制,有哪些成本呢?

  • graphql-java + graphql-java-spring 此方案使用了SpringMVC的DispatcherServlet,所以完全可以复用原有的拦截器机制,权限认证需要基于Instrumentation自己实现,工作量大
  • graphql-spring-boot-starter + graphql-java-tools 此方案用了自己的GraphQLServlet,没有包含拦截器机制,要加上身份认证可以自己重写GraphQLServlet加入拦截器机制,或者在Instrumentation接口上做文章,两种方案都需要较大改动,且后期维护难度大,权限控制也需要基于Instrumentation自己实现

综合来看:

ok,再想想我们的需求是什么。

  1. 对项目的现有流程改动小(最大化复用现有逻辑)
  2. 支持权限控制
  3. 自动解析schema
  4. 不用硬编码、不要底层细节

这么一看的话可以得出这样的方案:

可以同时使用SpringMVC的拦截器和graphql-java-tools的优势。

似乎这种方案能满足我们的需求,但是有一个潜在的风险:

“A用户允许访问ApiA,ApiA能够访问到实体A,但是A用户没有权限访问实体A”

这时工程上就难以控制了,如果非要控制需要对实体进行权限,能做到,但是需要另外一套设计,工作量也不小。

风险

工程化实践时风险是必须要考虑的问题,GraphQL强大的自省功能(查询整个实体图的结构)能方便开发,也带来了相应的风险,同时嵌套循环查询、sql注入等问题也是需要防范的。

转载于:https://juejin.im/post/5ce55c98e51d455d877e0c9a

发表评论

表情:
评论列表 (有 0 条评论,267人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Graphql-Java实践(二)-变更

    这次来说一下相对应 “增删改的” Mutation操作,也是使用对应的DataFetcher,只不过增加了一个增删改的操作后返回查询结果罢了,老样子来看一个例子,IDL方式