首页 > web前端 > js教程 > 正文

使用 Axios 和自定义 Hook 构建强大的前端错误处理系统

Patricia Arquette
发布: 2024-11-02 20:54:03
原创
270 人浏览过

Building a Robust Frontend Error-Handling System with Axios and Custom Hooks

介绍

有效的错误处理对于提供无缝的用户体验和维护干净、可扩展的代码至关重要。在复杂的应用程序中,跨组件手动管理错误通常会导致代码混乱且不一致。本指南将向您展示如何使用 Axios、自定义钩子 (useApi) 和模块化服务层在 React 中构建模块化、可扩展且集中的错误处理系统,以创建用户友好、有组织且高效的结构。

挂钩:为什么集中式错误处理很重要

想象一下您正在构建一个电子商务平台。多个组件从不同的 API 获取数据,每个组件都可能因不同的原因而失败——网络问题、服务器错误或无效的用户输入。如果没有集中的错误处理系统,您的代码就会因重复的错误检查而变得混乱,并且用户会收到不一致的反馈。如何简化此流程以确保可靠性和无缝的用户体验?本指南将向您展示如何操作。

最后,您将学到:

  • 如何使用 Axios 拦截器设置集中式错误处理。
  • 自定义 useApi 钩子用于管理 API 请求状态的作用。
  • 使用服务模块组织API逻辑的好处。
  • 用于用户友好的错误处理的先进技术,包括重试选项和请求取消。

目录

  1. 为什么要集中处理错误?
  2. 基本实现
  3. 使用拦截器设置 Axios 实例
  4. 创建自定义 useApi Hook
  5. 理解 Promise 和 Promise 拒绝
  6. 组织服务模块
  7. 示例:用户服务
  8. 高级增强功能(可选)
    • 解析自定义时出错
    • 重试机制
    • 详细通知
    • 取消组件卸载请求
  9. 连接点
  10. 视觉摘要
  11. 将它们放在一起:一个现实世界的例子
  12. 最佳实践
  13. 延伸阅读
  14. 故障排除
  15. 环境配置
  16. 结论
  17. 号召性用语

为什么要集中处理错误?

集中式错误处理解决了两个常见的挑战:

重复错误代码

  • 问题:没有中心机制,组件依赖多个 try-catch 块。
  • 影响:导致不一致的错误处理和冗余代码。

用户体验不一致

  • 问题:在没有集中化的情况下,错误消息可能会因应用程序而异。
  • 影响:造成脱节的用户体验并可能让用户感到困惑。

使用 Axios 拦截器、自定义挂钩 (useApi) 和服务模块的集中式方法可以通过以下方式解决这些问题:

  • 错误解析和消息传递的单一位置:提供统一的位置来处理所有错误,确保一致性。
  • 关注点分离:允许组件纯粹专注于数据呈现和用户交互,将错误处理留给集中式模块。

基本实现

使用拦截器设置 Axios 实例

Axios 拦截器是 Axios 对每个请求或响应调用的函数。通过设置响应拦截器,您可以全局处理错误、解析响应以及根据特定条件执行日志记录或重定向用户等操作。

第 1 步:导入必要的模块

// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

第 2 步:创建 Axios 实例

const axiosInstance = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || '',
  headers: {
    'Content-Type': 'application/json',
  },
});
登录后复制
登录后复制
登录后复制

第 3 步:添加响应拦截器

axiosInstance.interceptors.response.use(
  (response) => response, // Pass through successful responses
  (error) => {
    if (!error.response) {
      toast.error(ERROR_MESSAGES.NETWORK_ERROR);
      return Promise.reject(error);
    }

    const { status, data } = error.response;
    let message = ERROR_MESSAGES[status] || ERROR_MESSAGES.GENERIC_ERROR;

    // Custom logic for specific error types
    if (data?.type === 'validation') {
      message = `Validation Error: ${data.message}`;
    } else if (data?.type === 'authentication') {
      message = `Authentication Error: ${data.message}`;
    }

    // Display error notification
    toast.error(message);

    // Handle unauthorized access by redirecting to login
    if (status === 401) {
      Router.push('/login');
    }

    return Promise.reject(error);
  }
);
登录后复制
登录后复制
登录后复制

说明:

  • 错误解析:拦截器检查错误是否有响应。如果没有,则假定出现网络错误并显示相应的消息。
  • 自定义错误消息:它尝试根据服务器提供的错误类型使用自定义错误消息。如果没有可用的消息,则会退回到预定义的消息。
  • 用户反馈:利用react-toastify显示toast通知,通过提供即时反馈来增强用户体验。
  • 重定向:如果发生 401 未经授权错误,将用户重定向到登录页面,通过防止未经授权的访问来确保安全。

