首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >无法将处理好的GraphQL错误从一个阿波罗服务器API传递到另一个API。

无法将处理好的GraphQL错误从一个阿波罗服务器API传递到另一个API。
EN

Stack Overflow用户
提问于 2019-04-09 06:48:23
回答 1查看 1.3K关注 0票数 1

我正在尝试通过阿波罗服务器快车和Passport构建一个使用GraphQL的微型服务web应用程序示例。

到目前为止,我有4微服务(用户,博客,项目,概要文件)和一个网关API,我将他们与片段的关系(如Blog.authorUser.projects等)。一切都很好,我可以全面执行CRUD。

然后,当我尝试实现身份验证时,所有的人都下了地狱(这让我大吃一惊),但奇怪的是,没有实现auth本身,这不是问题所在。

问题在于错误处理,更具体地说,将GraphQL错误从远程API传递给网关以进行拼接。网关发现了一个错误,但是实际的细节(如{password: 'password incorrect'})被Gateway吞没了。

用户API错误

代码语言:javascript
运行
AI代码解释
复制
{
  "errors": [
    {
      "message": "The request is invalid.",
      "type": "ValidationError",
      "state": {
        "password": [
          "password incorrect"
        ]
      },
      "path": [
        "loginUser"
      ],
      "stack": [
        ...
      ]
    }
  ],
  "data": {
    "loginUser": null
  }
}

网关API错误

