Vue中的自定义网络层实现:封装Fetch/Axios,实现统一的错误处理与认证逻辑

Vue 中的自定义网络层实现:封装 Fetch/Axios,实现统一的错误处理与认证逻辑

大家好,今天我们来聊聊在 Vue 项目中如何构建一个健壮且可维护的自定义网络层。网络层是连接前端应用和后端服务的桥梁,一个良好的网络层设计可以极大地提升项目的稳定性和开发效率。本次讲座将围绕封装 FetchAxios,实现统一的错误处理和认证逻辑展开。

1. 为什么需要自定义网络层?

在小型项目中,直接在组件中使用 FetchAxios 似乎也能满足需求。但随着项目规模的扩大,这种方式会带来以下问题:

  • 代码重复: 每个组件都需要编写相似的网络请求代码,包括请求头设置、错误处理等。
  • 维护困难: 如果后端接口发生变化,需要在各个组件中修改代码,维护成本高昂。
  • 缺乏统一管理: 难以对请求进行统一的配置和管理,例如设置超时时间、请求拦截器等。
  • 安全问题: 认证逻辑分散在各个组件中,容易出现安全漏洞。

因此,我们需要一个自定义的网络层,将网络请求相关的逻辑进行封装,提供统一的接口和处理机制,从而提高代码的可重用性、可维护性和安全性。

2. 技术选型:Fetch vs. Axios

在选择网络请求库时,FetchAxios 是两个常见的选择。

特性 Fetch Axios
内置/第三方 内置 第三方
浏览器兼容性 较好,但需要 polyfill 支持低版本浏览器 较好
请求拦截器 不支持 支持
响应拦截器 不支持 支持
自动转换 JSON 需要手动转换 自动转换
错误处理 需要手动处理 HTTP 错误状态码 可以自动处理 HTTP 错误状态码
取消请求 需要使用 AbortController 支持
文件上传 相对复杂 相对简单

选择建议:

  • 如果项目对浏览器兼容性要求很高,且不需要复杂的拦截器功能,可以使用 Fetch,并配合 polyfill
  • 如果项目需要请求和响应拦截器、自动转换 JSON、更方便的错误处理,以及文件上传等功能,建议使用 Axios

本次讲座将以 Axios 为例,演示如何构建自定义网络层。

3. 网络层设计与实现

我们的网络层主要包含以下几个模块:

  • Axios 实例创建与配置: 创建一个 Axios 实例,并设置全局配置,例如 baseURLtimeoutheaders 等。
  • 请求拦截器: 在请求发送前进行拦截,例如添加认证信息、记录请求日志等。
  • 响应拦截器: 在接收到响应后进行拦截,例如处理错误状态码、转换数据格式等。
  • 统一的错误处理: 捕获网络请求中发生的错误,并进行统一的处理,例如显示错误提示、跳转到错误页面等。
  • API 方法封装: 将不同的 API 请求封装成独立的函数,方便调用和管理。

3.1 创建 Axios 实例并配置

首先,我们需要安装 Axios

npm install axios

然后,创建一个名为 request.js 的文件,用于创建和配置 Axios 实例:

import axios from 'axios';

// 创建 Axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, //  API 基础路径, 可以在 .env 文件中配置
  timeout: 5000, // 请求超时时间
});

export default service;

.env 文件中配置 VUE_APP_BASE_API

VUE_APP_BASE_API = '/api' // 后端 API 地址

3.2 请求拦截器

请求拦截器可以在请求发送前进行拦截,例如添加认证信息、记录请求日志等。

import axios from 'axios';
import store from '@/store'; // 引入 Vuex store
import { getToken } from '@/utils/auth'; // 引入 token 管理工具

// 创建 Axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000,
});

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 在请求发送前做一些处理
    if (store.getters.token) {
      // 如果存在 token,则添加 `Authorization` 请求头
      config.headers['Authorization'] = 'Bearer ' + getToken();
    }
    return config;
  },
  error => {
    // 处理请求错误
    console.log(error); // for debug
    return Promise.reject(error);
  }
);

export default service;

3.3 响应拦截器

响应拦截器可以在接收到响应后进行拦截,例如处理错误状态码、转换数据格式等。

import axios from 'axios';
import store from '@/store';
import { getToken } from '@/utils/auth';
import { Message } from 'element-ui'; // 引入 Element UI 的 Message 组件

// 创建 Axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000,
});

