首页 > 解决方案 > 如何在我的 Vue Graphql 组件中使用错误并让其他错误在全局范围内处理?

问题描述

我们将 Vue 与 apollo 一起使用,我很难正确处理错误。

这是我们的组件

<template>
   <div v-if="product">
      <router-view :key="product.id" :product="product" />
   </div>
   <div v-else-if="!this.$apollo.loading">
      <p>Product is not available anymore</p>
   </div>
</template>
    
    <script>
import productQuery from "@/graphql/product.graphql";

export default {
   name: "ProductWrapper",
   props: ["productId"],
   apollo: {
      product: {
         query: productQuery,
         variables() {
            return {
               productId: this.productId,
            };
         },
      },
   },
};
</script>

如果产品不再可用,我们在后端有三个选项:a)后端可以发送null而不会出错 b)使用联合发送一个错误对象作为数据的一部分 c)使用 apollo 发送一些错误扩展,以便于错误处理在客户端

选项 a) 似乎是一个奇怪的选项 选项 b) 对于我们的用例来说太复杂了。所以我决定选择c):

在我们的后端,我们使用 apollo 错误扩展来发送一些适当的错误代码供客户端处理。

{
   data: { }
   errors: [
      {
         "message": "NOT_FOUND",
         "locations": ...,
         "path": ...
         "extensions": {
         "details": [],
         "errorCode": "NOT_FOUND",
         "message": "NOT_FOUND",
         "classification": "DataFetchingException"
      }
   ]
}

一切正常,因为产品结果为 null ,因为没有发送数据,只是一个错误。但是 vue-apollo 正在将它记录到 console.error 中。我不希望任何日志记录到 console.error 因为用户在他的浏览器中看到一个红色标记。但是如果没有其他人处理过这个错误,我希望它在 console.error 中弹出。

我可以在三个地方添加错误处理:

  1. 查询定义中的 error()
  2. $error() 所有查询的默认处理程序
  3. 错误链接

ErrorLink 似乎是错误的地方,因为只有组件中的查询知道 NOT_FOUND 不是致命的,但有时会发生。$error 也是如此

那我怎么说:这个错误可能会发生,我对此做好了充分的准备。所有其他错误应由 ErrorLink 处理。如何使用组件中的错误?

标签: error-handlinggraphqlapollovue-apollo

解决方案


我对 vue-apollo 错误处理的概述。

SmartQuery 错误处理程序

文档https ://apollo.vuejs.org/api/smart-query.html

error(error, vm, key, type, options) 是出现错误时调用的钩子。error 是具有 graphQLErrors 属性或 networkError 属性的 Apollo 错误对象。vm 是相关的组件实例。key 是智能查询键。类型是“查询”或“订阅”。options 是最终的 watchQuery 选项对象。

未记录:如果您返回false错误处理将停止并且不调用默认错误处理程序(Apollo 提供程序)。如果您返回true或不返回任何内容(又名undefined),则会调用默认错误处理程序。

代码示例

export default {
  name: "ProductWrapper",
  props: ['productId'],
  apollo: {
    product: {
        query: productQuery,
        variables() {
            return {
                productId: this.productId
            }
        },
        error(errors) {
          console.log("Smart Query Error Handler")
          console.log(errors.graphQLErrors)
          return false;
        }
    }
  }
}

VueApolloProvider 错误处理程序

文档: https ://apollo.vuejs.org/api/apollo-provider.html

所有智能查询和订阅的全局错误处理程序

重要提示:这不适用于突变。

未记录:当智能查询的错误处理程序返回时,不会调用它false

代码示例

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
  errorHandler: (errors) => {
    console.log("Provider errorHandler")
    console.log(errors)
  }
});

VueApolloProvider $error 特殊选项(没用)

文档 https://apollo.vuejs.org/guide/apollo/special-options.html

$error 在默认处理程序中捕获错误(请参阅错误高级选项 > 智能查询)

