【问题标题】:How to handle error and send response in GraphQL如何在 GraphQL 中处理错误并发送响应
【发布时间】:2019-12-12 00:20:56
【问题描述】:

我从 GraphQL 开始,我无法理解如何在 GraphQL 中抛出错误

我浏览了网络上的几篇文章,但几乎所有文章都使用 Apollo,而且代码结构看起来与我的工作方式大不相同。

考虑这段代码,我在这里进行突变,现在如何发送带有错误的响应消息并在出现错误时更改标头状态消息?

  AddNewPersonalInfo: {
  type: userDashboardType,
  args: { 
    parameter: {
      type: userCreationlInputType
    }
  }, 
  resolve: async (parent, args, context) => {
    args.parameter.userId = context.req.headers.userId
    //Check if user info already exsist
    const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
    if (checkIfUserInformationExsist[0]) {
      const error = {
        code: 403, 
        message: 'User info Already exsist'
      }
      throw new Error(error)
    } else {
      try {
      const addLinkedinUser = await insertIntheTable('personal', payload)
      return true
      } catch (err) {
        console.error(err)
        throw new Error(err)
      }
    }
  }
}

【问题讨论】:

    标签: javascript node.js graphql


    【解决方案1】:

    我在我的一个项目中遇到过,很难设置响应的状态码。因此,我使用express-graphql 进行了一些自定义错误响应来识别正确的statusCode

    以下是示例(我在我的一个项目中使用的):

    --------app.js file--------

    const graphqlHTTP = require('express-graphql')
    
    app.use('/graphql', (req, res) => {
      graphqlHTTP({
        schema: GraphQLSchema, //A GraphQLSchema instance from GraphQL.js. A schema must be provided.
        graphiql: true,
        context: { req },
        formatError: (err) => {
          const error = getErrorCode(err.message)
          return ({ message: error.message, statusCode: error.statusCode })
        }
      })(req, res)
    })
    

    --------getErrorCode函数实现--------

    const { errorType } = require('../constants')
    
    const getErrorCode = errorName => {
      return errorType[errorName]
    }
    
    module.exports = getErrorCode
    

    --------Constant.js文件--------

    exports.errorName = {
      USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',
      SERVER_ERROR: 'SERVER_ERROR'
    }
    
    exports.errorType = {
      USER_ALREADY_EXISTS: {
        message: 'User is already exists.',
        statusCode: 403
      },
      SERVER_ERROR: {
        message: 'Server error.',
        statusCode: 500
      }
    }
    

    现在,我们可以使用我们的设置了。

    从您的查询或突变中,您需要需要常量文件并返回自定义错误:

    const { errorName } = require('../constant')
    
    AddNewPersonalInfo: {
      type: userDashboardType,
      args: { 
        parameter: {
          type: userCreationlInputType
        }
      }, 
      resolve: async (parent, args, context) => {
        args.parameter.userId = context.req.headers.userId
        //Check if user info already exsist
        const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
        if (checkIfUserInformationExsist[0]) {
          const error = {
            code: 403, 
            message: 'User info Already exsist'
          }
          throw new Error(errorName.USER_ALREADY_EXISTS) // Here you can use error from constatnt file
        } else {
          try {
          const addLinkedinUser = await insertIntheTable('personal', payload)
          return true
          } catch (err) {
            console.error(err)
            throw new Error(errorName.SERVER_ERROR) // Here you can use error from constatnt file
          }
        }
      }
    }
    

    --------错误响应--------

    {
      error: [{
        "statusCode": 403,
        "message": "User is already exists."
      }],
      data: null
    }
    

    我们也只需要从 FS 端编写自定义错误处理。

    注意:- formatError: 已弃用并由 customFormatErrorFn 取代。它将在版本 1.0.0 中删除。可以参考customFormatErrorFn

    【讨论】:

      【解决方案2】:

      graphql 应该是一个不应该的应用层(参见最后一段为什么不应该而不是)需要http 工作。尽管在 99% 的情况下它运行在 http 之上,但由于这样做非常方便,graphql 本身就是一个第 7 层协议。

      在你的情况下这意味着什么?嗯,这意味着你不应该将 HTTP/REST 的概念与 graphql 的概念混为一谈,而应该关注后者。 headers 错误代码是一个 HTTP/REST 概念,graphql 在响应的errors 字段中发送错误,nodejs 实现已经捕获了所有错误并将它们添加到列表中。 HTTP 状态将始终为 200,您的客户不应该关心和使用您的 graphql api,而不是 REST 与 graphql 的混合。

      话虽如此,REST over HTTP 在一些方面做得更好。所以人们,包括 Apollo 的开发者,也有点混合概念,主要是因为 graphql 标准不完整(也就是说,它没有解决你在构建 API 时可能遇到的所有问题的标准/规则),所以人们即兴创作。对于任何严肃的项目,我都不推荐 graphql。

      Reference

      【讨论】:

        【解决方案3】:

        您可以像这样在 graphqlHTTP 中指定一个错误函数:

        app.use("/graphql", graphqlHTTP({
                schema,
                graphiql: true,
                customFormatErrorFn: err => { 
                    try { 
                        err.details = JSON.parse(err.message);
                        err.message = Array.isArray(err.details.error) ? err.details.error.join(",") : err.details.error;
                        return err;
                    } catch {
                        return err;
                    }
                }
            }));
        

        err.message 可能包含 JSON 对象或字符串。

        您可以使用这些函数来生成特定的客户端和服务器错误函数:

        const clientError = error => new Error(JSON.stringify({
            success: false,
            code: 400,
            error
        }));
        
        const serverError = ({ name, message, stack }) => new Error(JSON.stringify({
            success: false,
            error: "Server Error",
            code: 500,
            name,
            message,
            stack
        }));
        
        const userValidationError = err => { 
            if (err.name === "ValidationError") return clientError(Object.values(err.errors).map(({ message }) => message));
            return serverError(err);
        }
        
        module.exports = {
            clientError,
            serverError,
            userValidationError
        };
        

        userValidationError 函数在您遇到 mongodb 验证错误时很有用。

        这样你就可以像这样在解析函数中使用它:

        try { 
            const createdDocument = await MongooseDoc.create(data);
            return createdDocument;
        } catch (err) { 
            throw userValidationError(err);
        }
        

        响应将是

        {
          "errors": [
            {
              "message": "error details 1,error details 2",
              "locations": [
                {
                  "line": 2,
                  "column": 3
                }
              ],
              "path": [
                "document"
              ],
              "details": {
                "success": false,
                "code": 400,
                "error": [
                  "error details 1",
                  "error details 2"
                ]
              }
            }
          ],
          "data": {
            "document": null
          }
        }
        

        如果你想抛出一个clientError,你把它扔到外面try catch。

        希望这段代码可以帮助某人在 graphql 中发送动态错误消息。

        【讨论】:

          猜你喜欢
          • 2012-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-03
          • 2015-06-06
          • 1970-01-01
          • 1970-01-01
          • 2019-04-04
          相关资源
          最近更新 更多