第 4 步:导出 Axios 实例

export default axiosInstance;
登录后复制
登录后复制

自定义错误消息

在单独的配置文件中定义自定义错误消息,以保持一致性和易于管理。

// config/customErrors.js

const ERROR_MESSAGES = {
  NETWORK_ERROR: "Network error. Please check your connection and try again.",
  BAD_REQUEST: "There was an issue with your request. Please check and try again.",
  UNAUTHORIZED: "You are not authorized to perform this action. Please log in.",
  FORBIDDEN: "Access denied. You don't have permission to view this resource.",
  NOT_FOUND: "The requested resource was not found.",
  GENERIC_ERROR: "Something went wrong. Please try again later.",

  // You can add more messages here if you want
};

export default ERROR_MESSAGES;
登录后复制
登录后复制

快速总结:Axios 拦截器配置

设置 Axios 拦截器提供:

  • 集中错误解析:在一个地方管理错误,确保所有 API 请求的一致性。
  • 用户反馈:利用react-toastify立即通知用户有关问题。
  • 重定向和安全:在需要时重定向未经授权的用户登录,确保应用程序安全。

这个集中式 Axios 实例是构建可靠、用户友好的 API 通信层的关键,可确保跨应用程序对所有 API 请求和错误处理进行一致管理。

创建自定义 useApi 挂钩

useApi 挂钩集中 API 请求处理、管理加载、数据和错误状态。通过抽象这个过程,useApi 可以让组件避免重复的 try-catch 块,而专注于数据呈现。

参数:

  • apiFunc (Function):要执行的 API 函数,通常来自服务模块。
  • immediate(布尔值,可选):确定是否应在钩子初始化后立即进行 API 调用。默认为 false。

返回值:

  • data:API 调用的响应数据。
  • loading:表示API调用是否正在进行。
  • 错误:捕获失败的 API 调用中的任何错误消息。
  • request:发起API调用的函数,可以带必要的参数来调用。

执行:

// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

解释:

  • 状态管理
    • data:存储响应数据。
    • loading:指示 API 调用是否正在进行。
    • 错误:捕获任何错误消息。
  • 请求函数
    • 发起 API 调用。
    • 根据 API 调用的结果管理状态更新。

了解 Promise 和 Promise 拒绝

在深入研究之前,有必要了解 JavaScript 中的 Promise 和 Promise Rejection,因为它们在处理 API 调用等异步操作中发挥着关键作用。

  • Promises:Promise 是表示异步操作最终完成或失败的对象。它允许您附加回调来处理操作的成功(解决)或失败(拒绝)。
  • Promise Rejection:当操作失败时,Promise 被“拒绝”,触发 .catch 方法或 .then 中的第二个参数。

Axios 和 useApi 的相关性:

  • Axios 和 Promises:Axios 使用 Promises 来处理 HTTP 请求。当您使用 Axios 发出请求时,它会返回一个 Promise,该 Promise 会使用响应数据进行解析,或者会因错误而拒绝。
  • Axios 拦截器中的 Promise 拒绝:在 Axios 拦截器中,当发生错误时,拦截器使用 Promise.reject(error) 来拒绝 Promise。此拒绝会传播到进行 API 调用的位置。
  • 在useApi中捕获拒绝:useApi钩子的请求函数使用try-catch来处理这些拒绝。当 apiFunc(...args) 拒绝时,catch 块会捕获错误,并相应地更新错误状态。

处理 Promise 拒绝的重要性:

  • 防止未处理的拒绝:如果不处理 Promise 拒绝,可能会导致意外行为并使调试变得困难。
  • 一致的错误管理:通过集中处理 Promise 拒绝,您可以确保所有错误得到统一管理,从而增强代码可靠性和用户体验。

如果不使用 useApi Hook 怎么办?

如果没有 useApi 钩子,您将需要在每个进行 API 调用的组件中实现 try-catch 块。这种方法导致:

  • 重复代码:每个组件都会有类似的错误处理逻辑,增加代码冗余。
  • 不一致的错误处理:不同的组件可能以不同的方式处理错误,从而导致不一致的用户体验。
  • 增加维护工作:更新错误处理逻辑需要跨多个组件进行更改,使得维护变得很麻烦。

通过使用 useApi 钩子,您可以抽象出重复的错误处理逻辑,从而促进更干净、更易于维护的代码。

用法示例:

// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,useApi 挂钩管理 API 调用以获取产品。它处理加载状态,捕获任何错误,并将获取的数据提供给组件进行渲染。


组织服务模块

服务模块定义 API 端点函数,按实体(例如用户、产品)组织。这种结构使 API 逻辑与组件代码分离,确保模块化和重用。

示例:产品服务

// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

示例:用户服务

const axiosInstance = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || '',
  headers: {
    'Content-Type': 'application/json',
  },
});
登录后复制
登录后复制
登录后复制

服务模块的优点:

  • 启用重用和模块化:API 函数可以跨多个组件重用,减少代码重复。
  • 确保关注点分离:通过将 API 逻辑移至服务中来保持组件清洁,提高代码组织和可维护性。
  • 更容易测试:服务模块可以独立测试,确保API交互在集成到组件之前按预期工作。

高级增强功能(可选)

对于那些准备进一步改进错误处理系统的人,请考虑实施这些先进技术:

自定义解析错误

对错误进行分类(例如网络与验证)并提供可操作的消息,以帮助用户了解问题和可能的解决方案。

实施:

axiosInstance.interceptors.response.use(
  (response) => response, // Pass through successful responses
  (error) => {
    if (!error.response) {
      toast.error(ERROR_MESSAGES.NETWORK_ERROR);
      return Promise.reject(error);
    }

    const { status, data } = error.response;
    let message = ERROR_MESSAGES[status] || ERROR_MESSAGES.GENERIC_ERROR;

    // Custom logic for specific error types
    if (data?.type === 'validation') {
      message = `Validation Error: ${data.message}`;
    } else if (data?.type === 'authentication') {
      message = `Authentication Error: ${data.message}`;
    }

    // Display error notification
    toast.error(message);

    // Handle unauthorized access by redirecting to login
    if (status === 401) {
      Router.push('/login');
    }

    return Promise.reject(error);
  }
);
登录后复制
登录后复制
登录后复制

说明:

  • 错误分类:拦截器检查错误响应中的类型属性以确定错误的性质(例如,验证或身份验证)。
  • 可操作消息:根据错误类型为用户提供特定的错误消息,增强他们的理解和适当响应的能力。

重试机制

为失败的请求实现重试选项,例如 UI 中的重试按钮或具有指数退避的自动重试,以增强可靠性。

实施:

export default axiosInstance;
登录后复制
登录后复制

说明:

  • 重试:配置 Axios 使用指数退避策略重试失败的请求最多 3 次。
  • 重试条件:网络错误、幂等请求或服务器响应 500 内部服务器错误时发生重试。
  • 记录重试:记录每次重试尝试,这对于调试和监控非常有用。

详细通知

按严重性(信息、警告、错误)区分通知,以帮助用户了解错误的重要性。

实施:

// config/customErrors.js

const ERROR_MESSAGES = {
  NETWORK_ERROR: "Network error. Please check your connection and try again.",
  BAD_REQUEST: "There was an issue with your request. Please check and try again.",
  UNAUTHORIZED: "You are not authorized to perform this action. Please log in.",
  FORBIDDEN: "Access denied. You don't have permission to view this resource.",
  NOT_FOUND: "The requested resource was not found.",
  GENERIC_ERROR: "Something went wrong. Please try again later.",

  // You can add more messages here if you want
};

export default ERROR_MESSAGES;
登录后复制
登录后复制

说明:

  • 通知类型:根据错误类别确定 Toast 通知的类型(信息、警告、错误)。
  • 用户反馈:为用户提供特定于上下文的反馈,帮助他们了解问题的严重性和性质。

取消组件卸载请求

使用 Axios 取消令牌,通过在组件卸载时取消正在进行的请求来防止内存泄漏。

实施:

// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

说明:

  • 取消令牌:当发出新请求或卸载组件时,利用 Axios 取消令牌取消正在进行的 API 请求。
  • 防止内存泄漏:确保未安装的组件上不会发生状态更新,防止潜在的内存泄漏。
  • 记录取消的请求:记录取消的请求以用于调试目的。

高级增强功能总结

实施这些先进技术可以将您的错误处理系统提升到一个新的水平:

  • 错误解析定制:向用户传递更具体的错误消息,帮助他们直接理解和解决问题。
  • 重试机制:通过允许请求在某些错误后自动或手动重试来提高可靠性。
  • 详细通知:区分错误类型,根据严重性显示通知,以更好地通知用户。
  • 取消组件卸载请求:防止内存泄漏和冗余请求,确保应用性能稳定高效。