代码语言:javascript
运行
AI代码解释
复制
{
  "errors": [
    {
      "message": "The request is invalid.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "loginUser"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "errors": [
            {
              "message": "The request is invalid.",
              "locations": [],
              "path": [
                "loginUser"
              ]
            }
          ],
          "stacktrace": [
            "Error: The request is invalid.",
            ... // stacktrace refers to node_modules/graphql- 
            tools/src/stitching
  ],
  "data": {
    "loginUser": null
  }
}

网关src/index.js导入快件

代码语言:javascript
运行
AI代码解释
复制
s';
import { ApolloServer } from 'apollo-server-express';
// ...
import errorHandler from '../error-handling/errorHandler';

// ... app setup

const startGateway = async () => {
    const schema = await makeSchema(); // stitches schema
    const app = express();

    app.use('/graphql', (req, res, next) => {
        // passport
        // ...
    });

    const server = new ApolloServer({
        schema,
        context: ({ req }) =>  ({ authScope: req.headers.authorization }),
        // custom error handler that tries to unravel, clean and return error
        formatError: (err) => errorHandler(true)(err)
    });

    server.applyMiddleware({ app });
    app.listen({ port: PORT }, () => console.log(`\n Gateway Server ready at http://localhost:${PORT}${server.graphqlPath} \n`));
};

startGateway().catch(err => console.log(err));

网关src/remoteSchema/index.js (在这里进行拼接)

代码语言:javascript
运行
AI代码解释
复制
import { makeRemoteExecutableSchema, introspectSchema } from 'graphql-tools';
import { ApolloLink } from 'apollo-link';
import { setContext } from 'apollo-link-context';
import { introspectionLink, stitchingLink } from './link';


// graphql API metadata
const graphqlApis = [
    { uri: config.USER_DEV_API },
    { uri: config.BLOG_DEV_API },
    { uri: config.PROJECT_DEV_API },
    { uri: config.PROFILE_DEV_API }
];

// create executable schemas from remote GraphQL APIs
export default async () => {
    const schemas = [];

    for (const api of graphqlApis) {

        const contextLink = setContext((request, previousContext) => {
            const { authScope } = previousContext.graphqlContext;
            return {
                headers: {
                    authorization: authScope
                }
            };
        });

        // INTROSPECTION LINK
        const apiIntroSpectionLink = await introspectionLink(api.uri);

        // INTROSPECT SCHEMA
        const remoteSchema = await introspectSchema(apiIntroSpectionLink);

        // STITCHING LINK
        const apiSticthingLink = stitchingLink(api.uri);

        // MAKE REMOTE SCHEMA
        const remoteExecutableSchema = makeRemoteExecutableSchema({
            schema: remoteSchema,
            link: ApolloLink.from([contextLink, apiSticthingLink])
        });

        schemas.push(remoteExecutableSchema);
    }

    return schemas;
};

缝纫还有更多的东西,但是这里太多了。但缝合成功了。

用户API src/解析器

代码语言:javascript
运行
AI代码解释
复制
const resolvers = {
    Query: {/*...*/},
    Mutation: {
        loginUser: async (parent, user) => {
            const errorArray = [];

            // ...get the data...

            const valid = await bcrypt.compare(user.password, ifUser.password);

            if (!valid) {
                errorArray.push(validationError('password', 'password incorrect'));
                // throws a formatted error in USER API but not handled in GATEWAY
                throw new GraphQlValidationError(errorArray);
            }

            // ... return json web token if valid

        }
    }
}

用户errors.js

代码语言:javascript
运行
AI代码解释
复制
export class GraphQlValidationError extends GraphQLError {
    constructor(errors) {
        super('The request is invalid.');
        this.state = errors.reduce((result, error) => {
            if (Object.prototype.hasOwnProperty.call(result, error.key)) {
                result[error.key].push(error.message);
            } else {
                result[error.key] = [error.message];
            }
            return result;
        }, {});
        this.type = errorTypes.VALIDATION_ERROR;
    }
}

export const validationError = (key, message) => ({ key, message });

网关&用户errorHandler.js

代码语言:javascript
运行
AI代码解释
复制
import formatError from './formatError';

export default includeStack => (error) => {
    const formattedError = formatError(includeStack)(error);

    return formattedError;
};

formatError.js

代码语言:javascript
运行
AI代码解释
复制
import errorTypes from './errorTypes';
import unwrapErrors from './unwrapErrors';

export default shouldIncludeStack => (error) => {
    const unwrappedError = unwrapErrors(error);

    const formattedError = {
        message: unwrappedError.message || error.message,
        type: unwrappedError.type || error.type || errorTypes.ERROR,
        state: unwrappedError.state || error.state,
        detail: unwrappedError.detail || error.detail,
        path: unwrappedError.path || error.path,
    };

    if (shouldIncludeStack) {
        formattedError.stack = unwrappedError.stack || error.extensions.exception.stacktrace;
    }

    return formattedError;
};

unwrapErrors.js

代码语言:javascript
运行
AI代码解释
复制
export default function unwrapErrors(err) {
    if (err.extensions) {
        return unwrapErrors(err.extensions);
    }

    if (err.exception) {
        return unwrapErrors(err.exception);
    }

    if (err.errors) {
        return unwrapErrors(err.errors);
    }


    return err;
}

如果代码片段不是需要的,我会提前道歉。我很乐意回答任何问题。

提前感谢!

EN

回答 1

Stack Overflow用户

发布于 2019-04-09 07:45:31

Ok似乎已经通过这一讨论指向这个要旨的帮助修复了它。这是一个拼接错误和一些不必要的错误格式。我从formatError中删除了ApolloServer({}),并重新格式化了./src/remoteSchema/index.js,如下所示:

代码语言:javascript
运行
AI代码解释
复制
import { makeRemoteExecutableSchema, introspectSchema } from 'graphql-tools';
import { ApolloLink } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { onError } from 'apollo-link-error';
import fetch from 'node-fetch';
import config from '../../config/config';


// graphql API metadata
const graphqlApis = [
    { uri: config.USER_DEV_API },
    { uri: config.BLOG_DEV_API },
    { uri: config.PROJECT_DEV_API },
    { uri: config.PROFILE_DEV_API }
];

// create executable schemas from remote GraphQL APIs
export default async () => {
    const schemas = [];

    /*eslint-disable*/
    for (const api of graphqlApis) {

        let remoteLink = new HttpLink({ uri : api.uri, fetch });
        let remoteContext = setContext((req, previous) => {
            // if the authorization token doesn't exist, or is malformed, do not pass it upstream
            if (
                !previous.graphqlContext.authorization
                ||
                !previous.graphqlContext.authorization.match(/^Bearer /)
            ) {
                return;
            }

            return {
                headers: {
                    'Authorization': previous.graphqlContext.authorization,
                }
            }
        });

        let remoteError = onError(({ networkError, graphQLErrors }) => {
            if (graphQLErrors) {
                graphQLErrors.forEach((val) => {
                    Object.setPrototypeOf(val, Error.prototype);
                });
            }
        });
        let remoteSchema  = await introspectSchema(remoteLink);
        let remoteExecutableSchema = makeRemoteExecutableSchema({
            schema : remoteSchema,
            link : ApolloLink.from([
                remoteContext,
                remoteError,
                remoteLink
            ])
        });

        schemas.push(remoteExecutableSchema);
    }

    return schemas;
};

这是一个星期的痛苦,但据我所见,他们的问题将有望在graphql 5.0中得到解决。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55595592

复制
相关文章

相似问题

领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文