// 请求拦截器
service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      config.headers['Authorization'] = 'Bearer ' + getToken();
    }
    return config;
  },
  error => {
    console.log(error);
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    // 2xx 范围内的状态码都会触发该函数
    const res = response.data;

    // 如果自定义状态码不是 20000,则认为有错误
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      });

      // 50008: 非法 Token; 50012: 其他客户端登录; 50014: Token 过期;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // 重新登录
        store.dispatch('user/resetToken').then(() => {
          location.reload();
        });
      }
      return Promise.reject(new Error(res.message || 'Error'));
    } else {
      return res;
    }
  },
  error => {
    // 超出 2xx 范围的状态码都会触发该函数
    console.log('err' + error); // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    });
    return Promise.reject(error);
  }
);

export default service;

3.4 统一的错误处理

在响应拦截器中,我们对不同类型的错误进行了处理:

  • 自定义错误状态码: 根据后端返回的 code 值,判断是否发生了错误。如果 code 不是 20000,则显示错误提示信息。
  • Token 过期或失效: 如果 code500085001250014,则表示 Token 过期或失效,需要重新登录。
  • 其他错误: 显示 Axios 返回的错误信息。

3.5 API 方法封装

为了方便调用和管理 API 请求,我们可以将不同的 API 请求封装成独立的函数。创建一个 api 文件夹,并在其中创建不同的 API 文件,例如 user.js

import request from '@/utils/request';

export function login(data) {
  return request({
    url: '/user/login',
    method: 'post',
    data
  });
}

export function getInfo(token) {
  return request({
    url: '/user/info',
    method: 'get',
    params: { token }
  });
}

export function logout() {
  return request({
    url: '/user/logout',
    method: 'post'
  });
}

4. 在组件中使用网络层

在组件中,我们可以直接调用 API 文件中封装的函数来发起网络请求:

<template>
  <div>
    <button @click="handleLogin">Login</button>
  </div>
</template>

<script>
import { login } from '@/api/user';

export default {
  methods: {
    async handleLogin() {
      try {
        const response = await login({
          username: 'admin',
          password: 'password'
        });
        console.log(response); // 处理登录成功后的逻辑
      } catch (error) {
        console.error(error); // 处理登录失败后的逻辑
      }
    }
  }
};
</script>

5. 其他考虑因素

  • 取消请求: Axios 提供了 CancelToken 用于取消请求。可以在组件销毁时取消未完成的请求,避免内存泄漏。
  • 文件上传: 可以使用 AxiosFormData 对象进行文件上传。
  • 数据缓存: 可以使用 Axiosadapter 选项,结合 localStoragesessionStorage 实现数据缓存。
  • TypeScript 支持: 如果项目使用 TypeScript,可以为 Axios 实例、请求和响应数据定义类型,提高代码的可维护性。

代码示例:取消请求

import axios from 'axios';

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

const request = axios.get('/api/data', {
  cancelToken: source.token
}).then(response => {
  // 处理响应数据
}).catch(error => {
  if (axios.isCancel(error)) {
    console.log('Request canceled', error.message);
  } else {
    // 处理其他错误
  }
});

// 取消请求
source.cancel('Operation canceled by the user.');

代码示例:文件上传

import axios from 'axios';

const formData = new FormData();
formData.append('file', file);
formData.append('name', 'myfile');

axios.post('/api/upload', formData, {
  headers: {
    'Content-Type': 'multipart/form-data'
  }
}).then(response => {
  // 处理响应数据
}).catch(error => {
  // 处理错误
});

6. 总结

自定义网络层是构建健壮 Vue 应用的关键。通过封装 Axios,我们可以实现统一的错误处理、认证逻辑和 API 管理,提高代码的可重用性、可维护性和安全性。在实际项目中,可以根据具体需求对网络层进行定制和扩展。

构建网络层,提升项目质量

构建一个好的网络层是提升项目质量的重要一步,它可以减少重复代码,统一处理错误,方便管理API,让我们的 Vue 项目更加健壮和易于维护。

灵活运用拦截器,处理认证与日志

请求和响应拦截器是网络层的核心功能,我们可以利用它们添加认证信息、记录请求日志、转换数据格式等,从而更好地控制网络请求的行为。

持续优化与扩展,适应变化的需求

随着项目的不断发展,我们的网络层也需要不断优化和扩展,例如支持取消请求、文件上传、数据缓存等,以适应不断变化的需求。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注