这些增强功能是可选的,但非常有价值,因为它们为应用程序的错误处理方法增加了深度、灵活性和以用户为中心的改进。


连接点

当组件通过useApi发起API调用时,会触发以下流程:

组件使用服务模块:

每个服务模块(例如 userService、productService)都为特定 API 端点定义函数并使用配置的 axiosInstance。组件仅与这些服务功能交互。

useApi 通过Service模块触发axios:

组件将服务函数(例如,productService.getProducts)传递给useApi。当 request 被调用时,useApi 将函数转发给服务,然后服务通过 axiosInstance 发出 HTTP 请求。

Axios拦截器中的自定义错误解析:

axiosInstance 中的拦截器处理错误日志记录、解析预定义的自定义错误消息并集中错误处理。

来自 useApi 的结构化响应:

useApi 将结构化状态(数据、加载和错误)返回给组件,使其能够专注于呈现数据和处理交互。


视觉总结

以下概述描述了错误处理系统中的每个组件如何在应用程序内交互,从最初的 API 调用到用户反馈:

  1. 组件

    • 该组件使用 useApi 钩子发起 API 请求,从而消除了管理 API 调用、错误处理和加载状态的复杂性。
  2. 使用Api Hook

    • useApi 是一个自定义钩子,用于接收 API 请求的函数(来自服务模块)。它管理请求的加载状态,处理错误,并将结构化响应(数据、加载、错误)返回给组件。
  3. 服务模块

    • 服务模块为每个 API 端点(例如 getProducts、createProduct)定义特定功能,并使用集中式 axiosInstance 来处理所有请求,确保整个应用程序的一致性。
  4. Axios 实例

    • axiosInstance 管理 HTTP 请求和响应,应用任何自定义配置,例如基本 URL 和标头。
  5. API 响应

    • 来自 API 的响应通过 Axios 拦截器进行处理,该拦截器全局处理错误。这包括解析自定义错误消息和触发用户通知。
  6. 错误处理和用户通知

    • 拦截器通过react-toastify通知向用户显示错误消息,并且它们可以管理其他操作,例如在身份验证错误时将用户重定向到登录页面。

此流程支持集中式错误管理和一致的用户反馈,允许组件仅专注于呈现数据,而不需要处理重复的错误检查逻辑。


把它们放在一起:一个现实世界的例子

产品列表组件

此示例演示了从进行 API 调用到显示数据的整个流程,以及集中的错误处理和反馈。

// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

组件细分:

  • 导入语句

    • react-toastify:用于显示 toast 通知。
    • ProductService:包含与产品相关的API函数。
    • useApi:用于管理 API 调用的自定义钩子。
  • 钩子初始化:

    • 使用productService 中的getProducts 函数初始化useApi 挂钩。 false 参数表示 API 调用不应在钩子初始化后立即发生。
  • API 调用触发器:

    • 组件挂载时使用useEffect调用请求函数,获取第一页商品。
  • 错误处理:

    • 另一个 useEffect 监听错误状态的变化。如果发生错误,则会触发 toast 通知来通知用户。
  • 条件渲染:

    • 加载状态:API 调用正在进行时显示加载消息。
    • 错误状态:如果 API 调用失败,则显示错误消息。
    • 数据渲染:成功获取数据后,它会渲染产品网格及其图像、名称和价格。

此示例演示了集中式错误处理如何简化组件逻辑并确保一致的用户反馈。


最佳实践

遵守最佳实践可确保您的错误处理系统高效、可维护且用户友好。

关注点分离

  • 描述:通过使用服务模块将 API 逻辑与 UI 组件分开。这提高了代码组织和可维护性。
  • 示例:不要直接在组件内进行API调用,而是将它们委托给productService.js等服务模块。

一致的错误消息

  • 描述:统一处理所有错误,以简化调试并提供无缝的用户体验。
  • 示例:在 customErrors.js 中使用预定义的错误消息可确保用户收到一致的反馈,无论错误源自何处。

避免重复代码

  • 描述:使用集中式错误处理和自定义挂钩来消除组件之间重复的 try-catch 块。
  • 示例:useApi 挂钩管理错误状态和通知,允许组件仅专注于渲染数据。

有意义的错误消息

  • 描述:提供用户友好、可操作的错误消息,以增进理解并减少挫败感。
  • 示例:不要显示“发生错误”等通用消息,而是使用“验证错误:请输入有效的电子邮件地址”等特定消息。

处理边缘情况

  • 描述:预测和管理意外情况,例如网络故障或服务器错误,以防止您的应用程序崩溃。
  • 示例:Axios 拦截器通过显示“网络错误”toast 并防止应用程序崩溃来处理网络错误。