代码示例(错误)

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
  defaultOptions: {
    error(errors) {
      console.log("Provider $error")
      console.log(errors)
    }
  }
});

这是我的第一次尝试,但它会在安装组件时调用并为我们提供完整的组件。见https://github.com/vuejs/vue-apollo/issues/126

代码示例(可以吗?)

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
  defaultOptions: {
    $error() {
      return (errors) => {
        console.log("Provider $error handler")
        console.log(errors.graphQLErrors)
      }
    },
  },
});

这样就调用了 $error 函数。但它就像默认的 errorHandler 一样被调用(见上文)。文档似乎是错误的。所以这个方法对我来说似乎没什么用。正如您在此调试屏幕截图中所见,它的使用方式与其他错误处理程序一样:

调试截图

错误链接

文档: https ://www.apollographql.com/docs/react/data/error-handling/#advanced-error-handling-with-apollo-link

代码示例

import {onError} from "apollo-link-error";
const errorHandler = onError(({ networkError, graphQLErrors }) => {
  console.log("Link onError")
  console.log({ graphQLErrors, networkError})
})
const link = split(
  // split based on operation type
  ({query}) => {
    const definition = getMainDefinition(query);
      return definition.kind === 'OperationDefinition' &&
         definition.operation === 'subscription'
    },
   wsErrorHandler.concat(wsLink),
   errorHandler.concat(httpLink)
);

重要:这是在任何其他 ErrorHandler 之前调用的,它适用于查询、订阅和突变

突变捕获

文档 https://apollo.vuejs.org/guide/apollo/mutations.html

代码示例 1

async submit() {
  await this.$apollo.mutate({
    mutation: productDeleteMutation,
    variables: {
      productId: this.product.id
    },
  }).catch((errors) => {
    console.log("mutation .catch error")
    console.log(errors)
  })
}

代码示例 2 您也可以使用 try/catch:

async submit() {
  try {
    await this.$apollo.mutate({
      mutation: productDeleteMutation,
      variables: {
        productId: this.product.id
      },
    })
  } catch (errors) {
    console.log("mutation try/catch error")
    console.log(errors)
  }
}

唯一可以调用的其他处理程序是 onError ErrorLink(见上文),它将在 catch 错误处理之前调用。

vue 使用 errorCaptured 处理突变

将此生命周期钩子添加到您的组件中以捕获来自突变的错误。

errorCaptured(err, vm, info) {
  console.log("errorCaptured")
  console.log(err.graphQLErrors)
  return false
}

文档https ://vuejs.org/v2/api/#errorCaptured

更多链接https ://medium.com/js-dojo/error-exception-handling-in-vue-js-application-6c26eeb6b3e4

它仅适用于突变,因为智能查询的错误由 apollo 本身处理。

vue 全局 errorHandler 的突变错误

您可以拥有一个 vue 默认错误处理程序来从突变中捕获 graphql 错误

import Vue from 'vue';
    
Vue.config.errorHandler = (err, vm, info) => {
  if (err.graphQLErrors) {
    console.log("vue errorHandler")
    console.log(err.graphQLErrors)
  }
};

它仅适用于突变,因为智能查询的错误由 apollo 本身处理。

文档 https://vuejs.org/v2/api/#errorHandler

window.onerror

vue 之外的错误可以用纯 javascript 处理

window.onerror = function(message, source, lineno, colno, error) {
  if (error.graphQLErrors) {
    console.log("window.onerror")
    console.log(error.graphQLErrors)
  }
};

这不适用于 vue 组件内部外部的突变、查询或订阅,因为它们由 vue 或 apollo 本身处理(如果未处理,则打印到 console.err)

文档 https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror

概括

同时捕获突变和查询错误的唯一方法是使用 ApolloLink 的 onError。服务器、网络和身份验证错误应该在那里被捕获,因为这些不是特定于任何操作的。

补充说明

探讨如何处理后端的错误

https://blog.logrocket.com/handling-graphql-errors-like-a-champ-with-unions-and-interfaces/


推荐阅读