eggjs / egg

🥚 Born to build better enterprise frameworks and apps with Node.js & Koa
https://eggjs.org
MIT License
18.86k stars 1.81k forks source link

获取egg-graphql中业务代码抛出的异常 #3064

Open nigelvon opened 5 years ago

nigelvon commented 5 years ago

发现graphql请求触发的业务逻辑所抛出的异常都被捕获了,不知道如何获取Error对象,控制台和相关日志找了一圈也没看到。 有什么办法能够获取这些Error么?

ghost commented 5 years ago

使用try……catch也无法捕获吗?请问目前你的代码是?

nigelvon commented 5 years ago

单独对出错代码try catch是可以捕获的,想要在外层统一拦截。

SingleMai commented 5 years ago

萌新同问,如果想要在做统一封装拦截有什么方案吗。现在发现graphql的报错不会走到egg-error的拦截中,(即使是代码层面的报错)也会被统一拦截到了前端,只能一个个去加try...catch。 稍微看了一下源码,好像是有统一做try{}catch{}然后返回给前端,请问能否在这里切入提供一个函数方便自定义处理呢。

logzh commented 5 years ago

如果是egg-graphql 2.0以下版本,可以将插件中间件复制到项目下修改,覆盖插件里的中间件,具体修改配置graphqlKoa时带上formatError,在formatError中做错误的统一处理。 2.0+之后,同样的方式不灵了因为graphqlKoa api修改了,formatError只能在定义server的时候定义。

SingleMai commented 5 years ago

首先感谢大佬回复,不过这里提到的formateError只能在定义server的时候定义能否具体描述一下呢。我看源码里面是直接引用了graphql的方法来进行报错,好像并没有提供出定义的位置...以及这样做怎样和resetApi时的报错中间件能结合起来使用也是比较疑惑的点

logzh commented 5 years ago

@SingleMai

egg-egg-graphql 1.x版本

egg-egg-graphql 1.x版本对应的apollo-server-koa是1.x,下面是apollo-server-koa 1.x的官方demo: https://github.com/apollographql/apollo-server/blob/v1.4.0/packages/apollo-server-koa/README.md

import koa from 'koa'; // koa@2
import koaRouter from 'koa-router';
import koaBody from 'koa-bodyparser';
import { graphqlKoa } from 'apollo-server-koa';

const app = new koa();
const router = new koaRouter();
const PORT = 3000;

// koaBody is needed just for POST.
app.use(koaBody());

router.post('/graphql', graphqlKoa({ schema: myGraphQLSchema }));
router.get('/graphql', graphqlKoa({ schema: myGraphQLSchema }));

app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT);

如果要增加错误统一处理:

router.post('/graphql', graphqlKoa({ schema: myGraphQLSchema, formateError: func... }));

egg-egg-graphql 1.x插件中间件的使用是 https://github.com/eggjs/egg-graphql/blob/1.3.0/app/middleware/graphql.js

return graphqlKoa({
        schema: app.schema,
        context: this,
      })(this);

这样没有办法直接传 formateError 参数。

所以我才说

如果是egg-graphql 2.0以下版本,可以将插件中间件复制到项目下修改,覆盖插件里的中间件,具体修改配置graphqlKoa时带上formatError,在formatError中做错误的统一处理。

egg-egg-graphql 2.x版本

egg-egg-graphql 2.x版本对应的apollo-server-koa是2.x,下面是apollo-server-koa 2.x的官方demo: https://github.com/apollographql/apollo-server/blob/master/packages/apollo-server-koa/README.md

const Koa = require('koa');
const { ApolloServer, gql } = require('apollo-server-koa');

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const app = new Koa();
server.applyMiddleware({ app });

app.listen({ port: 4000 }, () =>
  console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`),
);

如果要增加错误统一处理:

const server = new ApolloServer({ typeDefs, resolvers, formateError: func ... });

egg-egg-graphql 2.x插件中间件的使用是 https://github.com/eggjs/egg-graphql/blob/2.3.0/app/middleware/graphql.js

return graphqlKoa({
        schema: app.schema,
        context: this,
      })(this);

这里的 graphqlKoa 即使传入 formateError 参数也没办法了。

对于“怎样和resetApi时的报错中间件能结合起来使用” 我们这边没有这样的场景,我们是的做了个graphql单独的服务,graphql服务的后面是后台接口等(包括其他数据来源),graphql服务提供服务给前台服务端或前台客户端。

SingleMai commented 5 years ago

感谢回复, so噶 抱歉最近有点太忙了 一直没上来看。大佬解释得非常清晰,算是理清了很多疑惑非常感谢!!

jtyjty99999 commented 5 years ago

我把这个 formateError 透出一下

jtyjty99999 commented 5 years ago

https://github.com/eggjs/egg-graphql/pull/24 跟进

michaellyu commented 5 years ago

eggjs/egg-graphql#24 一直没有进展吗? @jtyjty99999

wushanchao commented 5 years ago

egg中有两个地方可以捕捉egg-graphql的解析错误或者内部错误。

  1. 中间件
    exports.middleware = ['errorHandler','graphql'];
  2. 另一个是框架层面的onerror。

    exports.onerror = {
    all(err, ctx){
    if(ctx.request.url.indexOf('/graphql')>-1 && ctx.response.status !== 200){
      // console.log('捕获住了gql错误');
      ctx.set({
        "Content-Type": "application/json"
      });
      ctx.status = 200;
      ctx.body = JSON.stringify({
        data:null,
        message:'gql解析错误',
        success: false
      });
    }
    else{
      ctx.status = 400;
      ctx.body = 'error';
    }
    
    }
    }
Beiluola commented 4 years ago

我很高兴自己解决了这个问题,在这里分享一下。

标题中说捕获graphql中业务代码的异常,而自己的业务代码都是写在resolver中的,也就是说,如果可以在resolver执行前首先执行类似 try{ } catch(err){ }这样的代码,把resolver的实际执行放在 try{ } 代码段中,即可实现捕捉graphql异常。

所有resolver都可以在app.schema中获取到,那么就可以实现上述所说。

不仅如此,我还把它扩展成了类似中间件一样的机制,这样就可以有更多的玩法。

请参考下面项目的README介绍中的 resolver中间件 章节

https://github.com/beiluola/glacier