安全错误处理

  • 描述:避免在错误消息中暴露敏感信息。提供用户友好的消息,同时在服务器上安全地记录详细错误。
  • 示例:向用户显示一般错误消息,同时向开发人员发送详细的错误日志到 Sentry 等日志服务。

进一步阅读

通过以下资源增强您对本指南中涵盖的概念的理解:

  • Axios 拦截器文档:了解如何使用 Axios 拦截器全局处理请求和响应。
  • React Hooks 文档:了解 React Hooks 管理状态和副作用的基础知识。
  • Redux Toolkit 简介:开始使用 Redux Toolkit 在 React 应用程序中进行高效的状态管理。
  • React-Toastify 文档:了解如何实现 Toast 通知以获得更好的用户反馈。

故障排除

1. Toast 通知未出现

  • 原因您的应用程序中可能缺少来自 React-toastify 的组件。
  • 解决方案:确保包含在您的主应用程序组件中,通常位于pages/_app.js 中。
// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

2. API请求未被拦截器捕获

  • 原因:服务模块可能正在导入默认的 Axios 库,而不是集中式 axiosInstance。
  • 解决方案:确保所有服务模块都导入集中式axiosInstance。
const axiosInstance = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || '',
  headers: {
    'Content-Type': 'application/json',
  },
});
登录后复制
登录后复制
登录后复制

3. 重定向对特定错误不起作用

  • 原因:来自 next/router 的 Router 可能无法在 React 组件之外正确导入或使用。
  • 解决方案:确保Axios拦截器在Router可以执行重定向的上下文中使用。或者,在 useApi 挂钩或组件内处理重定向。

环境配置

管理不同的环境可确保您的应用程序在开发、测试和生产过程中与正确的 API 端点交互。

第1步:定义环境变量

在项目根目录中创建 .env.local 文件并定义 API 基本 URL:

axiosInstance.interceptors.response.use(
  (response) => response, // Pass through successful responses
  (error) => {
    if (!error.response) {
      toast.error(ERROR_MESSAGES.NETWORK_ERROR);
      return Promise.reject(error);
    }

    const { status, data } = error.response;
    let message = ERROR_MESSAGES[status] || ERROR_MESSAGES.GENERIC_ERROR;

    // Custom logic for specific error types
    if (data?.type === 'validation') {
      message = `Validation Error: ${data.message}`;
    } else if (data?.type === 'authentication') {
      message = `Authentication Error: ${data.message}`;
    }

    // Display error notification
    toast.error(message);

    // Handle unauthorized access by redirecting to login
    if (status === 401) {
      Router.push('/login');
    }

    return Promise.reject(error);
  }
);
登录后复制
登录后复制
登录后复制

第2步:在代码中访问环境变量

确保您的 Axios 实例使用环境变量:

// utils/axiosInstance.js
import axios from 'axios';
import ERROR_MESSAGES from '../config/customErrors';
import { toast } from 'react-toastify';
import Router from 'next/router';
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

最佳实践:

  • 保护敏感信息:切勿将 .env.local 文件或任何敏感环境变量提交到版本控制系统。使用 .gitignore 排除这些文件。
  • 一致的命名约定:对环境变量使用清晰一致的命名,通常以 NEXT_PUBLIC_ 为前缀,表示它们暴露给前端。
  • 为每个环境单独配置:为不同环境维护单独的.env文件(例如.env.development、.env.Production)以有效管理配置。

结论

通过构建集中式错误处理系统,您已经建立了一个干净、模块化且用户友好的结构,可以提高开发人员体验和用户满意度。无论您是刚刚开始还是希望增强应用程序的错误管理,此方法都提供了可以与您的应用程序一起成长的坚实基础。

鼓励自己尝试此处描述的功能,从基础知识开始,并在您熟悉后添加增强功能。集中式错误处理方法是一项宝贵的技能和实践,随着应用程序的扩展,它会带来回报。


号召性用语

准备好通过集中错误处理来增强您的 React/Next.js 应用程序了吗?

实施本指南中概述的策略,以体验更清晰的代码、一致的用户通知和改进的可维护性。

分享您的反馈

有问题、建议或经验要分享吗?通过发表评论或在 GitHub 和 Twitter 上联系来与社区互动。

敬请期待更多

我正在开发一个基于这个集中式错误处理系统的 npm 包。请继续关注更新,或推荐您认为有价值的功能!

编码快乐! ?✨

以上是使用 Axios 和自定义 Hook 构建强大的前端错误处理系统的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板